diff options
Diffstat (limited to 'drivers/staging/lustre/lustre/obdclass/idmap.c')
-rw-r--r-- | drivers/staging/lustre/lustre/obdclass/idmap.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/drivers/staging/lustre/lustre/obdclass/idmap.c b/drivers/staging/lustre/lustre/obdclass/idmap.c new file mode 100644 index 0000000..622f8d1 --- /dev/null +++ b/drivers/staging/lustre/lustre/obdclass/idmap.c @@ -0,0 +1,474 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + * GPL HEADER END + */ +/* + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Use is subject to license terms. + * + * Copyright (c) 2012, Intel Corporation. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * Lustre is a trademark of Sun Microsystems, Inc. + * + * lustre/obdclass/idmap.c + * + * Lustre user identity mapping. + * + * Author: Fan Yong <fanyong@clusterfs.com> + */ + +#define DEBUG_SUBSYSTEM S_SEC + +#include <lustre_idmap.h> +#include <md_object.h> +#include <obd_support.h> + +#define lustre_get_group_info(group_info) do { \ + atomic_inc(&(group_info)->usage); \ +} while (0) + +#define lustre_put_group_info(group_info) do { \ + if (atomic_dec_and_test(&(group_info)->usage)) \ + groups_free(group_info); \ +} while (0) + +/* + * groups_search() is copied from linux kernel! + * A simple bsearch. + */ +static int lustre_groups_search(group_info_t *group_info, + gid_t grp) +{ + int left, right; + + if (!group_info) + return 0; + + left = 0; + right = group_info->ngroups; + while (left < right) { + int mid = (left + right) / 2; + int cmp = grp - CFS_GROUP_AT(group_info, mid); + + if (cmp > 0) + left = mid + 1; + else if (cmp < 0) + right = mid; + else + return 1; + } + return 0; +} + +void lustre_groups_from_list(group_info_t *ginfo, gid_t *glist) +{ + int i; + int count = ginfo->ngroups; + + /* fill group_info from gid array */ + for (i = 0; i < ginfo->nblocks && count > 0; i++) { + int cp_count = min(CFS_NGROUPS_PER_BLOCK, count); + int off = i * CFS_NGROUPS_PER_BLOCK; + int len = cp_count * sizeof(*glist); + + memcpy(ginfo->blocks[i], glist + off, len); + count -= cp_count; + } +} +EXPORT_SYMBOL(lustre_groups_from_list); + +/* groups_sort() is copied from linux kernel! */ +/* a simple shell-metzner sort */ +void lustre_groups_sort(group_info_t *group_info) +{ + int base, max, stride; + int gidsetsize = group_info->ngroups; + + for (stride = 1; stride < gidsetsize; stride = 3 * stride + 1) + ; /* nothing */ + stride /= 3; + + while (stride) { + max = gidsetsize - stride; + for (base = 0; base < max; base++) { + int left = base; + int right = left + stride; + gid_t tmp = CFS_GROUP_AT(group_info, right); + + while (left >= 0 && + CFS_GROUP_AT(group_info, left) > tmp) { + CFS_GROUP_AT(group_info, right) = + CFS_GROUP_AT(group_info, left); + right = left; + left -= stride; + } + CFS_GROUP_AT(group_info, right) = tmp; + } + stride /= 3; + } +} +EXPORT_SYMBOL(lustre_groups_sort); + +int lustre_in_group_p(struct lu_ucred *mu, gid_t grp) +{ + int rc = 1; + + if (grp != mu->uc_fsgid) { + group_info_t *group_info = NULL; + + if (mu->uc_ginfo || !mu->uc_identity || + mu->uc_valid == UCRED_OLD) + if (grp == mu->uc_suppgids[0] || + grp == mu->uc_suppgids[1]) + return 1; + + if (mu->uc_ginfo) + group_info = mu->uc_ginfo; + else if (mu->uc_identity) + group_info = mu->uc_identity->mi_ginfo; + + if (!group_info) + return 0; + + lustre_get_group_info(group_info); + rc = lustre_groups_search(group_info, grp); + lustre_put_group_info(group_info); + } + return rc; +} +EXPORT_SYMBOL(lustre_in_group_p); + +struct lustre_idmap_entry { + struct list_head lie_rmt_uid_hash; /* hashed as lie_rmt_uid; */ + struct list_head lie_lcl_uid_hash; /* hashed as lie_lcl_uid; */ + struct list_head lie_rmt_gid_hash; /* hashed as lie_rmt_gid; */ + struct list_head lie_lcl_gid_hash; /* hashed as lie_lcl_gid; */ + uid_t lie_rmt_uid; /* remote uid */ + uid_t lie_lcl_uid; /* local uid */ + gid_t lie_rmt_gid; /* remote gid */ + gid_t lie_lcl_gid; /* local gid */ +}; + +static inline __u32 lustre_idmap_hashfunc(__u32 id) +{ + return id & (CFS_IDMAP_HASHSIZE - 1); +} + +static +struct lustre_idmap_entry *idmap_entry_alloc(uid_t rmt_uid, uid_t lcl_uid, + gid_t rmt_gid, gid_t lcl_gid) +{ + struct lustre_idmap_entry *e; + + OBD_ALLOC_PTR(e); + if (e == NULL) + return NULL; + + INIT_LIST_HEAD(&e->lie_rmt_uid_hash); + INIT_LIST_HEAD(&e->lie_lcl_uid_hash); + INIT_LIST_HEAD(&e->lie_rmt_gid_hash); + INIT_LIST_HEAD(&e->lie_lcl_gid_hash); + e->lie_rmt_uid = rmt_uid; + e->lie_lcl_uid = lcl_uid; + e->lie_rmt_gid = rmt_gid; + e->lie_lcl_gid = lcl_gid; + + return e; +} + +static void idmap_entry_free(struct lustre_idmap_entry *e) +{ + if (!list_empty(&e->lie_rmt_uid_hash)) + list_del(&e->lie_rmt_uid_hash); + if (!list_empty(&e->lie_lcl_uid_hash)) + list_del(&e->lie_lcl_uid_hash); + if (!list_empty(&e->lie_rmt_gid_hash)) + list_del(&e->lie_rmt_gid_hash); + if (!list_empty(&e->lie_lcl_gid_hash)) + list_del(&e->lie_lcl_gid_hash); + OBD_FREE_PTR(e); +} + +/* + * return value + * NULL: not found entry + * ERR_PTR(-EACCES): found 1(remote):N(local) mapped entry + * others: found normal entry + */ +static +struct lustre_idmap_entry *idmap_search_entry(struct lustre_idmap_table *t, + uid_t rmt_uid, uid_t lcl_uid, + gid_t rmt_gid, gid_t lcl_gid) +{ + struct list_head *head; + struct lustre_idmap_entry *e; + + head = &t->lit_idmaps[RMT_UIDMAP_IDX][lustre_idmap_hashfunc(rmt_uid)]; + list_for_each_entry(e, head, lie_rmt_uid_hash) + if (e->lie_rmt_uid == rmt_uid) { + if (e->lie_lcl_uid == lcl_uid) { + if (e->lie_rmt_gid == rmt_gid && + e->lie_lcl_gid == lcl_gid) + /* must be quaternion match */ + return e; + } else { + /* 1:N uid mapping */ + CERROR("rmt uid %u already be mapped to %u" + " (new %u)\n", e->lie_rmt_uid, + e->lie_lcl_uid, lcl_uid); + return ERR_PTR(-EACCES); + } + } + + head = &t->lit_idmaps[RMT_GIDMAP_IDX][lustre_idmap_hashfunc(rmt_gid)]; + list_for_each_entry(e, head, lie_rmt_gid_hash) + if (e->lie_rmt_gid == rmt_gid) { + if (e->lie_lcl_gid == lcl_gid) { + if (unlikely(e->lie_rmt_uid == rmt_uid && + e->lie_lcl_uid == lcl_uid)) + /* after uid mapping search above, + * we should never come here */ + LBUG(); + } else { + /* 1:N gid mapping */ + CERROR("rmt gid %u already be mapped to %u" + " (new %u)\n", e->lie_rmt_gid, + e->lie_lcl_gid, lcl_gid); + return ERR_PTR(-EACCES); + } + } + + return NULL; +} + +static __u32 idmap_lookup_uid(struct list_head *hash, int reverse, + __u32 uid) +{ + struct list_head *head = &hash[lustre_idmap_hashfunc(uid)]; + struct lustre_idmap_entry *e; + + if (!reverse) { + list_for_each_entry(e, head, lie_rmt_uid_hash) + if (e->lie_rmt_uid == uid) + return e->lie_lcl_uid; + } else { + list_for_each_entry(e, head, lie_lcl_uid_hash) + if (e->lie_lcl_uid == uid) + return e->lie_rmt_uid; + } + + return CFS_IDMAP_NOTFOUND; +} + +static __u32 idmap_lookup_gid(struct list_head *hash, int reverse, __u32 gid) +{ + struct list_head *head = &hash[lustre_idmap_hashfunc(gid)]; + struct lustre_idmap_entry *e; + + if (!reverse) { + list_for_each_entry(e, head, lie_rmt_gid_hash) + if (e->lie_rmt_gid == gid) + return e->lie_lcl_gid; + } else { + list_for_each_entry(e, head, lie_lcl_gid_hash) + if (e->lie_lcl_gid == gid) + return e->lie_rmt_gid; + } + + return CFS_IDMAP_NOTFOUND; +} + +int lustre_idmap_add(struct lustre_idmap_table *t, + uid_t ruid, uid_t luid, + gid_t rgid, gid_t lgid) +{ + struct lustre_idmap_entry *e0, *e1; + + LASSERT(t); + + spin_lock(&t->lit_lock); + e0 = idmap_search_entry(t, ruid, luid, rgid, lgid); + spin_unlock(&t->lit_lock); + if (!e0) { + e0 = idmap_entry_alloc(ruid, luid, rgid, lgid); + if (!e0) + return -ENOMEM; + + spin_lock(&t->lit_lock); + e1 = idmap_search_entry(t, ruid, luid, rgid, lgid); + if (e1 == NULL) { + list_add_tail(&e0->lie_rmt_uid_hash, + &t->lit_idmaps[RMT_UIDMAP_IDX] + [lustre_idmap_hashfunc(ruid)]); + list_add_tail(&e0->lie_lcl_uid_hash, + &t->lit_idmaps[LCL_UIDMAP_IDX] + [lustre_idmap_hashfunc(luid)]); + list_add_tail(&e0->lie_rmt_gid_hash, + &t->lit_idmaps[RMT_GIDMAP_IDX] + [lustre_idmap_hashfunc(rgid)]); + list_add_tail(&e0->lie_lcl_gid_hash, + &t->lit_idmaps[LCL_GIDMAP_IDX] + [lustre_idmap_hashfunc(lgid)]); + } + spin_unlock(&t->lit_lock); + if (e1 != NULL) { + idmap_entry_free(e0); + if (IS_ERR(e1)) + return PTR_ERR(e1); + } + } else if (IS_ERR(e0)) { + return PTR_ERR(e0); + } + + return 0; +} +EXPORT_SYMBOL(lustre_idmap_add); + +int lustre_idmap_del(struct lustre_idmap_table *t, + uid_t ruid, uid_t luid, + gid_t rgid, gid_t lgid) +{ + struct lustre_idmap_entry *e; + int rc = 0; + + LASSERT(t); + + spin_lock(&t->lit_lock); + e = idmap_search_entry(t, ruid, luid, rgid, lgid); + if (IS_ERR(e)) + rc = PTR_ERR(e); + else if (e) + idmap_entry_free(e); + spin_unlock(&t->lit_lock); + + return rc; +} +EXPORT_SYMBOL(lustre_idmap_del); + +int lustre_idmap_lookup_uid(struct lu_ucred *mu, + struct lustre_idmap_table *t, + int reverse, uid_t uid) +{ + struct list_head *hash; + + if (mu && (mu->uc_valid == UCRED_OLD || mu->uc_valid == UCRED_NEW)) { + if (!reverse) { + if (uid == mu->uc_o_uid) + return mu->uc_uid; + else if (uid == mu->uc_o_fsuid) + return mu->uc_fsuid; + } else { + if (uid == mu->uc_uid) + return mu->uc_o_uid; + else if (uid == mu->uc_fsuid) + return mu->uc_o_fsuid; + } + } + + if (t == NULL) + return CFS_IDMAP_NOTFOUND; + + hash = t->lit_idmaps[reverse ? LCL_UIDMAP_IDX : RMT_UIDMAP_IDX]; + + spin_lock(&t->lit_lock); + uid = idmap_lookup_uid(hash, reverse, uid); + spin_unlock(&t->lit_lock); + + return uid; +} +EXPORT_SYMBOL(lustre_idmap_lookup_uid); + +int lustre_idmap_lookup_gid(struct lu_ucred *mu, struct lustre_idmap_table *t, + int reverse, gid_t gid) +{ + struct list_head *hash; + + if (mu && (mu->uc_valid == UCRED_OLD || mu->uc_valid == UCRED_NEW)) { + if (!reverse) { + if (gid == mu->uc_o_gid) + return mu->uc_gid; + else if (gid == mu->uc_o_fsgid) + return mu->uc_fsgid; + } else { + if (gid == mu->uc_gid) + return mu->uc_o_gid; + else if (gid == mu->uc_fsgid) + return mu->uc_o_fsgid; + } + } + + if (t == NULL) + return CFS_IDMAP_NOTFOUND; + + hash = t->lit_idmaps[reverse ? LCL_GIDMAP_IDX : RMT_GIDMAP_IDX]; + + spin_lock(&t->lit_lock); + gid = idmap_lookup_gid(hash, reverse, gid); + spin_unlock(&t->lit_lock); + + return gid; +} +EXPORT_SYMBOL(lustre_idmap_lookup_gid); + +struct lustre_idmap_table *lustre_idmap_init(void) +{ + struct lustre_idmap_table *t; + int i, j; + + OBD_ALLOC_PTR(t); + if(unlikely(t == NULL)) + return (ERR_PTR(-ENOMEM)); + + spin_lock_init(&t->lit_lock); + for (i = 0; i < ARRAY_SIZE(t->lit_idmaps); i++) + for (j = 0; j < ARRAY_SIZE(t->lit_idmaps[i]); j++) + INIT_LIST_HEAD(&t->lit_idmaps[i][j]); + + return t; +} +EXPORT_SYMBOL(lustre_idmap_init); + +void lustre_idmap_fini(struct lustre_idmap_table *t) +{ + struct list_head *list; + struct lustre_idmap_entry *e; + int i; + LASSERT(t); + + list = t->lit_idmaps[RMT_UIDMAP_IDX]; + spin_lock(&t->lit_lock); + for (i = 0; i < CFS_IDMAP_HASHSIZE; i++) + while (!list_empty(&list[i])) { + e = list_entry(list[i].next, + struct lustre_idmap_entry, + lie_rmt_uid_hash); + idmap_entry_free(e); + } + spin_unlock(&t->lit_lock); + + OBD_FREE_PTR(t); +} +EXPORT_SYMBOL(lustre_idmap_fini); |