diff options
Diffstat (limited to 'sys/nfs4client/nfs4_idmap.c')
-rw-r--r-- | sys/nfs4client/nfs4_idmap.c | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/sys/nfs4client/nfs4_idmap.c b/sys/nfs4client/nfs4_idmap.c new file mode 100644 index 000000000000..1b932e6cdaf8 --- /dev/null +++ b/sys/nfs4client/nfs4_idmap.c @@ -0,0 +1,513 @@ +/* $FreeBSD$ */ +/* $Id: nfs4_idmap.c,v 1.4 2003/11/05 14:58:59 rees Exp $ */ + +/* + * copyright (c) 2003 + * the regents of the university of michigan + * all rights reserved + * + * permission is granted to use, copy, create derivative works and redistribute + * this software and such derivative works for any purpose, so long as the name + * of the university of michigan is not used in any advertising or publicity + * pertaining to the use or distribution of this software without specific, + * written prior authorization. if the above copyright notice or any other + * identification of the university of michigan is included in any copy of any + * portion of this software, then the disclaimer below must also be included. + * + * this software is provided as is, without representation from the university + * of michigan as to its fitness for any purpose, and without warranty by the + * university of michigan of any kind, either express or implied, including + * without limitation the implied warranties of merchantability and fitness for + * a particular purpose. the regents of the university of michigan shall not be + * liable for any damages, including special, indirect, incidental, or + * consequential damages, with respect to any claim arising out of or in + * connection with the use of the software, even if it has been or is hereafter + * advised of the possibility of such damages. + */ + +/* TODO: + * o validate ascii + * */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/lockmgr.h> +#include <sys/fnv_hash.h> +#include <sys/proc.h> +#include <sys/syscall.h> +#include <sys/sysent.h> +#include <sys/libkern.h> + +#include <rpc/rpcclnt.h> + +#include <nfs4client/nfs4_dev.h> +#include <nfs4client/nfs4_idmap.h> + + +#ifdef IDMAPVERBOSE +#define IDMAP_DEBUG(X...) printf(X); +#else +#define IDMAP_DEBUG(X...) +#endif + +#define IDMAP_HASH_SIZE 37 + +MALLOC_DEFINE(M_IDMAP, "idmap", "idmap"); + +#define idmap_entry_get(ID) MALLOC((ID), struct idmap_entry, sizeof(struct idmap_entry), M_IDMAP, M_WAITOK | M_ZERO) +#define idmap_entry_put(ID) FREE((ID), M_IDMAP) + + + +struct idmap_entry { + struct idmap_msg id_info; + + TAILQ_ENTRY(idmap_entry) id_entry_id; + TAILQ_ENTRY(idmap_entry) id_entry_name; +}; + +struct idmap_hash { + TAILQ_HEAD(, idmap_entry) hash_name[IDMAP_HASH_SIZE]; + TAILQ_HEAD(, idmap_entry) hash_id[IDMAP_HASH_SIZE]; + + struct lock hash_lock; +}; + +#define IDMAP_RLOCK(lock) lockmgr(lock, LK_SHARED, NULL, curthread) +#define IDMAP_WLOCK(lock) lockmgr(lock, LK_EXCLUSIVE, NULL, curthread) +#define IDMAP_UNLOCK(lock) lockmgr(lock, LK_RELEASE, NULL, curthread) + + +static struct idmap_hash idmap_uid_hash; +static struct idmap_hash idmap_gid_hash; + +static struct idmap_entry * idmap_name_lookup(uint32_t, char *); +static struct idmap_entry * idmap_id_lookup(uint32_t, ident_t); +static int idmap_upcall_name(uint32_t, char *, struct idmap_entry **); +static int idmap_upcall_id(uint32_t , ident_t, struct idmap_entry ** ); +static int idmap_add(struct idmap_entry *); + +static int +idmap_upcall_name(uint32_t type, char * name, struct idmap_entry ** found) +{ + int error; + struct idmap_entry * e; + size_t len, siz; + + if (type > IDMAP_MAX_TYPE || type == 0) { + IDMAP_DEBUG("bad type %d\n", type); + return EINVAL; /* XXX */ + } + + if (name == NULL || (len = strlen(name)) == 0 || len > IDMAP_MAXNAMELEN) { + IDMAP_DEBUG("idmap_upcall_name: bad name\n"); + return EFAULT; /* XXX */ + } + + MALLOC(e, struct idmap_entry *, sizeof(struct idmap_entry), M_IDMAP, + M_WAITOK | M_ZERO); + + e->id_info.id_type = type; + bcopy(name, e->id_info.id_name, len); + e->id_info.id_namelen = len; + + + siz = sizeof(struct idmap_msg); + error = nfs4dev_call(NFS4DEV_TYPE_IDMAP, (caddr_t)&e->id_info, siz, + (caddr_t)&e->id_info, &siz); + + if (error) { + IDMAP_DEBUG("error %d in nfs4dev_upcall()\n", error); + *found = NULL; + return error; + } + + if (siz != sizeof(struct idmap_msg)) { + IDMAP_DEBUG("bad size of returned message\n"); + *found = NULL; + return EFAULT; + } + + + *found = e; + return 0; +} + +static int +idmap_upcall_id(uint32_t type, ident_t id, struct idmap_entry ** found) +{ + int error; + struct idmap_entry * e; + size_t siz; + + if (type > IDMAP_MAX_TYPE) + panic("bad type"); /* XXX */ + + MALLOC(e, struct idmap_entry *, sizeof(struct idmap_entry), M_IDMAP, + M_WAITOK | M_ZERO); + + e->id_info.id_type = type; + e->id_info.id_namelen = 0; /* should already */ + e->id_info.id_id = id; + + siz = sizeof(struct idmap_msg); + error = nfs4dev_call(NFS4DEV_TYPE_IDMAP, (caddr_t)&e->id_info, siz, + (caddr_t)&e->id_info, &siz); + + if (error) { + IDMAP_DEBUG("error %d in nfs4dev_upcall()\n", error); + *found = NULL; + return error; + } + + if (siz != sizeof(struct idmap_msg)) { + IDMAP_DEBUG("bad size of returned message\n"); + *found = NULL; + return EFAULT; + } + + *found = e; + return 0; +} + +static int +idmap_hashf(struct idmap_entry *e, uint32_t * hval_id, uint32_t * hval_name) +{ + switch (e->id_info.id_type) { + case IDMAP_TYPE_UID: + *hval_id = e->id_info.id_id.uid % IDMAP_HASH_SIZE; + break; + case IDMAP_TYPE_GID: + *hval_id = e->id_info.id_id.gid % IDMAP_HASH_SIZE; + break; + default: + /* XXX yikes! */ + panic("hashf: bad type!"); + break; + } + + if (e->id_info.id_namelen == 0) + /* XXX */ panic("hashf: bad name"); + + *hval_name = fnv_32_str(e->id_info.id_name, FNV1_32_INIT) % IDMAP_HASH_SIZE; + return 0; +} + +static int +idmap_add(struct idmap_entry * e) +{ + struct idmap_hash * hash; + uint32_t hval_id, hval_name; + + if (e == NULL) + panic("idmap_add null"); + + if (e->id_info.id_namelen == 0) + panic("idmap_add name of len 0"); + + switch (e->id_info.id_type) { + case IDMAP_TYPE_UID: + hash = &idmap_uid_hash; + break; + case IDMAP_TYPE_GID: + hash = &idmap_gid_hash; + break; + default: + /* XXX yikes */ + panic("idmap add: bad type!"); + break; + } + + if (idmap_hashf(e, &hval_id, &hval_name) != 0) { + IDMAP_DEBUG("idmap_hashf fails!\n"); + return -1; + } + + IDMAP_WLOCK(&hash->hash_lock); + + TAILQ_INSERT_TAIL(&hash->hash_id[hval_id], e, id_entry_id); + TAILQ_INSERT_TAIL(&hash->hash_name[hval_name], e, id_entry_name); + + IDMAP_UNLOCK(&hash->hash_lock); + + return 0; +} + +static struct idmap_entry * +idmap_id_lookup(uint32_t type, ident_t id) +{ + struct idmap_hash * hash; + uint32_t hval; + struct idmap_entry * e; + + switch (type) { + case IDMAP_TYPE_UID: + hash = &idmap_uid_hash; + hval = id.uid % IDMAP_HASH_SIZE; + break; + case IDMAP_TYPE_GID: + hash = &idmap_gid_hash; + hval = id.gid % IDMAP_HASH_SIZE; + break; + default: + /* XXX yikes */ + panic("lookup: bad type!"); + break; + } + + + IDMAP_RLOCK(&hash->hash_lock); + + TAILQ_FOREACH(e, &hash->hash_id[hval], id_entry_name) { + if ((type == IDMAP_TYPE_UID && e->id_info.id_id.uid == id.uid)|| + (type == IDMAP_TYPE_GID && e->id_info.id_id.gid == id.gid)) { + IDMAP_UNLOCK(&hash->hash_lock); + return e; + } + } + + IDMAP_UNLOCK(&hash->hash_lock); + return NULL; +} + +static struct idmap_entry * +idmap_name_lookup(uint32_t type, char * name) +{ + struct idmap_hash * hash; + uint32_t hval; + struct idmap_entry * e; + size_t len; + + switch (type) { + case IDMAP_TYPE_UID: + hash = &idmap_uid_hash; + break; + case IDMAP_TYPE_GID: + hash = &idmap_gid_hash; + break; + default: + /* XXX yikes */ + panic("lookup: bad type!"); + break; + } + + len = strlen(name); + + if (len == 0 || len > IDMAP_MAXNAMELEN) { + IDMAP_DEBUG("bad name length %d\n", len); + return NULL; + } + + hval = fnv_32_str(name, FNV1_32_INIT) % IDMAP_HASH_SIZE; + + IDMAP_RLOCK(&hash->hash_lock); + + TAILQ_FOREACH(e, &hash->hash_name[hval], id_entry_name) { + if ((strlen(e->id_info.id_name) == strlen(name)) && strncmp(e->id_info.id_name, name, strlen(name)) == 0) { + IDMAP_UNLOCK(&hash->hash_lock); + return e; + } + } + + IDMAP_UNLOCK(&hash->hash_lock); + return NULL; +} + +void +idmap_init(void) +{ + unsigned int i; + + for (i=0; i<IDMAP_HASH_SIZE; i++) { + TAILQ_INIT(&idmap_uid_hash.hash_name[i]); + TAILQ_INIT(&idmap_uid_hash.hash_id[i]); + + TAILQ_INIT(&idmap_gid_hash.hash_name[i]); + TAILQ_INIT(&idmap_gid_hash.hash_id[i]); + } + + lockinit(&idmap_uid_hash.hash_lock, PLOCK, "idmap uid hash table", 0,0); + lockinit(&idmap_gid_hash.hash_lock, PLOCK, "idmap gid hash table", 0,0); + +} + +void idmap_uninit(void) +{ + struct idmap_entry * e; + int i; + + lockdestroy(&idmap_uid_hash.hash_lock); + lockdestroy(&idmap_gid_hash.hash_lock); + + for (i=0; i<IDMAP_HASH_SIZE; i++) { + while(!TAILQ_EMPTY(&idmap_uid_hash.hash_name[i])) { + e = TAILQ_FIRST(&idmap_uid_hash.hash_name[i]); + TAILQ_REMOVE(&idmap_uid_hash.hash_name[i], e, id_entry_name); + TAILQ_REMOVE(&idmap_uid_hash.hash_id[i], e, id_entry_id); + FREE(e, M_IDMAP); + } + + while(!TAILQ_EMPTY(&idmap_gid_hash.hash_name[i])) { + e = TAILQ_FIRST(&idmap_gid_hash.hash_name[i]); + TAILQ_REMOVE(&idmap_gid_hash.hash_name[i], e, id_entry_name); + TAILQ_REMOVE(&idmap_gid_hash.hash_id[i], e, id_entry_id); + FREE(e, M_IDMAP); + } + + } +} + +int +idmap_uid_to_name(uid_t uid, char ** name, size_t * len) +{ + struct idmap_entry * e; + int error = 0; + ident_t id; + + id.uid = uid; + + + if ((e = idmap_id_lookup(IDMAP_TYPE_UID, id)) == NULL) { + if ((error = idmap_upcall_id(IDMAP_TYPE_UID, id, &e)) != 0) { + IDMAP_DEBUG("error in upcall\n"); + return error; + } + + if (e == NULL) { + IDMAP_DEBUG("no error from upcall, but no data returned\n"); + return EFAULT; + } + + if (idmap_add(e) != 0) { + IDMAP_DEBUG("idmap_add failed\n"); + FREE(e, M_IDMAP); + } + } + + *name = e->id_info.id_name; + *len = e->id_info.id_namelen; + return 0; +} + +int +idmap_gid_to_name(gid_t gid, char ** name, size_t * len) +{ + struct idmap_entry * e; + int error = 0; + ident_t id; + + id.gid = gid; + + + if ((e = idmap_id_lookup(IDMAP_TYPE_GID, id)) == NULL) { + if ((error = idmap_upcall_id(IDMAP_TYPE_GID, (ident_t)id, &e))) { + IDMAP_DEBUG("error in upcall\n"); + return error; + } + + if (e == NULL) { + IDMAP_DEBUG("no error from upcall, but no data returned\n"); + return EFAULT; + } + + if (idmap_add(e) != 0) { + IDMAP_DEBUG("idmap_add failed\n"); + FREE(e, M_IDMAP); + } + } + + *name = e->id_info.id_name; + *len = e->id_info.id_namelen; + return 0; +} + +int +idmap_name_to_uid(char * name, size_t len, uid_t * id) +{ + struct idmap_entry * e; + int error = 0; + char * namestr; + + if (name == NULL ) + return EFAULT; + + if (len == 0 || len > IDMAP_MAXNAMELEN) { + IDMAP_DEBUG("idmap_name_to_uid: bad len\n"); + return EINVAL; + } + + /* XXX hack */ + MALLOC(namestr, char *, len + 1, M_TEMP, M_WAITOK); + bcopy(name, namestr, len); + namestr[len] = '\0'; + + + if ((e = idmap_name_lookup(IDMAP_TYPE_UID, namestr)) == NULL) { + if ((error = idmap_upcall_name(IDMAP_TYPE_UID, namestr, &e))) { + FREE(namestr, M_TEMP); + return error; + } + + if (e == NULL) { + IDMAP_DEBUG("no error from upcall, but no data returned\n"); + FREE(namestr, M_TEMP); + return EFAULT; + } + + if (idmap_add(e) != 0) { + IDMAP_DEBUG("idmap_add failed\n"); + FREE(e, M_IDMAP); + } + } + + *id = e->id_info.id_id.uid; + FREE(namestr, M_TEMP); + return 0; +} + +int +idmap_name_to_gid(char * name, size_t len, gid_t * id) +{ + struct idmap_entry * e; + int error = 0; + + char * namestr; + + if (name == NULL ) + return EFAULT; + + if (len == 0 || len > IDMAP_MAXNAMELEN) { + IDMAP_DEBUG("idmap_name_to_uid: bad len\n"); + return EINVAL; + } + + /* XXX hack */ + MALLOC(namestr, char *, len + 1, M_TEMP, M_WAITOK); + bcopy(name, namestr, len); + namestr[len] = '\0'; + + + if ((e = idmap_name_lookup(IDMAP_TYPE_GID, namestr)) == NULL) { + if ((error = idmap_upcall_name(IDMAP_TYPE_GID, namestr, &e)) != 0) { + IDMAP_DEBUG("error in upcall\n"); + FREE(namestr, M_TEMP); + return error; + } + + if (e == NULL) { + IDMAP_DEBUG("no error from upcall, but no data returned\n"); + FREE(namestr, M_TEMP); + return EFAULT; + } + + if (idmap_add(e) != 0) { + IDMAP_DEBUG("idmap_add failed\n"); + FREE(e, M_IDMAP); + } + } + + *id = e->id_info.id_id.gid; + FREE(namestr, M_TEMP); + return 0; +} |