/* Copyright (c) 2010 Gluster, Inc. This file is part of GlusterFS. GlusterFS is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GlusterFS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #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; GF_VALIDATE_OR_GOTO (ACTRL, fr, out); st = fr->local; fr->local = NULL; out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %p", st); return st; } void ac_set_accesstype_str (int access, char *str, size_t len) { char *read = "read "; char *write = "write "; char *exec = "exec "; char *dontcare = "don'tcare "; GF_ASSERT (len > (strlen (read) + strlen (write) + strlen (exec) + strlen (dontcare))); GF_ASSERT (str); if (access & ACCTEST_READ) { snprintf (str, len, "%s", read); len -= strlen (read); } if (access & ACCTEST_WRITE) { snprintf (str, len, "%s", write); len -= strlen (write); } if (access & ACCTEST_EXEC) { snprintf (str, len, "%s", exec); len -= strlen (exec); } if (access & ACCTEST_DONTCARE) { snprintf (str, len, "%s", dontcare); len -= strlen (dontcare); } } int ac_test_owner_access (struct iatt *ia, uid_t uid, int accesstest) { int ret = -1; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); /* First test permissions using the uid. */ if (ia->ia_uid != uid) { ret = -1; gf_log (ACTRL, GF_LOG_TRACE, "UID mismatch (orig: %d, user: %d)", ia->ia_uid, uid); 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; gf_log (ACTRL, GF_LOG_TRACE, "Access test marked as don't care"); 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: if (0 == ret) gf_log (ACTRL, GF_LOG_TRACE, "Owner access allowed"); else gf_log (ACTRL, GF_LOG_TRACE, "Owner access not allowed"); 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; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); /* 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)) { gf_log (ACTRL, GF_LOG_TRACE, "GID mismatch (orig: %d, user: %d)", ia->ia_gid, gid); 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) { gf_log (ACTRL, GF_LOG_TRACE, "None of the gids match with gid " "on the stat"); 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)) { gf_log (ACTRL, GF_LOG_TRACE, "Access test marked as don't care"); 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: if (0 == ret) gf_log (ACTRL, GF_LOG_TRACE, "Group access allowed"); else gf_log (ACTRL, GF_LOG_TRACE, "Group access not allowed"); return ret; } int ac_test_other_access (struct iatt *ia, int accesstest) { int ret = -1; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); ret = 0; 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; out: if (0 == ret) gf_log (ACTRL, GF_LOG_TRACE, "Other access allowed"); else gf_log (ACTRL, GF_LOG_TRACE, "Other access not allowed"); 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; char accesstest_str[32] = {0}; GF_VALIDATE_OR_GOTO (ACTRL, ia, out); GF_VALIDATE_OR_GOTO (ACTRL, operrno, out); ac_set_accesstype_str (accesstest, accesstest_str, sizeof (accesstest_str)); gf_log (ACTRL, GF_LOG_TRACE, "Testing for accesstypes %s", accesstest_str); 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) { 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) { goto out; } if (ac_test_other (testwho)) { gf_log (ACTRL, GF_LOG_TRACE, "Testing other access"); ret = ac_test_other_access (ia, accesstest); } 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; GF_VALIDATE_OR_GOTO (ACTRL, loc, out); if (inode) { loc->inode = inode_ref (inode); loc->ino = inode->ino; } if (parent) loc->parent = inode_ref (parent); loc->path = gf_strdup (path); if (!loc->path) { goto loc_wipe; } loc->name = strrchr (loc->path, '/'); if (loc->name) { loc->name++; } else { gf_log (ACTRL, GF_LOG_ERROR, "path: %s, doesn't have '/'", loc->path); goto loc_wipe; } ret = 0; loc_wipe: if (ret < 0) { if (inode) gf_log (ACTRL, GF_LOG_ERROR, "location fill failed for " "inode: %s", uuid_utoa (inode->gfid)); loc_wipe (loc); } out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %d", ret); return ret; } int ac_inode_loc_fill (inode_t *inode, loc_t *loc) { char *resolvedpath = NULL; inode_t *parent = NULL; int ret = -EFAULT; GF_VALIDATE_OR_GOTO (ACTRL, inode, out); GF_VALIDATE_OR_GOTO (ACTRL, loc, out); if ((inode) && (inode->ino == 1)) { goto ignore_parent; } parent = inode_parent (inode, 0, NULL); GF_VALIDATE_OR_GOTO (ACTRL, parent, err); ignore_parent: ret = inode_path (inode, NULL, &resolvedpath); if (ret < 0) { gf_log (ACTRL, GF_LOG_ERROR, "Unable to get path for inode: %s", uuid_utoa (inode->gfid)); goto err; } ret = ac_loc_fill (loc, inode, parent, resolvedpath); if (ret < 0) goto err; err: if (parent) inode_unref (parent); if (resolvedpath) GF_FREE (resolvedpath); out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %d", ret); return ret; } int ac_parent_loc_fill (loc_t *parentloc, loc_t *childloc) { int ret = -1; GF_VALIDATE_OR_GOTO (ACTRL, parentloc, out); GF_VALIDATE_OR_GOTO (ACTRL, childloc, out); ret = ac_inode_loc_fill (childloc->parent, parentloc); out: gf_log (ACTRL, GF_LOG_TRACE, "Returning %d", ret); return ret; } 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) { gf_log (this->name, GF_LOG_ERROR, "truncate failed with " "error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ac_truncate_resume (frame, this, loc, offset); return 0; } stub = fop_truncate_stub (frame, ac_truncate_resume, loc, offset); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "truncate failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "access failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ac_access_resume (frame, this, loc, mask); return 0; } stub = fop_access_stub (frame, ac_access_resume, loc, mask); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "access failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "readlink failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ac_readlink_resume (frame, this, loc, size); return 0; } stub = fop_readlink_stub (frame, ac_readlink_resume, loc, size); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "readlink failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (readlink, frame, -1, -ret, NULL, NULL); } return 0; } int ac_mknod_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dev_t rdev, dict_t *params) { STACK_WIND (frame, default_mknod_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod, loc, mode, rdev, params); 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) { gf_log (this->name, GF_LOG_ERROR, "mknod failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (mknod, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dev_t rdev, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_mknod_resume (frame, this, loc, mode, rdev, params); return 0; } stub = fop_mknod_stub (frame, ac_mknod_resume, loc, mode, rdev, params); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "mknod failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (mknod, frame, -1, -ret, NULL, NULL, NULL, NULL); } return 0; } int ac_mkdir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dict_t *params) { STACK_WIND (frame, default_mkdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, loc, mode, params); 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) { gf_log (this->name, GF_LOG_ERROR, "mkdir failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (mkdir, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_mkdir_resume (frame, this, loc, mode, params); return 0; } stub = fop_mkdir_stub (frame, ac_mkdir_resume, loc, mode, params); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "mkdir failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "unlink failed with error: %s", strerror (op_errno)); 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, }; if (__is_fuse_call (frame)) { ac_unlink_resume (frame, this, loc); return 0; } stub = fop_unlink_stub (frame, ac_unlink_resume, loc); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "unlink failed with error: %s", strerror (-ret)); STACK_UNWIND_STRICT (unlink, frame, -1, -ret, NULL, NULL); } return 0; } int ac_rmdir_resume (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags) { STACK_WIND (frame, default_rmdir_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->rmdir, loc, flags); 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) { gf_log (this->name, GF_LOG_ERROR, "rmdir failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (rmdir, frame, -1, op_errno, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_rmdir_resume (frame, this, loc, flags); return 0; } stub = fop_rmdir_stub (frame, ac_rmdir_resume, loc, flags); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "rmdir failed with error: %s", strerror (-ret)); 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, dict_t *params) { STACK_WIND (frame, default_symlink_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->symlink, linkname, loc, params); 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) { gf_log (this->name, GF_LOG_ERROR, "symlink failed with error: %s", strerror (op_errno)); STACK_UNWIND_STRICT (symlink, frame, -1, op_errno, NULL, NULL, NULL, NULL); if (stub) call_stub_destroy (stub); } return 0; } int ac_symlink (call_frame_t *frame, xlator_t *this, const char *linkname, loc_t *loc, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_symlink_resume (frame, this, linkname, loc, params); return 0; } stub = fop_symlink_stub (frame, ac_symlink_resume, linkname, loc, params); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "symlink failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "rename failed with error: %s", strerror (op_errno)); 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 < 0) { 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); gf_log (this->name, GF_LOG_ERROR, "rename failed with error: %s", strerror (op_errno)); 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, }; if (__is_fuse_call (frame)) { ac_rename_resume (frame, this, oldloc, newloc); return 0; } stub = fop_rename_stub (frame, ac_rename_resume, oldloc, newloc); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "rename failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "link failed with error: %s", strerror (op_errno)); 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, }; if (__is_fuse_call (frame)) { ac_link_resume (frame, this, oldloc, newloc); return 0; } stub = fop_link_stub (frame, ac_link_resume, oldloc, newloc); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "link failed with error: %s", strerror (-ret)); 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, dict_t *params) { STACK_WIND (frame, default_create_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, loc, flags, mode, fd, params); 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) { gf_log (this->name, GF_LOG_ERROR, "create failed with error: %s", strerror (op_errno)); 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, dict_t *params) { call_stub_t *stub = NULL; int ret = -EFAULT; loc_t parentloc = {0, }; if (__is_fuse_call (frame)) { ac_create_resume (frame, this, loc, flags, mode, fd, params); return 0; } stub = fop_create_stub (frame, ac_create_resume, loc, flags, mode, fd, params); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "create failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "open failed with error: %s", strerror (op_errno)); 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) { gf_log (this->name, GF_LOG_ERROR, "open failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ret = ac_open_resume (frame, this, loc, flags, fd, wbflags); return 0; } stub = fop_open_stub (frame, ac_open_resume, loc, flags, fd, wbflags); if (!stub) { 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); gf_log (this->name, GF_LOG_ERROR, "open failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "readv failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ret = ac_readv_resume (frame, this, fd, size, offset); return 0; } stub = fop_readv_stub (frame, ac_readv_resume, fd, size, offset); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "readv failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "writev failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ret = ac_writev_resume (frame, this, fd, vector, count, offset, iobref); return 0; } stub = fop_writev_stub (frame, ac_writev_resume, fd, vector, count, offset, iobref); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "writev failed with error: %s", strerror (-ret)); 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) { gf_log (this->name, GF_LOG_ERROR, "opendir failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ret = ac_opendir_resume (frame, this, loc, fd); return 0; } stub = fop_opendir_stub (frame, ac_opendir_resume, loc, fd); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "opendir failed with error: %s", strerror (-ret)); 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_WRITE, ACCTEST_ANY, &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) { gf_log (this->name, GF_LOG_ERROR, "setattr failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ret = ac_setattr_resume (frame, this, loc, buf, valid); return 0; } stub = fop_setattr_stub (frame, ac_setattr_resume, loc, buf, valid); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "setattr failed with error: %s", strerror (-ret)); 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_WRITE, ACCTEST_ANY, &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) { gf_log (this->name, GF_LOG_ERROR, "fsetattr failed with error: %s", strerror (op_errno)); 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; if (__is_fuse_call (frame)) { ret = ac_fsetattr_resume (frame, this, fd, buf, valid); return 0; } stub = fop_fsetattr_stub (frame, ac_fsetattr_resume, fd, buf, valid); if (!stub) { 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) { gf_log (this->name, GF_LOG_ERROR, "fsetattr failed with error: %s", strerror (-ret)); 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, /* * Allow Writes and Reads to proceed without permission checks because: * a. We expect that when the fds are opened, thats when the perm checks happen * depending on the read/write mode used. * * b. In case of nfs clients, we expect the nfs clients to perform the checks * based on getattr/access nfs requests. * * Keep these functions around in case we ever run into a nfs client that * depends on nfs server to perform these checks. Till then, just remove the * references from here instead. .readv = ac_readv, .writev = ac_writev, */ .opendir = ac_opendir, .setattr = ac_setattr, .fsetattr = ac_fsetattr, }; int init (xlator_t *this) { int ret = -1; if (!this->children || this->children->next) { gf_log (this->name, GF_LOG_ERROR, "FATAL: access-control not configured with " "exactly one child"); goto out; } if (!this->parents) { gf_log (this->name, GF_LOG_WARNING, "dangling volume. check volfile "); } ret = 0; out: return ret; } void fini (xlator_t *this) { return; } struct xlator_cbks cbks = { };