diff options
Diffstat (limited to 'xlators/system/posix-acl/src/posix-acl.c')
| -rw-r--r-- | xlators/system/posix-acl/src/posix-acl.c | 2246 |
1 files changed, 2246 insertions, 0 deletions
diff --git a/xlators/system/posix-acl/src/posix-acl.c b/xlators/system/posix-acl/src/posix-acl.c new file mode 100644 index 00000000000..fc227364b31 --- /dev/null +++ b/xlators/system/posix-acl/src/posix-acl.c @@ -0,0 +1,2246 @@ +/* + Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include <errno.h> + +#include <glusterfs/defaults.h> + +#include "posix-acl.h" +#include "posix-acl-xattr.h" +#include "posix-acl-mem-types.h" +#include "posix-acl-messages.h" + +#define UINT64(ptr) ((uint64_t)((long)(ptr))) +#define PTR(num) ((void *)((long)(num))) + +int32_t +mem_acct_init(xlator_t *this) +{ + int ret = -1; + + if (!this) + return ret; + + ret = xlator_mem_acct_init(this, gf_posix_acl_mt_end + 1); + + if (ret != 0) { + gf_log(this->name, GF_LOG_ERROR, + "Memory accounting init" + "failed"); + return ret; + } + + return ret; +} + +static uid_t +r00t() +{ + struct posix_acl_conf *conf = NULL; + + conf = THIS->private; + + return conf->super_uid; +} + +static int +allowed_xattr(const char *key) +{ + if (!key) + return 0; + + if (strcmp(POSIX_ACL_ACCESS_XATTR, key) == 0) + return 1; + if (strcmp(POSIX_ACL_DEFAULT_XATTR, key) == 0) + return 1; + if (strcmp(GF_POSIX_ACL_ACCESS, key) == 0) + return 1; + if (strcmp(GF_POSIX_ACL_DEFAULT, key) == 0) + return 1; + return 0; +} + +int +frame_is_user(call_frame_t *frame, uid_t uid) +{ + return (frame->root->uid == uid); +} + +int +frame_is_super_user(call_frame_t *frame) +{ + int ret; + + ret = frame_is_user(frame, r00t()); + if (!ret) + ret = frame_is_user(frame, 0); + + return ret; +} + +int +frame_in_group(call_frame_t *frame, gid_t gid) +{ + int i = 0; + + if (frame->root->gid == gid) + return 1; + + for (i = 0; i < frame->root->ngrps; i++) + if (frame->root->groups[i] == gid) + return 1; + return 0; +} + +mode_t +posix_acl_access_set_mode(struct posix_acl *acl, struct posix_acl_ctx *ctx) +{ + struct posix_ace *ace = NULL; + struct posix_ace *group_ce = NULL; + struct posix_ace *mask_ce = NULL; + int count = 0; + int i = 0; + mode_t mode = 0; + int mask = 0; + + count = acl->count; + + ace = acl->entries; + for (i = 0; i < count; i++) { + switch (ace->tag) { + case POSIX_ACL_USER_OBJ: + mask |= S_IRWXU; + mode |= (ace->perm << 6); + break; + case POSIX_ACL_GROUP_OBJ: + group_ce = ace; + break; + case POSIX_ACL_MASK: + mask_ce = ace; + break; + case POSIX_ACL_OTHER: + mask |= S_IRWXO; + mode |= (ace->perm); + break; + } + ace++; + } + + if (mask_ce) { + mask |= S_IRWXG; + mode |= (mask_ce->perm << 3); + } else { + if (!group_ce) + goto out; + mask |= S_IRWXG; + mode |= (group_ce->perm << 3); + } + +out: + ctx->perm = (ctx->perm & ~mask) | mode; + + return mode; +} + +static int +sticky_permits(call_frame_t *frame, inode_t *parent, inode_t *inode) +{ + struct posix_acl_ctx *par = NULL; + struct posix_acl_ctx *ctx = NULL; + + if ((0 > frame->root->pid) || frame_is_super_user(frame)) + return 1; + + par = posix_acl_ctx_get(parent, frame->this); + if (par == NULL) + return 0; + + ctx = posix_acl_ctx_get(inode, frame->this); + if (ctx == NULL) + return 0; + + if (!(par->perm & S_ISVTX)) + return 1; + + if (frame_is_user(frame, par->uid)) + return 1; + + if (frame_is_user(frame, ctx->uid)) + return 1; + + return 0; +} + +static gf_boolean_t +_does_acl_exist(struct posix_acl *acl) +{ + if (acl && (acl->count > POSIX_ACL_MINIMAL_ACE_COUNT)) + return _gf_true; + return _gf_false; +} + +static void +posix_acl_get_acl_string(call_frame_t *frame, struct posix_acl *acl, + char **acl_str) +{ + int i = 0; + size_t size_acl = 0; + size_t offset = 0; + struct posix_ace *ace = NULL; + char tmp_str[1024] = {0}; +#define NON_GRP_FMT "(tag:%" PRIu16 ",perm:%" PRIu16 ",id:%" PRIu32 ")" +#define GRP_FMT "(tag:%" PRIu16 ",perm:%" PRIu16 ",id:%" PRIu32 ",in-groups:%d)" + + if (!_does_acl_exist(acl)) + goto out; + + ace = acl->entries; + for (i = 0; i < acl->count; i++) { + if (ace->tag != POSIX_ACL_GROUP) { + size_acl += snprintf(tmp_str, sizeof tmp_str, NON_GRP_FMT, ace->tag, + ace->perm, ace->id); + } else { + size_acl += snprintf(tmp_str, sizeof tmp_str, GRP_FMT, ace->tag, + ace->perm, ace->id, + frame_in_group(frame, ace->id)); + } + + ace++; + } + + *acl_str = GF_CALLOC(1, size_acl + 1, gf_posix_acl_mt_char); + if (!*acl_str) + goto out; + + ace = acl->entries; + for (i = 0; i < acl->count; i++) { + if (ace->tag != POSIX_ACL_GROUP) { + offset += snprintf(*acl_str + offset, size_acl - offset, + NON_GRP_FMT, ace->tag, ace->perm, ace->id); + } else { + offset += snprintf(*acl_str + offset, size_acl - offset, GRP_FMT, + ace->tag, ace->perm, ace->id, + frame_in_group(frame, ace->id)); + } + + ace++; + } +out: + return; +} + +static void +posix_acl_log_permit_denied(call_frame_t *frame, inode_t *inode, int want, + struct posix_acl_ctx *ctx, struct posix_acl *acl) +{ + char *acl_str = NULL; + client_t *client = NULL; + + if (!frame || !inode || !ctx || !frame->root) + goto out; + + client = frame->root->client; + posix_acl_get_acl_string(frame, acl, &acl_str); + + gf_msg(frame->this->name, GF_LOG_INFO, EACCES, POSIX_ACL_MSG_EACCES, + "client: %s, gfid: %s, req(uid:%d,gid:%d,perm:%d," + "ngrps:%" PRIu16 + "), ctx(uid:%d,gid:%d,in-groups:%d,perm:%d%d%d," + "updated-fop:%s, acl:%s)", + client ? client->client_uid : "-", uuid_utoa(inode->gfid), + frame->root->uid, frame->root->gid, want, frame->root->ngrps, + ctx->uid, ctx->gid, frame_in_group(frame, ctx->gid), + (ctx->perm & S_IRWXU) >> 6, (ctx->perm & S_IRWXG) >> 3, + ctx->perm & S_IRWXO, gf_fop_string(ctx->fop), + acl_str ? acl_str : "-"); +out: + GF_FREE(acl_str); + return; +} + +static int +acl_permits(call_frame_t *frame, inode_t *inode, int want) +{ + int verdict = 0; + struct posix_acl *acl = NULL; + struct posix_ace *ace = NULL; + struct posix_acl_ctx *ctx = NULL; + struct posix_acl_conf *conf = NULL; + int i = 0; + int perm = 0; + int found = 0; + int acl_present = 0; + + conf = frame->this->private; + + if ((0 > frame->root->pid) || frame_is_super_user(frame)) + goto green; + + ctx = posix_acl_ctx_get(inode, frame->this); + if (!ctx) + goto red; + + posix_acl_get(inode, frame->this, &acl, NULL); + if (!acl) { + acl = posix_acl_ref(frame->this, conf->minimal_acl); + } + + ace = acl->entries; + + if (_does_acl_exist(acl)) + acl_present = 1; + + for (i = 0; i < acl->count; i++) { + switch (ace->tag) { + case POSIX_ACL_USER_OBJ: + perm = ((ctx->perm & S_IRWXU) >> 6); + if (frame_is_user(frame, ctx->uid)) + goto perm_check; + break; + case POSIX_ACL_USER: + perm = ace->perm; + if (frame_is_user(frame, ace->id)) + goto mask_check; + break; + case POSIX_ACL_GROUP_OBJ: + if (acl_present) + perm = ace->perm; + else + perm = ((ctx->perm & S_IRWXG) >> 3); + if (frame_in_group(frame, ctx->gid)) { + found = 1; + if ((perm & want) == want) + goto mask_check; + } + break; + case POSIX_ACL_GROUP: + perm = ace->perm; + if (frame_in_group(frame, ace->id)) { + found = 1; + if ((perm & want) == want) + goto mask_check; + } + break; + case POSIX_ACL_MASK: + break; + case POSIX_ACL_OTHER: + perm = (ctx->perm & S_IRWXO); + if (!found) + goto perm_check; + /* fall through */ + default: + goto red; + } + + ace++; + } + +mask_check: + ace = acl->entries; + + for (i = 0; i < acl->count; i++, ace++) { + if (ace->tag != POSIX_ACL_MASK) + continue; + if ((ace->perm & perm & want) == want) { + goto green; + } + goto red; + } + +perm_check: + if ((perm & want) == want) { + goto green; + } else { + goto red; + } + +green: + verdict = 1; + goto out; +red: + verdict = 0; + posix_acl_log_permit_denied(frame, inode, want, ctx, acl); +out: + if (acl) + posix_acl_unref(frame->this, acl); + + return verdict; +} + +struct posix_acl_ctx * +__posix_acl_ctx_get(inode_t *inode, xlator_t *this, gf_boolean_t create) +{ + struct posix_acl_ctx *ctx = NULL; + uint64_t int_ctx = 0; + int ret = 0; + + ret = __inode_ctx_get(inode, this, &int_ctx); + if ((ret == 0) && (int_ctx)) + return PTR(int_ctx); + + if (create == _gf_false) + return NULL; + + ctx = GF_CALLOC(1, sizeof(*ctx), gf_posix_acl_mt_ctx_t); + if (!ctx) + return NULL; + + ret = __inode_ctx_put(inode, this, UINT64(ctx)); + if (ret) { + GF_FREE(ctx); + ctx = NULL; + } + + return ctx; +} + +struct posix_acl_ctx * +posix_acl_ctx_new(inode_t *inode, xlator_t *this) +{ + struct posix_acl_ctx *ctx = NULL; + + if (inode == NULL) { + gf_log_callingfn(this->name, GF_LOG_WARNING, "inode is NULL"); + return NULL; + } + + LOCK(&inode->lock); + { + ctx = __posix_acl_ctx_get(inode, this, _gf_true); + } + UNLOCK(&inode->lock); + + if (ctx == NULL) + gf_log_callingfn(this->name, GF_LOG_ERROR, + "creating inode ctx" + "failed for %s", + uuid_utoa(inode->gfid)); + return ctx; +} + +struct posix_acl_ctx * +posix_acl_ctx_get(inode_t *inode, xlator_t *this) +{ + struct posix_acl_ctx *ctx = NULL; + + if (inode == NULL) { + gf_log_callingfn(this->name, GF_LOG_WARNING, "inode is NULL"); + return NULL; + } + + LOCK(&inode->lock); + { + ctx = __posix_acl_ctx_get(inode, this, _gf_false); + } + UNLOCK(&inode->lock); + + if (ctx == NULL) + gf_log_callingfn(this->name, GF_LOG_ERROR, + "inode ctx is NULL " + "for %s", + uuid_utoa(inode->gfid)); + return ctx; +} + +int +__posix_acl_set_specific(inode_t *inode, xlator_t *this, gf_boolean_t is_access, + struct posix_acl *acl) +{ + int ret = 0; + struct posix_acl_ctx *ctx = NULL; + + ctx = posix_acl_ctx_get(inode, this); + if (!ctx) { + ret = -1; + goto out; + } + + if (is_access) + ctx->acl_access = acl; + else + ctx->acl_default = acl; +out: + return ret; +} + +int +__posix_acl_set(inode_t *inode, xlator_t *this, struct posix_acl *acl_access, + struct posix_acl *acl_default) +{ + int ret = 0; + struct posix_acl_ctx *ctx = NULL; + + ctx = posix_acl_ctx_get(inode, this); + if (!ctx) + goto out; + + ctx->acl_access = acl_access; + ctx->acl_default = acl_default; + +out: + return ret; +} + +int +__posix_acl_get(inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p, + struct posix_acl **acl_default_p) +{ + int ret = 0; + struct posix_acl_ctx *ctx = NULL; + + ctx = posix_acl_ctx_get(inode, this); + if (!ctx) + goto out; + + if (acl_access_p) + *acl_access_p = ctx->acl_access; + if (acl_default_p) + *acl_default_p = ctx->acl_default; + +out: + return ret; +} + +struct posix_acl * +posix_acl_new(xlator_t *this, int entrycnt) +{ + struct posix_acl *acl = NULL; + struct posix_ace *ace = NULL; + + acl = GF_CALLOC(1, sizeof(*acl) + (entrycnt * sizeof(*ace)), + gf_posix_acl_mt_posix_ace_t); + if (!acl) + return NULL; + + acl->count = entrycnt; + + posix_acl_ref(this, acl); + + return acl; +} + +void +posix_acl_destroy(xlator_t *this, struct posix_acl *acl) +{ + GF_FREE(acl); + + return; +} + +struct posix_acl * +posix_acl_ref(xlator_t *this, struct posix_acl *acl) +{ + struct posix_acl_conf *conf = NULL; + + conf = this->private; + + LOCK(&conf->acl_lock); + { + acl->refcnt++; + } + UNLOCK(&conf->acl_lock); + + return acl; +} + +struct posix_acl * +posix_acl_dup(xlator_t *this, struct posix_acl *acl) +{ + struct posix_acl *dup = NULL; + + dup = posix_acl_new(this, acl->count); + if (!dup) + return NULL; + + memcpy(dup->entries, acl->entries, sizeof(struct posix_ace) * acl->count); + + return dup; +} + +void +posix_acl_unref(xlator_t *this, struct posix_acl *acl) +{ + struct posix_acl_conf *conf = NULL; + int refcnt = 0; + + conf = this->private; + if (!conf) + goto out; + + LOCK(&conf->acl_lock); + { + refcnt = --acl->refcnt; + } + UNLOCK(&conf->acl_lock); +out: + if (!refcnt) + posix_acl_destroy(this, acl); +} + +int +posix_acl_set_specific(inode_t *inode, xlator_t *this, struct posix_acl *acl, + gf_boolean_t is_access) +{ + int ret = 0; + int oldret = 0; + struct posix_acl *old_acl = NULL; + struct posix_acl_conf *conf = NULL; + + conf = this->private; + + LOCK(&conf->acl_lock); + { + if (is_access) + oldret = __posix_acl_get(inode, this, &old_acl, NULL); + else + oldret = __posix_acl_get(inode, this, NULL, &old_acl); + if (acl) + acl->refcnt++; + ret = __posix_acl_set_specific(inode, this, is_access, acl); + } + UNLOCK(&conf->acl_lock); + + if (!oldret && old_acl) { + posix_acl_unref(this, old_acl); + } + + return ret; +} + +int +posix_acl_set(inode_t *inode, xlator_t *this, struct posix_acl *acl_access, + struct posix_acl *acl_default) +{ + int ret = 0; + int oldret = 0; + struct posix_acl *old_access = NULL; + struct posix_acl *old_default = NULL; + struct posix_acl_conf *conf = NULL; + + conf = this->private; + + LOCK(&conf->acl_lock); + { + oldret = __posix_acl_get(inode, this, &old_access, &old_default); + if (acl_access) + acl_access->refcnt++; + if (acl_default) + acl_default->refcnt++; + + ret = __posix_acl_set(inode, this, acl_access, acl_default); + } + UNLOCK(&conf->acl_lock); + + if (oldret == 0) { + if (old_access) + posix_acl_unref(this, old_access); + if (old_default) + posix_acl_unref(this, old_default); + } + + return ret; +} + +int +posix_acl_get(inode_t *inode, xlator_t *this, struct posix_acl **acl_access_p, + struct posix_acl **acl_default_p) +{ + struct posix_acl_conf *conf = NULL; + struct posix_acl *acl_access = NULL; + struct posix_acl *acl_default = NULL; + int ret = 0; + + conf = this->private; + + LOCK(&conf->acl_lock); + { + ret = __posix_acl_get(inode, this, &acl_access, &acl_default); + + if (ret != 0) + goto unlock; + + if (acl_access && acl_access_p) + acl_access->refcnt++; + if (acl_default && acl_default_p) + acl_default->refcnt++; + } +unlock: + UNLOCK(&conf->acl_lock); + + if (acl_access_p) + *acl_access_p = acl_access; + if (acl_default_p) + *acl_default_p = acl_default; + + return ret; +} + +mode_t +posix_acl_inherit_mode(struct posix_acl *acl, mode_t modein) +{ + struct posix_ace *ace = NULL; + int count = 0; + int i = 0; + mode_t newmode = 0; + mode_t mode = 0; + struct posix_ace *mask_ce = NULL; + struct posix_ace *group_ce = NULL; + + newmode = mode = modein; + + count = acl->count; + + ace = acl->entries; + for (i = 0; i < count; i++) { + switch (ace->tag) { + case POSIX_ACL_USER_OBJ: + ace->perm &= (mode >> 6) | ~S_IRWXO; + mode &= (ace->perm << 6) | ~S_IRWXU; + break; + case POSIX_ACL_GROUP_OBJ: + group_ce = ace; + break; + case POSIX_ACL_MASK: + mask_ce = ace; + break; + case POSIX_ACL_OTHER: + ace->perm &= (mode) | ~S_IRWXO; + mode &= (ace->perm) | ~S_IRWXO; + break; + } + ace++; + } + + if (mask_ce) { + mask_ce->perm &= (mode >> 3) | ~S_IRWXO; + mode &= (mask_ce->perm << 3) | ~S_IRWXG; + } else if (group_ce) { + group_ce->perm &= (mode >> 3) | ~S_IRWXO; + mode &= (group_ce->perm << 3) | ~S_IRWXG; + } + + newmode = ((modein & (S_IFMT | S_ISUID | S_ISGID | S_ISVTX)) | + (mode & (S_IRWXU | S_IRWXG | S_IRWXO))); + + return newmode; +} + +mode_t +posix_acl_inherit(xlator_t *this, loc_t *loc, dict_t *params, mode_t mode, + int32_t umask, int is_dir) +{ + int ret = 0; + struct posix_acl *par_default = NULL; + struct posix_acl *acl_default = NULL; + struct posix_acl *acl_access = NULL; + struct posix_acl_ctx *ctx = NULL; + char *xattr_default = NULL; + char *xattr_access = NULL; + int size_default = 0; + int size_access = 0; + mode_t retmode = 0; + int16_t tmp_mode = 0; + mode_t client_umask = 0; + + retmode = mode; + client_umask = umask; + ret = dict_get_int16(params, "umask", &tmp_mode); + if (ret == 0) { + client_umask = (mode_t)tmp_mode; + dict_del(params, "umask"); + ret = dict_get_int16(params, "mode", &tmp_mode); + if (ret == 0) { + retmode = (mode_t)tmp_mode; + dict_del(params, "mode"); + } else { + gf_log(this->name, GF_LOG_ERROR, + "client sent umask, but not the original mode"); + } + } + + ret = posix_acl_get(loc->parent, this, NULL, &par_default); + + if (!par_default) + goto out; + + ctx = posix_acl_ctx_new(loc->inode, this); + + acl_access = posix_acl_dup(this, par_default); + if (!acl_access) + goto out; + + client_umask = 0; // No umask if we inherit an ACL + retmode = posix_acl_inherit_mode(acl_access, retmode); + ctx->perm = retmode; + + size_access = posix_acl_to_xattr(this, acl_access, NULL, 0); + xattr_access = GF_CALLOC(1, size_access, gf_posix_acl_mt_char); + if (!xattr_access) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = -1; + goto out; + } + posix_acl_to_xattr(this, acl_access, xattr_access, size_access); + + ret = dict_set_bin(params, POSIX_ACL_ACCESS_XATTR, xattr_access, + size_access); + if (ret) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + GF_FREE(xattr_access); + ret = -1; + goto out; + } + + if (!is_dir) + goto set; + + acl_default = posix_acl_ref(this, par_default); + + size_default = posix_acl_to_xattr(this, acl_default, NULL, 0); + xattr_default = GF_CALLOC(1, size_default, gf_posix_acl_mt_char); + if (!xattr_default) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + ret = -1; + goto out; + } + posix_acl_to_xattr(this, acl_default, xattr_default, size_default); + + ret = dict_set_bin(params, POSIX_ACL_DEFAULT_XATTR, xattr_default, + size_default); + if (ret) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + GF_FREE(xattr_default); + ret = -1; + goto out; + } + +set: + ret = posix_acl_set(loc->inode, this, acl_access, acl_default); + if (ret != 0) + goto out; + +out: + retmode &= ~client_umask; + + if (par_default) + posix_acl_unref(this, par_default); + if (acl_access) + posix_acl_unref(this, acl_access); + if (acl_default) + posix_acl_unref(this, acl_default); + + return retmode; +} + +mode_t +posix_acl_inherit_dir(xlator_t *this, loc_t *loc, dict_t *params, mode_t mode, + int32_t umask) +{ + mode_t retmode = 0; + + retmode = posix_acl_inherit(this, loc, params, mode, umask, 1); + + return retmode; +} + +mode_t +posix_acl_inherit_file(xlator_t *this, loc_t *loc, dict_t *params, mode_t mode, + int32_t umask) +{ + mode_t retmode = 0; + + retmode = posix_acl_inherit(this, loc, params, mode, umask, 0); + + return retmode; +} + +int +posix_acl_ctx_update(inode_t *inode, xlator_t *this, struct iatt *buf, + glusterfs_fop_t fop) +{ + struct posix_acl_ctx *ctx = NULL; + struct posix_acl *acl = NULL; + struct posix_ace *ace = NULL; + struct posix_ace *mask_ce = NULL; + struct posix_ace *group_ce = NULL; + int ret = 0; + int i = 0; + + if (!buf || !buf->ia_ctime) { + /* No need to update ctx if buf is empty */ + gf_log_callingfn(this->name, GF_LOG_DEBUG, "iatt struct is empty (%d)", + fop); + goto out; + } + + LOCK(&inode->lock); + { + ctx = __posix_acl_ctx_get(inode, this, _gf_true); + if (!ctx) { + ret = -1; + goto unlock; + } + + ctx->uid = buf->ia_uid; + ctx->gid = buf->ia_gid; + ctx->perm = st_mode_from_ia(buf->ia_prot, buf->ia_type); + ctx->fop = fop; + + acl = ctx->acl_access; + if (!_does_acl_exist(acl)) + goto unlock; + + /* This is an extended ACL (not minimal acl). In case we + are only refreshing from iatt and not ACL xattrs (for + e.g. from postattributes of setattr() call, we need to + update the corresponding ACEs as well. + */ + ace = acl->entries; + for (i = 0; i < acl->count; i++) { + switch (ace->tag) { + case POSIX_ACL_USER_OBJ: + ace->perm = (ctx->perm & S_IRWXU) >> 6; + break; + case POSIX_ACL_USER: + case POSIX_ACL_GROUP: + break; + case POSIX_ACL_GROUP_OBJ: + group_ce = ace; + break; + case POSIX_ACL_MASK: + mask_ce = ace; + break; + case POSIX_ACL_OTHER: + ace->perm = (ctx->perm & S_IRWXO); + break; + } + ace++; + } + + if (mask_ce) + mask_ce->perm = (ctx->perm & S_IRWXG) >> 3; + else if (group_ce) + group_ce->perm = (ctx->perm & S_IRWXG) >> 3; + else + ret = -1; + } +unlock: + UNLOCK(&inode->lock); +out: + return ret; +} + +int +posix_acl_lookup_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, struct iatt *buf, + dict_t *xattr, struct iatt *postparent) +{ + struct posix_acl *acl_access = NULL; + struct posix_acl *acl_default = NULL; + struct posix_acl *old_access = NULL; + struct posix_acl *old_default = NULL; + struct posix_acl_ctx *ctx = NULL; + data_t *data = NULL; + int ret = 0; + dict_t *my_xattr = NULL; + + if (op_ret != 0) + goto unwind; + + ctx = posix_acl_ctx_new(inode, this); + if (!ctx) { + op_ret = -1; + op_errno = ENOMEM; + goto unwind; + } + + ret = posix_acl_get(inode, this, &old_access, &old_default); + + if (xattr == NULL) + goto acl_set; + + data = dict_get(xattr, POSIX_ACL_ACCESS_XATTR); + if (!data) + goto acl_default; + + if (old_access && + posix_acl_matches_xattr(this, old_access, data->data, data->len)) { + acl_access = posix_acl_ref(this, old_access); + } else { + acl_access = posix_acl_from_xattr(this, data->data, data->len); + } + +acl_default: + data = dict_get(xattr, POSIX_ACL_DEFAULT_XATTR); + if (!data) + goto acl_set; + + if (old_default && + posix_acl_matches_xattr(this, old_default, data->data, data->len)) { + acl_default = posix_acl_ref(this, old_default); + } else { + acl_default = posix_acl_from_xattr(this, data->data, data->len); + } + +acl_set: + posix_acl_ctx_update(inode, this, buf, GF_FOP_LOOKUP); + + ret = posix_acl_set(inode, this, acl_access, acl_default); + if (ret) + gf_log(this->name, GF_LOG_WARNING, "failed to set ACL in context"); +unwind: + my_xattr = frame->local; + frame->local = NULL; + STACK_UNWIND_STRICT(lookup, frame, op_ret, op_errno, inode, buf, xattr, + postparent); + + if (acl_access) + posix_acl_unref(this, acl_access); + if (acl_default) + posix_acl_unref(this, acl_default); + if (old_access) + posix_acl_unref(this, old_access); + if (old_default) + posix_acl_unref(this, old_default); + if (my_xattr) + dict_unref(my_xattr); + + return 0; +} + +int +posix_acl_lookup(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr) +{ + int ret = 0; + dict_t *my_xattr = NULL; + + if (!loc->parent) + /* lookup of / is always permitted */ + goto green; + + if (acl_permits(frame, loc->parent, POSIX_ACL_EXECUTE)) + goto green; + else + goto red; + +green: + if (xattr) { + my_xattr = dict_ref(xattr); + } else { + my_xattr = dict_new(); + } + + ret = dict_set_int8(my_xattr, POSIX_ACL_ACCESS_XATTR, 0); + if (ret) + gf_log(this->name, GF_LOG_WARNING, "failed to set key %s", + POSIX_ACL_ACCESS_XATTR); + + ret = dict_set_int8(my_xattr, POSIX_ACL_DEFAULT_XATTR, 0); + if (ret) + gf_log(this->name, GF_LOG_WARNING, "failed to set key %s", + POSIX_ACL_DEFAULT_XATTR); + + frame->local = my_xattr; + STACK_WIND(frame, posix_acl_lookup_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, loc, my_xattr); + return 0; +red: + STACK_UNWIND_STRICT(lookup, frame, -1, EACCES, NULL, NULL, NULL, NULL); + + return 0; +} + +int +posix_acl_access(call_frame_t *frame, xlator_t *this, loc_t *loc, int mask, + dict_t *xdata) +{ + int op_ret = 0; + int op_errno = 0; + int perm = 0; + int mode = 0; + int is_fuse_call = 0; + + is_fuse_call = __is_fuse_call(frame); + + if (mask & R_OK) + perm |= POSIX_ACL_READ; + if (mask & W_OK) + perm |= POSIX_ACL_WRITE; + if (mask & X_OK) + perm |= POSIX_ACL_EXECUTE; + if (!mask) { + goto unwind; + } + if (!perm) { + op_ret = -1; + op_errno = EINVAL; + goto unwind; + } + + if (is_fuse_call) { + mode = acl_permits(frame, loc->inode, perm); + if (mode) { + op_ret = 0; + op_errno = 0; + } else { + op_ret = -1; + op_errno = EACCES; + } + } else { + if (perm & POSIX_ACL_READ) { + if (acl_permits(frame, loc->inode, POSIX_ACL_READ)) + mode |= POSIX_ACL_READ; + } + + if (perm & POSIX_ACL_WRITE) { + if (acl_permits(frame, loc->inode, POSIX_ACL_WRITE)) + mode |= POSIX_ACL_WRITE; + } + + if (perm & POSIX_ACL_EXECUTE) { + if (acl_permits(frame, loc->inode, POSIX_ACL_EXECUTE)) + mode |= POSIX_ACL_EXECUTE; + } + } + +unwind: + if (is_fuse_call) + STACK_UNWIND_STRICT(access, frame, op_ret, op_errno, NULL); + else + STACK_UNWIND_STRICT(access, frame, 0, mode, NULL); + return 0; +} + +int +posix_acl_truncate(call_frame_t *frame, xlator_t *this, loc_t *loc, off_t off, + dict_t *xdata) +{ + struct posix_acl_ctx *ctx = NULL; + + if (acl_permits(frame, loc->inode, POSIX_ACL_WRITE)) + goto green; + /* NFS does a truncate through SETATTR, the owner does not need write + * permissions for this. Group permissions and root are checked above. + */ + else if (frame->root->pid == NFS_PID) { + ctx = posix_acl_ctx_get(loc->inode, frame->this); + + if (ctx && frame_is_user(frame, ctx->uid)) + goto green; + } + + /* fail by default */ + STACK_UNWIND_STRICT(truncate, frame, -1, EACCES, NULL, NULL, NULL); + return 0; + +green: + STACK_WIND(frame, default_truncate_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, off, xdata); + return 0; +} + +int +posix_acl_open(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + fd_t *fd, dict_t *xdata) +{ + int perm = 0; + + switch (flags & O_ACCMODE) { + case O_RDONLY: + perm = POSIX_ACL_READ; + + /* If O_FMODE_EXEC is present, its good enough + to have '--x' perm, and its not covered in + O_ACCMODE bits */ + if (flags & O_FMODE_EXEC) + perm = POSIX_ACL_EXECUTE; + + break; + case O_WRONLY: + perm = POSIX_ACL_WRITE; + break; + case O_RDWR: + perm = POSIX_ACL_READ | POSIX_ACL_WRITE; + break; + } + + if (flags & (O_TRUNC | O_APPEND)) + perm |= POSIX_ACL_WRITE; + + if (acl_permits(frame, loc->inode, perm)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_open_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->open, loc, flags, fd, xdata); + return 0; +red: + STACK_UNWIND_STRICT(open, frame, -1, EACCES, NULL, NULL); + return 0; +} + +int +posix_acl_readv(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t offset, uint32_t flags, dict_t *xdata) +{ + if (__is_fuse_call(frame)) + goto green; + + if (acl_permits(frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; + +green: + STACK_WIND(frame, default_readv_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readv, fd, size, offset, flags, xdata); + return 0; +red: + STACK_UNWIND_STRICT(readv, frame, -1, EACCES, NULL, 0, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_writev(call_frame_t *frame, xlator_t *this, fd_t *fd, + struct iovec *vector, int count, off_t offset, uint32_t flags, + struct iobref *iobref, dict_t *xdata) +{ + if (__is_fuse_call(frame)) + goto green; + + if (acl_permits(frame, fd->inode, POSIX_ACL_WRITE)) + goto green; + else + goto red; + +green: + STACK_WIND(frame, default_writev_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, fd, vector, count, offset, + flags, iobref, xdata); + return 0; +red: + STACK_UNWIND_STRICT(writev, frame, -1, EACCES, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_ftruncate(call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, + dict_t *xdata) +{ + if (__is_fuse_call(frame)) + goto green; + + if (acl_permits(frame, fd->inode, POSIX_ACL_WRITE)) + goto green; + else + goto red; + +green: + STACK_WIND(frame, default_ftruncate_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, fd, offset, xdata); + return 0; +red: + STACK_UNWIND_STRICT(ftruncate, frame, -1, EACCES, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_opendir(call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd, + dict_t *xdata) +{ + if (acl_permits(frame, loc->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_opendir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->opendir, loc, fd, xdata); + return 0; +red: + STACK_UNWIND_STRICT(opendir, frame, -1, EACCES, NULL, NULL); + return 0; +} + +int +posix_acl_mkdir_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update(inode, this, buf, GF_FOP_MKDIR); + +unwind: + STACK_UNWIND_STRICT(mkdir, frame, op_ret, op_errno, inode, buf, preparent, + postparent, xdata); + return 0; +} + +int +posix_acl_mkdir(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + mode_t umask, dict_t *xdata) +{ + mode_t newmode = 0; + + newmode = mode; + if (acl_permits(frame, loc->parent, POSIX_ACL_WRITE | POSIX_ACL_EXECUTE)) + goto green; + else + goto red; +green: + newmode = posix_acl_inherit_dir(this, loc, xdata, mode, umask); + + STACK_WIND(frame, posix_acl_mkdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, loc, newmode, umask, xdata); + return 0; +red: + STACK_UNWIND_STRICT(mkdir, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_mknod_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, struct iatt *buf, + struct iatt *preparent, struct iatt *postparent, + dict_t *xdata) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update(inode, this, buf, GF_FOP_MKNOD); + +unwind: + STACK_UNWIND_STRICT(mknod, frame, op_ret, op_errno, inode, buf, preparent, + postparent, xdata); + return 0; +} + +int +posix_acl_mknod(call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + dev_t rdev, mode_t umask, dict_t *xdata) +{ + mode_t newmode = 0; + + newmode = mode; + if (acl_permits(frame, loc->parent, POSIX_ACL_WRITE | POSIX_ACL_EXECUTE)) + goto green; + else + goto red; +green: + newmode = posix_acl_inherit_file(this, loc, xdata, mode, umask); + + STACK_WIND(frame, posix_acl_mknod_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mknod, loc, newmode, rdev, umask, + xdata); + return 0; +red: + STACK_UNWIND_STRICT(mknod, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_create_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, fd_t *fd, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update(inode, this, buf, GF_FOP_CREATE); + +unwind: + STACK_UNWIND_STRICT(create, frame, op_ret, op_errno, fd, inode, buf, + preparent, postparent, xdata); + return 0; +} + +int +posix_acl_create(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata) +{ + mode_t newmode = 0; + + newmode = mode; + if (acl_permits(frame, loc->parent, POSIX_ACL_WRITE | POSIX_ACL_EXECUTE)) + goto green; + else + goto red; +green: + newmode = posix_acl_inherit_file(this, loc, xdata, mode, umask); + + STACK_WIND(frame, posix_acl_create_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->create, loc, flags, newmode, umask, fd, + xdata); + return 0; +red: + STACK_UNWIND_STRICT(create, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL, + NULL); + return 0; +} + +int +posix_acl_symlink_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update(inode, this, buf, GF_FOP_SYMLINK); + +unwind: + STACK_UNWIND_STRICT(symlink, frame, op_ret, op_errno, inode, buf, preparent, + postparent, xdata); + return 0; +} + +int +posix_acl_symlink(call_frame_t *frame, xlator_t *this, const char *linkname, + loc_t *loc, mode_t umask, dict_t *xdata) +{ + if (acl_permits(frame, loc->parent, POSIX_ACL_WRITE | POSIX_ACL_EXECUTE)) + goto green; + else + goto red; +green: + STACK_WIND(frame, posix_acl_symlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->symlink, linkname, loc, umask, xdata); + return 0; +red: + STACK_UNWIND_STRICT(symlink, frame, -1, EACCES, NULL, NULL, NULL, NULL, + NULL); + return 0; +} + +int +posix_acl_unlink(call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag, + dict_t *xdata) +{ + if (!sticky_permits(frame, loc->parent, loc->inode)) + goto red; + + if (acl_permits(frame, loc->parent, POSIX_ACL_WRITE | POSIX_ACL_EXECUTE)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_unlink_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, xflag, xdata); + return 0; +red: + STACK_UNWIND_STRICT(unlink, frame, -1, EACCES, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_rmdir(call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + dict_t *xdata) +{ + if (!sticky_permits(frame, loc->parent, loc->inode)) + goto red; + + if (acl_permits(frame, loc->parent, POSIX_ACL_WRITE | POSIX_ACL_EXECUTE)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_rmdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rmdir, loc, flags, xdata); + return 0; +red: + STACK_UNWIND_STRICT(rmdir, frame, -1, EACCES, NULL, NULL, NULL); + return 0; +} + +int +posix_acl_rename(call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new, + dict_t *xdata) +{ + if (!acl_permits(frame, old->parent, POSIX_ACL_WRITE)) + goto red; + + if (!acl_permits(frame, new->parent, POSIX_ACL_WRITE)) + goto red; + + if (!sticky_permits(frame, old->parent, old->inode)) + goto red; + + if (new->inode) { + if (!sticky_permits(frame, new->parent, new->inode)) + goto red; + } + + STACK_WIND(frame, default_rename_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, old, new, xdata); + return 0; +red: + STACK_UNWIND_STRICT(rename, frame, -1, EACCES, NULL, NULL, NULL, NULL, NULL, + NULL); + return 0; +} + +int +posix_acl_link(call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new, + dict_t *xdata) +{ + struct posix_acl_ctx *ctx = NULL; + int op_errno = 0; + + ctx = posix_acl_ctx_get(old->inode, this); + if (!ctx) { + op_errno = EIO; + goto red; + } + + if (!acl_permits(frame, new->parent, POSIX_ACL_WRITE)) { + op_errno = EACCES; + goto red; + } + + if (!sticky_permits(frame, new->parent, new->inode)) { + op_errno = EACCES; + goto red; + } + + STACK_WIND(frame, default_link_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->link, old, new, xdata); + return 0; +red: + STACK_UNWIND_STRICT(link, frame, -1, op_errno, NULL, NULL, NULL, NULL, + NULL); + + return 0; +} + +int +posix_acl_readdir(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t offset, dict_t *xdata) +{ + if (acl_permits(frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_readdir_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readdir, fd, size, offset, xdata); + return 0; +red: + STACK_UNWIND_STRICT(readdir, frame, -1, EACCES, NULL, NULL); + + return 0; +} + +int +posix_acl_readdirp_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, gf_dirent_t *entries, + dict_t *xdata) +{ + gf_dirent_t *entry = NULL; + struct posix_acl *acl_access = NULL; + struct posix_acl *acl_default = NULL; + struct posix_acl_ctx *ctx = NULL; + data_t *data = NULL; + int ret = 0; + + if (op_ret <= 0) + goto unwind; + + list_for_each_entry(entry, &entries->list, list) + { + /* Update the inode ctx */ + if (!entry->dict || !entry->inode) + continue; + + ctx = posix_acl_ctx_new(entry->inode, this); + if (!ctx) { + op_ret = -1; + op_errno = ENOMEM; + goto unwind; + } + + ret = posix_acl_get(entry->inode, this, &acl_access, &acl_default); + + data = dict_get(entry->dict, POSIX_ACL_ACCESS_XATTR); + if (!data) + goto acl_default; + + if (acl_access && + posix_acl_matches_xattr(this, acl_access, data->data, data->len)) + goto acl_default; + + if (acl_access) + posix_acl_unref(this, acl_access); + + acl_access = posix_acl_from_xattr(this, data->data, data->len); + + acl_default: + data = dict_get(entry->dict, POSIX_ACL_DEFAULT_XATTR); + if (!data) + goto acl_set; + + if (acl_default && + posix_acl_matches_xattr(this, acl_default, data->data, data->len)) + goto acl_set; + + if (acl_default) + posix_acl_unref(this, acl_default); + + acl_default = posix_acl_from_xattr(this, data->data, data->len); + + acl_set: + posix_acl_ctx_update(entry->inode, this, &entry->d_stat, + GF_FOP_READDIRP); + + ret = posix_acl_set(entry->inode, this, acl_access, acl_default); + if (ret) + gf_log(this->name, GF_LOG_WARNING, "failed to set ACL in context"); + + if (acl_access) + posix_acl_unref(this, acl_access); + if (acl_default) + posix_acl_unref(this, acl_default); + } + +unwind: + STACK_UNWIND_STRICT(readdirp, frame, op_ret, op_errno, entries, xdata); + return 0; +} + +int +posix_acl_readdirp(call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t offset, dict_t *dict) +{ + int ret = 0; + dict_t *alloc_dict = NULL; + + if (acl_permits(frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + if (!dict) + dict = alloc_dict = dict_new(); + + if (dict) { + ret = dict_set_int8(dict, POSIX_ACL_ACCESS_XATTR, 0); + if (ret) + gf_log(this->name, GF_LOG_WARNING, "failed to set key %s", + POSIX_ACL_ACCESS_XATTR); + + ret = dict_set_int8(dict, POSIX_ACL_DEFAULT_XATTR, 0); + if (ret) + gf_log(this->name, GF_LOG_WARNING, "failed to set key %s", + POSIX_ACL_DEFAULT_XATTR); + } + + STACK_WIND(frame, posix_acl_readdirp_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readdirp, fd, size, offset, dict); + + if (alloc_dict) + dict_unref(alloc_dict); + return 0; +red: + STACK_UNWIND_STRICT(readdirp, frame, -1, EACCES, NULL, NULL); + + return 0; +} + +int +setattr_scrutiny(call_frame_t *frame, inode_t *inode, struct iatt *buf, + int valid) +{ + struct posix_acl_ctx *ctx = NULL; + + if (frame_is_super_user(frame)) + return 0; + + ctx = posix_acl_ctx_get(inode, frame->this); + if (!ctx) + return EIO; + + if (valid & GF_SET_ATTR_MODE) { + /* + The effective UID of the calling process must match the owner of + the file, or the process must be privileged + */ + if (!frame_is_user(frame, ctx->uid)) + return EPERM; + /* + If the calling process is not privileged (Linux: does not have + the CAP_FSETID capability), and the group of the file does not + match the effective group ID of the process or one of its + supplementary group IDs, the S_ISGID bit will be turned off, but + this will not cause an error to be returned. + + */ + if (!frame_in_group(frame, ctx->gid)) + buf->ia_prot.sgid = 0; + } + + if (valid & (GF_SET_ATTR_ATIME | GF_SET_ATTR_MTIME)) { + /* + Changing timestamps is permitted when: either the process has + appropri? ate privileges, or the effective user ID equals the + user ID of the file, or times is NULL and the process has write + permission for the file. + */ + if (!frame_is_user(frame, ctx->uid) && + !acl_permits(frame, inode, POSIX_ACL_WRITE)) + return EACCES; + } + + if (valid & GF_SET_ATTR_UID) { + if ((!frame_is_super_user(frame)) && (buf->ia_uid != ctx->uid)) + return EPERM; + } + + if (valid & GF_SET_ATTR_GID) { + if (!frame_is_user(frame, ctx->uid)) + return EPERM; + if (!frame_in_group(frame, buf->ia_gid)) + return EPERM; + } + + return 0; +} + +int +posix_acl_setattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + inode_t *inode = NULL; + + inode = frame->local; + frame->local = NULL; + + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update(inode, this, postbuf, GF_FOP_SETATTR); + +unwind: + STACK_UNWIND_STRICT(setattr, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + return 0; +} + +int +posix_acl_setattr(call_frame_t *frame, xlator_t *this, loc_t *loc, + struct iatt *buf, int valid, dict_t *xdata) +{ + int op_errno = 0; + + op_errno = setattr_scrutiny(frame, loc->inode, buf, valid); + + if (op_errno) + goto red; + + frame->local = loc->inode; + + STACK_WIND(frame, posix_acl_setattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setattr, loc, buf, valid, xdata); + return 0; +red: + STACK_UNWIND_STRICT(setattr, frame, -1, op_errno, NULL, NULL, NULL); + + return 0; +} + +int +posix_acl_fsetattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + inode_t *inode = NULL; + + inode = frame->local; + frame->local = NULL; + + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update(inode, this, postbuf, GF_FOP_FSETATTR); + +unwind: + STACK_UNWIND_STRICT(fsetattr, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + return 0; +} + +int +posix_acl_fsetattr(call_frame_t *frame, xlator_t *this, fd_t *fd, + struct iatt *buf, int valid, dict_t *xdata) +{ + int op_errno = 0; + + op_errno = setattr_scrutiny(frame, fd->inode, buf, valid); + + if (op_errno) + goto red; + + frame->local = fd->inode; + + STACK_WIND(frame, posix_acl_fsetattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsetattr, fd, buf, valid, xdata); + return 0; +red: + STACK_UNWIND_STRICT(fsetattr, frame, -1, EACCES, NULL, NULL, NULL); + + return 0; +} + +int +setxattr_scrutiny(call_frame_t *frame, inode_t *inode, dict_t *xattr) +{ + struct posix_acl_ctx *ctx = NULL; + int found = 0; + + if (frame_is_super_user(frame)) + return 0; + + ctx = posix_acl_ctx_get(inode, frame->this); + if (!ctx) + return EIO; + + if (dict_get(xattr, POSIX_ACL_ACCESS_XATTR) || + dict_get(xattr, POSIX_ACL_DEFAULT_XATTR) || + dict_get(xattr, GF_POSIX_ACL_ACCESS) || + dict_get(xattr, GF_POSIX_ACL_DEFAULT)) { + found = 1; + if (!frame_is_user(frame, ctx->uid)) + return EPERM; + } + + if (!found && !acl_permits(frame, inode, POSIX_ACL_WRITE)) + return EACCES; + + return 0; +} + +struct posix_acl * +posix_acl_xattr_update(xlator_t *this, inode_t *inode, dict_t *xattr, + char *name, struct posix_acl *old) +{ + struct posix_acl *acl = NULL; + data_t *data = NULL; + + data = dict_get(xattr, name); + if (data) { + acl = posix_acl_from_xattr(this, data->data, data->len); + } + + if (!acl && old) + acl = posix_acl_ref(this, old); + + return acl; +} + +int +posix_acl_setxattr_update(xlator_t *this, inode_t *inode, dict_t *xattr) +{ + struct posix_acl *acl_access = NULL; + struct posix_acl *acl_default = NULL; + struct posix_acl *old_access = NULL; + struct posix_acl *old_default = NULL; + struct posix_acl_ctx *ctx = NULL; + int ret = 0; + + ctx = posix_acl_ctx_get(inode, this); + if (!ctx) + return -1; + + ret = posix_acl_get(inode, this, &old_access, &old_default); + + acl_access = posix_acl_xattr_update(this, inode, xattr, + POSIX_ACL_ACCESS_XATTR, old_access); + + acl_default = posix_acl_xattr_update(this, inode, xattr, + POSIX_ACL_DEFAULT_XATTR, old_default); + + ret = posix_acl_set(inode, this, acl_access, acl_default); + + if (acl_access && acl_access != old_access) { + posix_acl_access_set_mode(acl_access, ctx); + } + + if (acl_access) + posix_acl_unref(this, acl_access); + if (acl_default) + posix_acl_unref(this, acl_default); + if (old_access) + posix_acl_unref(this, old_access); + if (old_default) + posix_acl_unref(this, old_default); + + return ret; +} + +/* * + * Posix acl can be set using other xattr such as GF_POSIX_ACL_ACCESS/ + * GF_POSIX_ACL_DEFAULT which requires to update the context of + * access-control translator + */ +int +handling_other_acl_related_xattr(xlator_t *this, inode_t *inode, dict_t *xattr) +{ + struct posix_acl *acl = NULL; + struct posix_acl_ctx *ctx = NULL; + data_t *data = NULL; + int ret = 0; + + if (!this || !xattr || !inode) + goto out; + + data = dict_get(xattr, POSIX_ACL_ACCESS_XATTR); + if (data) { + acl = posix_acl_from_xattr(this, data->data, data->len); + if (!acl) { + ret = -1; + goto out; + } + + ret = posix_acl_set_specific(inode, this, acl, _gf_true); + if (ret) + goto out; + + ctx = posix_acl_ctx_get(inode, this); + if (!ctx) { + ret = -1; + goto out; + } + + posix_acl_access_set_mode(acl, ctx); + posix_acl_unref(this, acl); + acl = NULL; + } + + data = dict_get(xattr, POSIX_ACL_DEFAULT_XATTR); + if (data) { + acl = posix_acl_from_xattr(this, data->data, data->len); + if (!acl) { + ret = -1; + goto out; + } + + ret = posix_acl_set_specific(inode, this, acl, _gf_false); + } + +out: + if (acl) + posix_acl_unref(this, acl); + + return ret; +} +int +posix_acl_setxattr_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, dict_t *xdata) +{ + /* + * Update the context of posix_acl_translator, if any of + * POSIX_ACL_*_XATTR set in the call back + */ + handling_other_acl_related_xattr(this, (inode_t *)cookie, xdata); + + STACK_UNWIND_STRICT(setxattr, frame, op_ret, op_errno, xdata); + + return 0; +} + +int +posix_acl_setxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, + dict_t *xattr, int flags, dict_t *xdata) +{ + int op_errno = 0; + + op_errno = setxattr_scrutiny(frame, loc->inode, xattr); + + if (op_errno != 0) + goto red; + + if (dict_get(xattr, POSIX_ACL_ACCESS_XATTR) || + dict_get(xattr, POSIX_ACL_DEFAULT_XATTR)) + posix_acl_setxattr_update(this, loc->inode, xattr); + + /* + * inode is required in call back function to update the context + * this translator + */ + STACK_WIND_COOKIE(frame, posix_acl_setxattr_cbk, loc->inode, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->setxattr, loc, + xattr, flags, xdata); + return 0; +red: + STACK_UNWIND_STRICT(setxattr, frame, -1, op_errno, NULL); + + return 0; +} + +int +posix_acl_fsetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd, + dict_t *xattr, int flags, dict_t *xdata) +{ + int op_errno = 0; + + op_errno = setxattr_scrutiny(frame, fd->inode, xattr); + + if (op_errno != 0) + goto red; + + if (dict_get(xattr, POSIX_ACL_ACCESS_XATTR) || + dict_get(xattr, POSIX_ACL_DEFAULT_XATTR)) + posix_acl_setxattr_update(this, fd->inode, xattr); + + STACK_WIND(frame, default_fsetxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsetxattr, fd, xattr, flags, xdata); + return 0; +red: + STACK_UNWIND_STRICT(fsetxattr, frame, -1, op_errno, NULL); + + return 0; +} + +int +posix_acl_getxattr(call_frame_t *frame, xlator_t *this, loc_t *loc, + const char *name, dict_t *xdata) +{ + if (allowed_xattr(name)) + goto green; + + if (acl_permits(frame, loc->inode, POSIX_ACL_READ)) + goto green; + else + goto red; + +green: + STACK_WIND(frame, default_getxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->getxattr, loc, name, xdata); + return 0; + +red: + STACK_UNWIND_STRICT(getxattr, frame, -1, EACCES, NULL, NULL); + + return 0; +} + +int +posix_acl_fgetxattr(call_frame_t *frame, xlator_t *this, fd_t *fd, + const char *name, dict_t *xdata) +{ + if (allowed_xattr(name)) + goto green; + + if (acl_permits(frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_fgetxattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fgetxattr, fd, name, xdata); + return 0; +red: + STACK_UNWIND_STRICT(fgetxattr, frame, -1, EACCES, NULL, NULL); + + return 0; +} + +int +posix_acl_removexattr(call_frame_t *frame, xlator_t *this, loc_t *loc, + const char *name, dict_t *xdata) +{ + struct posix_acl_ctx *ctx = NULL; + int op_errno = EACCES; + + if (frame_is_super_user(frame)) + goto green; + + ctx = posix_acl_ctx_get(loc->inode, this); + if (!ctx) { + op_errno = EIO; + goto red; + } + + if (allowed_xattr(name)) { + if (!frame_is_user(frame, ctx->uid)) { + op_errno = EPERM; + goto red; + } + } + + if (acl_permits(frame, loc->inode, POSIX_ACL_WRITE)) + goto green; + else + goto red; +green: + STACK_WIND(frame, default_removexattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->removexattr, loc, name, xdata); + return 0; +red: + STACK_UNWIND_STRICT(removexattr, frame, -1, op_errno, NULL); + + return 0; +} + +int +posix_acl_forget(xlator_t *this, inode_t *inode) +{ + struct posix_acl_ctx *ctx = NULL; + + ctx = posix_acl_ctx_get(inode, this); + if (!ctx) + goto out; + + if (ctx->acl_access) + posix_acl_unref(this, ctx->acl_access); + + if (ctx->acl_default) + posix_acl_unref(this, ctx->acl_default); + + GF_FREE(ctx); +out: + return 0; +} + +int +reconfigure(xlator_t *this, dict_t *options) +{ + struct posix_acl_conf *conf = NULL; + + conf = this->private; + + GF_OPTION_RECONF("super-uid", conf->super_uid, options, uint32, err); + + return 0; +err: + return -1; +} + +int +init(xlator_t *this) +{ + struct posix_acl_conf *conf = NULL; + struct posix_acl *minacl = NULL; + struct posix_ace *minace = NULL; + + conf = GF_CALLOC(1, sizeof(*conf), gf_posix_acl_mt_conf_t); + if (!conf) { + gf_log(this->name, GF_LOG_ERROR, "out of memory"); + return -1; + } + + LOCK_INIT(&conf->acl_lock); + + this->private = conf; + + minacl = posix_acl_new(this, 3); + if (!minacl) + return -1; + + minace = minacl->entries; + minace[0].tag = POSIX_ACL_USER_OBJ; + minace[1].tag = POSIX_ACL_GROUP_OBJ; + minace[2].tag = POSIX_ACL_OTHER; + + conf->minimal_acl = minacl; + + GF_OPTION_INIT("super-uid", conf->super_uid, uint32, err); + + return 0; +err: + return -1; +} + +void +fini(xlator_t *this) +{ + struct posix_acl_conf *conf = NULL; + struct posix_acl *minacl = NULL; + + conf = this->private; + if (!conf) + return; + this->private = NULL; + + minacl = conf->minimal_acl; + + LOCK(&conf->acl_lock); + { + conf->minimal_acl = NULL; + } + UNLOCK(&conf->acl_lock); + + LOCK_DESTROY(&conf->acl_lock); + + GF_FREE(minacl); + + GF_FREE(conf); + + return; +} + +struct xlator_fops fops = { + .lookup = posix_acl_lookup, + .open = posix_acl_open, +#if FD_MODE_CHECK_IS_IMPLEMENTED + .readv = posix_acl_readv, + .writev = posix_acl_writev, + .ftruncate = posix_acl_ftruncate, + .fsetattr = posix_acl_fsetattr, + .fsetxattr = posix_acl_fsetxattr, + .fgetxattr = posix_acl_fgetxattr, +#endif + .access = posix_acl_access, + .truncate = posix_acl_truncate, + .mkdir = posix_acl_mkdir, + .mknod = posix_acl_mknod, + .create = posix_acl_create, + .symlink = posix_acl_symlink, + .unlink = posix_acl_unlink, + .rmdir = posix_acl_rmdir, + .rename = posix_acl_rename, + .link = posix_acl_link, + .opendir = posix_acl_opendir, + .readdir = posix_acl_readdir, + .readdirp = posix_acl_readdirp, + .setattr = posix_acl_setattr, + .setxattr = posix_acl_setxattr, + .getxattr = posix_acl_getxattr, + .removexattr = posix_acl_removexattr, +}; + +struct xlator_cbks cbks = {.forget = posix_acl_forget}; + +struct volume_options options[] = { + { + .key = {"super-uid"}, + .type = GF_OPTION_TYPE_INT, + .default_value = "0", + .description = "UID to be treated as super user's id instead of 0", + }, + {.key = {NULL}}, +}; + +xlator_api_t xlator_api = { + .init = init, + .fini = fini, + .reconfigure = reconfigure, + .mem_acct_init = mem_acct_init, + .op_version = {1}, /* Present from the initial version */ + .fops = &fops, + .cbks = &cbks, + .options = options, + .identifier = "access-control", + .category = GF_MAINTAINED, +}; |
