diff options
author | Anand Avati <avati@gluster.com> | 2011-07-01 17:18:10 +0000 |
---|---|---|
committer | Anand Avati <avati@gluster.com> | 2011-07-08 02:26:11 -0700 |
commit | 04ed499a8eef1c77c491227941d187778fb5c2e4 (patch) | |
tree | 7ba88ae7d30a01c22bba8e8c70abc6de301ce125 /xlators/system/posix-acl | |
parent | 6433214452c5d8d3ae63c5f4f578862be348ca63 (diff) |
posix-acl: implementation of POSIX ACL as a translator
Signed-off-by: Anand Avati <avati@gluster.com>
BUG: 2815 (Server-enforced ACLs)
URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=2815
Diffstat (limited to 'xlators/system/posix-acl')
-rw-r--r-- | xlators/system/posix-acl/Makefile.am | 1 | ||||
-rw-r--r-- | xlators/system/posix-acl/src/Makefile.am | 13 | ||||
-rw-r--r-- | xlators/system/posix-acl/src/posix-acl-xattr.c | 190 | ||||
-rw-r--r-- | xlators/system/posix-acl/src/posix-acl-xattr.h | 52 | ||||
-rw-r--r-- | xlators/system/posix-acl/src/posix-acl.c | 1792 | ||||
-rw-r--r-- | xlators/system/posix-acl/src/posix-acl.h | 86 |
6 files changed, 2134 insertions, 0 deletions
diff --git a/xlators/system/posix-acl/Makefile.am b/xlators/system/posix-acl/Makefile.am new file mode 100644 index 000000000..af437a64d --- /dev/null +++ b/xlators/system/posix-acl/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/xlators/system/posix-acl/src/Makefile.am b/xlators/system/posix-acl/src/Makefile.am new file mode 100644 index 000000000..57ff27e70 --- /dev/null +++ b/xlators/system/posix-acl/src/Makefile.am @@ -0,0 +1,13 @@ +xlator_LTLIBRARIES = posix-acl.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/system +posix_acl_la_LDFLAGS = -module -avoidversion +posix_acl_la_SOURCES = posix-acl.c posix-acl-xattr.c +posix_acl_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la + +noinst_HEADERS = posix-acl.h posix-acl-xattr.h + +AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS)\ + -I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS)\ + -L$(xlatordir)/ + +CLEANFILES = diff --git a/xlators/system/posix-acl/src/posix-acl-xattr.c b/xlators/system/posix-acl/src/posix-acl-xattr.c new file mode 100644 index 000000000..a473c3666 --- /dev/null +++ b/xlators/system/posix-acl/src/posix-acl-xattr.c @@ -0,0 +1,190 @@ +/* + Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 + <http://www.gnu.org/licenses/>. +*/ + + +#include <stdio.h> +#include <stdlib.h> + +#include "posix-acl.h" +#include "posix-acl-xattr.h" + + +int +posix_ace_cmp (const void *val1, const void *val2) +{ + const struct posix_ace *ace1 = NULL; + const struct posix_ace *ace2 = NULL; + int ret = 0; + + ace1 = val1; + ace2 = val2; + + ret = (ace1->tag - ace2->tag); + if (!ret) + ret = (ace1->id - ace2->id); + + return ret; +} + + +void +posix_acl_normalize (xlator_t *this, struct posix_acl *acl) +{ + qsort (acl->entries, acl->count, sizeof (struct posix_ace *), + posix_ace_cmp); +} + + +struct posix_acl * +posix_acl_from_xattr (xlator_t *this, const char *xattr_buf, int xattr_size) +{ + struct posix_acl_xattr_header *header = NULL; + struct posix_acl_xattr_entry *entry = NULL; + struct posix_acl *acl = NULL; + struct posix_ace *ace = NULL; + int size = 0; + int count = 0; + int i = 0; + + size = xattr_size; + + if (size < sizeof (*header)) + return NULL; + + size -= sizeof (*header); + + if (size % sizeof (*entry)) + return NULL; + + count = size / sizeof (*entry); + + header = (struct posix_acl_xattr_header *) (xattr_buf); + entry = (struct posix_acl_xattr_entry *) (header + 1); + + if (header->version != htole32 (POSIX_ACL_VERSION)) + return NULL; + + acl = posix_acl_new (this, count); + if (!acl) + return NULL; + + ace = acl->entries; + + for (i = 0; i < count; i++) { + ace->tag = letoh16 (entry->tag); + ace->perm = letoh16 (entry->perm); + + switch (ace->tag) { + case POSIX_ACL_USER_OBJ: + case POSIX_ACL_GROUP_OBJ: + case POSIX_ACL_MASK: + case POSIX_ACL_OTHER: + ace->id = POSIX_ACL_UNDEFINED_ID; + break; + + case POSIX_ACL_GROUP: + case POSIX_ACL_USER: + ace->id = letoh32 (entry->id); + break; + + default: + goto err; + } + + ace++; + entry++; + } + + posix_acl_normalize (this, acl); + + return acl; +err: + posix_acl_destroy (this, acl); + return NULL; +} + + +int +posix_acl_to_xattr (xlator_t *this, struct posix_acl *acl, char *xattr_buf, + int xattr_size) +{ + int size = 0; + struct posix_acl_xattr_header *header = NULL; + struct posix_acl_xattr_entry *entry = NULL; + struct posix_ace *ace = NULL; + int i = 0; + + size = sizeof (*header) + (acl->count * sizeof (*entry)); + + if (xattr_size < size) + return size; + + header = (struct posix_acl_xattr_header *) (xattr_buf); + entry = (struct posix_acl_xattr_entry *) (header + 1); + ace = acl->entries; + + header->version = htole32 (POSIX_ACL_VERSION); + + for (i = 0; i < acl->count; i++) { + entry->tag = htole16 (ace->tag); + entry->perm = htole16 (ace->perm); + + switch (ace->tag) { + case POSIX_ACL_USER: + case POSIX_ACL_GROUP: + entry->id = htole32 (ace->id); + break; + default: + entry->id = POSIX_ACL_UNDEFINED_ID; + break; + } + + ace++; + entry++; + } + + return 0; +} + + +int +posix_acl_matches_xattr (xlator_t *this, struct posix_acl *acl, const char *buf, + int size) +{ + struct posix_acl *acl2 = NULL; + int ret = 1; + + acl2 = posix_acl_from_xattr (this, buf, size); + if (!acl2) + return 0; + + if (acl->count != acl2->count) { + ret = 0; + goto out; + } + + if (memcmp (acl->entries, acl2->entries, + (acl->count * sizeof (struct posix_ace)))) + ret = 0; +out: + posix_acl_destroy (this, acl2); + + return ret; +} + diff --git a/xlators/system/posix-acl/src/posix-acl-xattr.h b/xlators/system/posix-acl/src/posix-acl-xattr.h new file mode 100644 index 000000000..008a55db4 --- /dev/null +++ b/xlators/system/posix-acl/src/posix-acl-xattr.h @@ -0,0 +1,52 @@ +/* + Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 + <http://www.gnu.org/licenses/>. +*/ + + +#ifndef _POSIX_ACL_XATTR_H +#define _POSIX_ACL_XATTR_H + +#include <stdint.h> + +#include "common-utils.h" +#include "posix-acl.h" + +#define POSIX_ACL_ACCESS_XATTR "system.posix_acl_access" +#define POSIX_ACL_DEFAULT_XATTR "system.posix_acl_default" + +#define POSIX_ACL_VERSION 2 + +struct posix_acl_xattr_entry { + uint16_t tag; + uint16_t perm; + uint32_t id; +}; + +struct posix_acl_xattr_header { + uint32_t version; + struct posix_acl_xattr_entry entries[0]; +}; + +struct posix_acl *posix_acl_from_xattr (xlator_t *this, const char *buf, int size); + +int posix_acl_to_xattr (xlator_t *this, struct posix_acl *acl, char *buf, int size); + +int posix_acl_matches_xattr (xlator_t *this, struct posix_acl *acl, const char *buf, int size); + + +#endif /* !_POSIX_ACL_XATTR_H */ 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 000000000..f05ed3f95 --- /dev/null +++ b/xlators/system/posix-acl/src/posix-acl.c @@ -0,0 +1,1792 @@ +/* + Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 + <http://www.gnu.org/licenses/>. +*/ + + +#include <errno.h> + +#include "xlator.h" +#include "glusterfs.h" + +#include "posix-acl.h" +#include "posix-acl-xattr.h" + + +#define UINT64(ptr) ((uint64_t)((long)(ptr))) +#define PTR(num) ((void *)((long)(num))) + + +int +whitelisted_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; + return 0; +} + + +int +frame_is_user (call_frame_t *frame, uid_t uid) +{ + return (frame->root->uid == uid); +} + + +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; + + par = posix_acl_ctx_get (parent, frame->this); + ctx = posix_acl_ctx_get (inode, frame->this); + + if (frame_is_user (frame, 0)) + return 1; + + 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 int +acl_permits (call_frame_t *frame, inode_t *inode, int want) +{ + int verdict = 0; + int ret = 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; + + conf = frame->this->private; + + ctx = posix_acl_ctx_get (inode, frame->this); + if (!ctx) + goto red; + + if (frame->root->uid == 0) + goto green; + + ret = posix_acl_get (inode, frame->this, &acl, NULL); + + if (!acl) { + acl = posix_acl_ref (frame->this, conf->minimal_acl); + } + + ace = acl->entries; + + 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: + 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; +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) +{ + 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); + + ctx = CALLOC (1, sizeof (*ctx)); + if (!ctx) + return NULL; + + ret = inode_ctx_put (inode, this, UINT64 (ctx)); + + return ctx; +} + + +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 = CALLOC (1, sizeof (*acl) + (entrycnt * sizeof (*ace))); + 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) +{ + 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_conf *conf = NULL; + struct posix_acl *dup = NULL; + + conf = this->private; + + 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; + + LOCK(&conf->acl_lock); + { + refcnt = --acl->refcnt; + } + UNLOCK(&conf->acl_lock); + + if (!refcnt) + posix_acl_destroy (this, acl); +} + + +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 { + group_ce->perm &= (mode >> 3) | ~S_IRWXO; + mode &= (group_ce->perm << 3) | ~S_IRWXG; + } + + newmode = ((modein & S_IFMT) | (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, + 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; + + retmode = mode; + + ret = posix_acl_get (loc->parent, this, NULL, &par_default); + + if (!par_default) + goto out; + + ctx = posix_acl_ctx_get (loc->inode, this); + + acl_access = posix_acl_dup (this, par_default); + if (!acl_access) + goto out; + + retmode = posix_acl_inherit_mode (acl_access, mode); + ctx->perm = retmode; + + size_access = posix_acl_to_xattr (this, acl_access, NULL, 0); + xattr_access = CALLOC (1, size_access); + 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"); + 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 = CALLOC (1, size_default); + 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"); + ret = -1; + goto out; + } + +set: + ret = posix_acl_set (loc->inode, this, acl_access, acl_default); + if (ret != 0) + goto out; + +out: + 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) +{ + mode_t retmode = 0; + + retmode = posix_acl_inherit (this, loc, params, mode, 1); + + return retmode; +} + + +mode_t +posix_acl_inherit_file (xlator_t *this, loc_t *loc, dict_t *params, mode_t mode) +{ + mode_t retmode = 0; + + retmode = posix_acl_inherit (this, loc, params, mode, 0); + + return retmode; +} + + +int +posix_acl_ctx_update (inode_t *inode, xlator_t *this, struct iatt *buf) +{ + struct posix_acl_ctx *ctx = NULL; + int ret = 0; + + ctx = posix_acl_ctx_get (inode, this); + if (!ctx) { + ret = -1; + goto out; + } + + LOCK(&inode->lock); + { + ctx->uid = buf->ia_uid; + ctx->gid = buf->ia_gid; + ctx->perm = st_mode_from_ia (buf->ia_prot, buf->ia_type); + } + 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; + data_t *data = NULL; + int ret = 0; + dict_t *my_xattr = NULL; + + if (op_ret != 0) + goto unwind; + + ret = posix_acl_get (inode, this, &old_access, &old_default); + + 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); + + ret = posix_acl_set (inode, this, acl_access, acl_default); + +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); + ret = dict_set_int8 (my_xattr, POSIX_ACL_DEFAULT_XATTR, 0); + + 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) +{ + int op_ret = 0; + int op_errno = 0; + int perm = 0; + + + 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 (!perm) { + op_ret = -1; + op_errno = EINVAL; + goto unwind; + } + + if (acl_permits (frame, loc->inode, perm)) { + op_ret = 0; + op_errno = 0; + } else { + op_ret = -1; + op_errno = EACCES; + } + +unwind: + STACK_UNWIND_STRICT (access, frame, op_ret, op_errno); + + return 0; +} + + +int +posix_acl_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iatt *prebuf, + struct iatt *postbuf) +{ + STACK_UNWIND_STRICT (truncate, frame, op_ret, op_errno, prebuf, postbuf); + + return 0; +} + + +int +posix_acl_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t off) +{ + if (acl_permits (frame, loc->inode, POSIX_ACL_WRITE)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_truncate_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->truncate, + loc, off); + return 0; +red: + STACK_UNWIND_STRICT (truncate, frame, -1, EACCES, NULL, NULL); + return 0; +} + + +int +posix_acl_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, fd_t *fd) +{ + STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd); + + return 0; +} + + +int +posix_acl_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + fd_t *fd, int wbflags) +{ + int perm = 0; + + switch (flags & O_ACCMODE) { + case O_RDONLY: + perm = POSIX_ACL_READ; + break; + case O_WRONLY: + perm = POSIX_ACL_WRITE; + break; + case O_RDWR: + perm = POSIX_ACL_READ|POSIX_ACL_WRITE; + break; + } + + if (acl_permits (frame, loc->inode, perm)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_open_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->open, + loc, flags, fd, wbflags); + return 0; +red: + STACK_UNWIND_STRICT (open, frame, -1, EACCES, NULL); + return 0; +} + + + +int +posix_acl_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, fd_t *fd) +{ + STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd); + + return 0; +} + + +int +posix_acl_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd) +{ + if (acl_permits (frame, loc->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_opendir_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->opendir, + loc, fd); + return 0; +red: + STACK_UNWIND_STRICT (opendir, frame, -1, EACCES, 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) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update (inode, this, buf); + +unwind: + STACK_UNWIND_STRICT (mkdir, frame, op_ret, op_errno, inode, buf, + preparent, postparent); + return 0; +} + + +int +posix_acl_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + dict_t *params) +{ + 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, params, mode); + + STACK_WIND (frame, posix_acl_mkdir_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, + loc, newmode, params); + return 0; +red: + STACK_UNWIND_STRICT (mkdir, frame, -1, EACCES, 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) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update (inode, this, buf); + +unwind: + STACK_UNWIND_STRICT (mknod, frame, op_ret, op_errno, inode, buf, + preparent, postparent); + return 0; +} + + +int +posix_acl_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + dev_t rdev, dict_t *params) +{ + 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, params, mode); + + STACK_WIND (frame, posix_acl_mknod_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod, + loc, newmode, rdev, params); + return 0; +red: + STACK_UNWIND_STRICT (mknod, frame, -1, EACCES, 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) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update (inode, this, buf); + +unwind: + STACK_UNWIND_STRICT (create, frame, op_ret, op_errno, fd, inode, buf, + preparent, postparent); + return 0; +} + + +int +posix_acl_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, + mode_t mode, fd_t *fd, dict_t *params) +{ + 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, params, mode); + + STACK_WIND (frame, posix_acl_create_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->create, + loc, flags, newmode, fd, params); + return 0; +red: + STACK_UNWIND_STRICT (create, frame, -1, EACCES, 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) +{ + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update (inode, this, buf); + +unwind: + STACK_UNWIND_STRICT (symlink, frame, op_ret, op_errno, inode, buf, + preparent, postparent); + return 0; +} + + +int +posix_acl_symlink (call_frame_t *frame, xlator_t *this, const char *linkname, + loc_t *loc, dict_t *params) +{ + 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, params); + return 0; +red: + STACK_UNWIND_STRICT (symlink, frame, -1, EACCES, NULL, NULL, NULL, NULL); + return 0; +} + + +int +posix_acl_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, + struct iatt *preparent, struct iatt *postparent) +{ + if (op_ret != 0) + goto unwind; +unwind: + STACK_UNWIND_STRICT (unlink, frame, op_ret, op_errno, + preparent, postparent); + return 0; +} + + +int +posix_acl_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) +{ + 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, posix_acl_unlink_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->unlink, + loc); + return 0; +red: + STACK_UNWIND_STRICT (unlink, frame, -1, EACCES, NULL, NULL); + return 0; +} + + +int +posix_acl_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, + struct iatt *preparent, struct iatt *postparent) +{ + if (op_ret != 0) + goto unwind; +unwind: + STACK_UNWIND_STRICT (rmdir, frame, op_ret, op_errno, + preparent, postparent); + return 0; +} + + +int +posix_acl_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags) +{ + 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, posix_acl_rmdir_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->rmdir, + loc, flags); + return 0; +red: + STACK_UNWIND_STRICT (rmdir, frame, -1, EACCES, NULL, NULL); + return 0; +} + + +int +posix_acl_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, struct iatt *buf, + struct iatt *preoldparent, struct iatt *postoldparent, + struct iatt *prenewparent, struct iatt *postnewparent) +{ + if (op_ret != 0) + goto unwind; +unwind: + STACK_UNWIND_STRICT (rename, frame, op_ret, op_errno, buf, + preoldparent, postoldparent, + prenewparent, postnewparent); + return 0; +} + + +int +posix_acl_rename (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new) +{ + 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, posix_acl_rename_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename, + old, new); + return 0; +red: + STACK_UNWIND_STRICT (rename, frame, -1, EACCES, NULL, NULL, NULL, NULL, + NULL); + return 0; +} + + +int +posix_acl_link_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) +{ + if (op_ret != 0) + goto unwind; +unwind: + STACK_UNWIND_STRICT (link, frame, op_ret, op_errno, inode, buf, + preparent, postparent); + return 0; +} + + +int +posix_acl_link (call_frame_t *frame, xlator_t *this, loc_t *old, loc_t *new) +{ + 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; + } + + STACK_WIND (frame, posix_acl_link_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->link, + old, new); + return 0; +red: + STACK_UNWIND_STRICT (link, frame, -1, op_errno, NULL, NULL, NULL, NULL); + + return 0; +} + + +int +posix_acl_readdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, gf_dirent_t *entries) +{ + if (op_ret != 0) + goto unwind; +unwind: + STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, entries); + return 0; +} + + +int +posix_acl_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t offset) +{ + if (acl_permits (frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_readdir_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdir, + fd, size, offset); + return 0; +red: + STACK_UNWIND_STRICT (readdir, frame, -1, EACCES, 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) +{ + if (op_ret != 0) + goto unwind; +unwind: + STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, entries); + return 0; +} + + +int +posix_acl_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t offset) +{ + if (acl_permits (frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_readdirp_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdirp, + fd, size, offset); + return 0; +red: + STACK_UNWIND_STRICT (readdirp, frame, -1, EACCES, 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->root->uid == 0) + 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->root->uid != 0) && + (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) +{ + inode_t *inode = NULL; + + inode = frame->local; + frame->local = NULL; + + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update (inode, this, postbuf); + +unwind: + STACK_UNWIND_STRICT (setattr, frame, op_ret, op_errno, prebuf, postbuf); + return 0; +} + + +int +posix_acl_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, + struct iatt *buf, int valid) +{ + 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); + return 0; +red: + STACK_UNWIND_STRICT (setattr, frame, -1, op_errno, 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) +{ + inode_t *inode = NULL; + + inode = frame->local; + frame->local = NULL; + + if (op_ret != 0) + goto unwind; + + posix_acl_ctx_update (inode, this, postbuf); + +unwind: + STACK_UNWIND_STRICT (fsetattr, frame, op_ret, op_errno, prebuf, postbuf); + return 0; +} + + +int +posix_acl_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, + struct iatt *buf, int valid) +{ + 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); + return 0; +red: + STACK_UNWIND_STRICT (fsetattr, frame, -1, EACCES, 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->root->uid == 0) + return 0; + + ctx = posix_acl_ctx_get (inode, frame->this); + if (!ctx) + return EIO; + + if (dict_get (xattr, POSIX_ACL_ACCESS_XATTR)) { + found = 1; + if (!frame_is_user (frame, ctx->uid)) + return EPERM; + } + + if (dict_get (xattr, POSIX_ACL_DEFAULT_XATTR)) { + 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; + mode_t mode = 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) { + mode = 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 0; +} + + +int +posix_acl_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno) +{ + STACK_UNWIND_STRICT (setxattr, frame, op_ret, op_errno); + + return 0; +} + + +int +posix_acl_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, + dict_t *xattr, int flags) +{ + int op_errno = 0; + + op_errno = setxattr_scrutiny (frame, loc->inode, xattr); + + if (op_errno != 0) + goto red; + + posix_acl_setxattr_update (this, loc->inode, xattr); + + STACK_WIND (frame, posix_acl_setxattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->setxattr, + loc, xattr, flags); + return 0; +red: + STACK_UNWIND_STRICT (setxattr, frame, -1, op_errno); + + return 0; +} + + +int +posix_acl_fsetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno) +{ + STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno); + + return 0; +} + + +int +posix_acl_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, + dict_t *xattr, int flags) +{ + int op_errno = 0; + + op_errno = setxattr_scrutiny (frame, fd->inode, xattr); + + if (op_errno != 0) + goto red; + + posix_acl_setxattr_update (this, fd->inode, xattr); + + STACK_WIND (frame, posix_acl_fsetxattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetxattr, + fd, xattr, flags); + return 0; +red: + STACK_UNWIND_STRICT (fsetxattr, frame, -1, op_errno); + + return 0; +} + + +int +posix_acl_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, dict_t *xattr) +{ + STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, xattr); + + return 0; +} + + +int +posix_acl_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, + const char *name) +{ + if (whitelisted_xattr (name)) + goto green; + + if (acl_permits (frame, loc->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_getxattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->getxattr, + loc, name); + return 0; +red: + STACK_UNWIND_STRICT (getxattr, frame, -1, EACCES, NULL); + + return 0; +} + + +int +posix_acl_fgetxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, dict_t *xattr) +{ + STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, xattr); + + return 0; +} + + +int +posix_acl_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, + const char *name) +{ + if (whitelisted_xattr (name)) + goto green; + + if (acl_permits (frame, fd->inode, POSIX_ACL_READ)) + goto green; + else + goto red; +green: + STACK_WIND (frame, posix_acl_fgetxattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->fgetxattr, + fd, name); + return 0; +red: + STACK_UNWIND_STRICT (fgetxattr, frame, -1, EACCES, NULL); + + return 0; +} + + +int +posix_acl_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno) +{ + STACK_UNWIND_STRICT (removexattr, frame, op_ret, op_errno); + + return 0; +} + + +int +posix_acl_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc, + const char *name) +{ + struct posix_acl_ctx *ctx = NULL; + int op_errno = EACCES; + + if (frame_is_user (frame, 0)) + goto green; + + ctx = posix_acl_ctx_get (loc->inode, this); + if (!ctx) { + op_errno = EIO; + goto red; + } + + if (whitelisted_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, posix_acl_removexattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->removexattr, + loc, name); + return 0; +red: + STACK_UNWIND_STRICT (removexattr, frame, -1, op_errno); + + 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); + + FREE (ctx); +out: + return 0; +} + + +int +init (xlator_t *this) +{ + struct posix_acl_conf *conf = NULL; + struct posix_acl *minacl = NULL; + struct posix_ace *minace = NULL; + + conf = CALLOC (1, sizeof (*conf)); + 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; + + return 0; +} + + +int +fini (xlator_t *this) +{ + return 0; +} + + +struct xlator_fops fops = { + .lookup = posix_acl_lookup, + .open = posix_acl_open, + .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, + .fsetattr = posix_acl_fsetattr, + .setxattr = posix_acl_setxattr, + .fsetxattr = posix_acl_fsetxattr, + .getxattr = posix_acl_getxattr, + .fgetxattr = posix_acl_fgetxattr, + .removexattr = posix_acl_removexattr, +}; + + +struct xlator_cbks cbks = { + .forget = posix_acl_forget +}; diff --git a/xlators/system/posix-acl/src/posix-acl.h b/xlators/system/posix-acl/src/posix-acl.h new file mode 100644 index 000000000..46b56d6f3 --- /dev/null +++ b/xlators/system/posix-acl/src/posix-acl.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2011 Gluster, Inc. <http://www.gluster.com> + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 + <http://www.gnu.org/licenses/>. +*/ + + +#ifndef _POSIX_ACL_H +#define _POSIX_ACL_H + +#include <stdint.h> + +#include "xlator.h" +#include "common-utils.h" +#include "byte-order.h" + + +#define POSIX_ACL_READ (0x04) +#define POSIX_ACL_WRITE (0x02) +#define POSIX_ACL_EXECUTE (0x01) + +#define POSIX_ACL_UNDEFINED_TAG (0x00) +#define POSIX_ACL_USER_OBJ (0x01) +#define POSIX_ACL_USER (0x02) +#define POSIX_ACL_GROUP_OBJ (0x04) +#define POSIX_ACL_GROUP (0x08) +#define POSIX_ACL_MASK (0x10) +#define POSIX_ACL_OTHER (0x20) + +#define POSIX_ACL_UNDEFINED_ID ((id_t)-1) + + +struct posix_ace { + uint16_t tag; + uint16_t perm; + uint32_t id; +}; + + +struct posix_acl { + int refcnt; + int count; + struct posix_ace entries[0]; +}; + + +struct posix_acl_ctx { + uid_t uid; + gid_t gid; + mode_t perm; + struct posix_acl *acl_access; + struct posix_acl *acl_default; +}; + + +struct posix_acl_conf { + gf_lock_t acl_lock; + struct posix_acl *minimal_acl; +}; + + +struct posix_acl *posix_acl_new (xlator_t *this, int entry_count); +struct posix_acl *posix_acl_ref (xlator_t *this, struct posix_acl *acl); +void posix_acl_unref (xlator_t *this, struct posix_acl *acl); +void posix_acl_destroy (xlator_t *this, struct posix_acl *acl); +struct posix_acl_ctx *posix_acl_ctx_get (inode_t *inode, xlator_t *this); +int posix_acl_get (inode_t *inode, xlator_t *this, + struct posix_acl **acl_access_p, + struct posix_acl **acl_default_p); +int posix_acl_set (inode_t *inode, xlator_t *this, struct posix_acl *acl_access, + struct posix_acl *acl_default); + +#endif /* !_POSIX_ACL_H */ |