
IDmap support for the NFSv4 server.


 fs/nfsd/Makefile                   |    2 
 fs/nfsd/nfs4idmap.c                |  490 +++++++++++++++++++++++++++++++++++++
 fs/nfsd/nfsctl.c                   |    7 
 include/linux/nfsd_idmap.h         |   46 +++
 include/linux/sunrpc/name_lookup.h |   54 +++-
 5 files changed, 587 insertions(+), 12 deletions(-)

diff -puN fs/nfsd/Makefile~idmap_server fs/nfsd/Makefile
--- linux-2.5.68/fs/nfsd/Makefile~idmap_server	2003-05-18 02:47:51.000000000 -0400
+++ linux-2.5.68-marius/fs/nfsd/Makefile	2003-05-18 02:47:51.000000000 -0400
@@ -7,5 +7,5 @@ obj-$(CONFIG_NFSD)	+= nfsd.o
 nfsd-y 			:= nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
 			   export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o
 nfsd-$(CONFIG_NFSD_V3)	+= nfs3proc.o nfs3xdr.o
-nfsd-$(CONFIG_NFSD_V4)	+= nfs4proc.o nfs4xdr.o nfs4state.o
+nfsd-$(CONFIG_NFSD_V4)	+= nfs4proc.o nfs4xdr.o nfs4state.o nfs4idmap.o
 nfsd-objs		:= $(nfsd-y)
diff -puN /dev/null fs/nfsd/nfs4idmap.c
--- /dev/null	2002-12-13 17:52:05.000000000 -0500
+++ linux-2.5.68-marius/fs/nfsd/nfs4idmap.c	2003-05-18 02:47:52.000000000 -0400
@@ -0,0 +1,490 @@
+/*
+ *  fs/nfsd/nfs4idmap.c
+ *
+ *  Mapping of UID/GIDs to name and vice versa.
+ *
+ *  Copyright (c) 2002, 2003 The Regents of the University of
+ *  Michigan.  All rights reserved.
+ *
+ *  Marius Aamodt Eriksen <marius@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs4.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/cache.h>
+#include <linux/idmap.h>
+#include <linux/nfsd_idmap.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+
+/*
+ * Cache entry
+ */
+
+#define ENT_INVALID 0x1
+struct ent {
+	struct cache_head h;
+	int               type;		       /* User / Group */
+	uid_t             id;
+	char              name[IDMAP_NAMESZ];
+	long              flags;
+};
+
+#define DefineSimpleCacheLookupMap(STRUCT, FUNC)			\
+        DefineCacheLookup(struct STRUCT, h, FUNC##_lookup,		\
+        (struct STRUCT *item, int set), /*no setup */,			\
+	& FUNC##_cache, FUNC##_hash(item), FUNC##_match(item, tmp),	\
+	STRUCT##_init(new, item), STRUCT##_update(tmp, item))
+
+#define IDMAP_WAITANSWER 0x1
+struct idmap_defer_req {
+	struct cache_req          req;
+	struct cache_deferred_req deferred_req;
+	wait_queue_head_t         waitq;
+	unsigned long             flags;
+};
+
+static size_t strlcpy(char *, const char *, size_t);
+static int    validate_ascii(char *, uint32_t);
+
+/* Deferral helpers */
+static struct cache_deferred_req *idmap_defer(struct cache_req *);
+static void                       idmap_revisit(struct cache_deferred_req *,
+                                      int);
+/* Common entry handling */
+
+#define ENT_HASHBITS          8
+#define ENT_HASHMAX           (1 << ENT_HASHBITS)
+#define ENT_HASHMASK          (ENT_HASHMAX - 1)
+
+static inline void
+ent_init(struct ent *new, struct ent *itm)
+{
+	new->id = itm->id;
+	new->type = itm->type;
+	new->flags = itm->flags;
+
+	strlcpy(new->name, itm->name, sizeof(new->name));
+}
+
+static inline void
+ent_update(struct ent *new, struct ent *itm)
+{
+	ent_init(new, itm);
+}
+
+void
+ent_put(struct cache_head *ch, struct cache_detail *cd)
+{
+	if (cache_put(ch, cd)) {
+		struct ent *map = container_of(ch, struct ent, h);
+		kfree(map);
+	}
+}
+
+/*
+ * ID -> Name cache
+ */
+
+static struct cache_head *idtoname_table[ENT_HASHMAX];
+
+static uint32_t
+idtoname_hash(struct ent *ent)
+{
+	uint32_t hash;
+
+	hash = ent->id;
+
+	/* Flip LSB for user/group */
+	if (ent->type == IDMAP_TYPE_GROUP)
+		hash |= 1 << (ENT_HASHBITS - 1);
+	else
+		hash &= ~(1 << (ENT_HASHBITS - 1));
+
+	return (hash & ENT_HASHMASK);
+}
+
+static void
+idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
+    int *blen)
+{
+ 	struct ent *ent = container_of(ch, struct ent, h);
+	struct idmap_msg *msg = (struct idmap_msg *)*bpp;
+
+	if (*blen < sizeof(*msg))
+		return;
+
+	memset(msg, 0, sizeof(*msg));
+
+	msg->im_conv = IDMAP_TRANS_IDTONAME;
+	msg->im_type = ent->type;
+	msg->im_id = ent->id;
+
+	*bpp += sizeof(*msg);
+	*blen -= sizeof(*msg);
+}
+
+static inline int
+idtoname_match(struct ent *a, struct ent *b)
+{
+	return (a->id == b->id && a->type == b->type);
+}
+
+static int         idtoname_parse(struct cache_detail *, char *, int);
+static struct ent *idtoname_lookup(struct ent *, int);
+
+struct cache_detail idtoname_cache = {
+	.hash_size     = ENT_HASHMAX,
+	.hash_table    = idtoname_table,
+	.name          = "nfs4.idtoname",
+	.cache_put     = ent_put,
+	.cache_request = idtoname_request,
+	.cache_parse   = idtoname_parse,
+};
+
+int
+idtoname_parse(struct cache_detail *cd, char *buf, int buflen)
+{
+	struct ent ent, *res;
+	struct idmap_msg *msg = (struct idmap_msg *)buf;
+
+	if (buflen != sizeof(*msg) ||
+	    validate_ascii(msg->im_name, IDMAP_NAMESZ) < 0 || msg->im_type > 1)
+		return (-EINVAL);
+
+	memset(&ent, 0, sizeof(ent));
+
+	if (msg->im_status & IDMAP_STATUS_FAIL)
+		set_bit(ENT_INVALID, &ent.flags);
+
+	ent.id = msg->im_id;
+	memcpy(ent.name, msg->im_name, sizeof(ent.name));
+
+	ent.type = msg->im_type;
+
+	ent.h.flags = 0;
+	ent.h.expiry_time = CURRENT_TIME.tv_sec + 3600;
+
+	if ((res = idtoname_lookup(&ent, 2)) == NULL)
+		return (-ENOMEM);
+
+	ent_put(&res->h, &idtoname_cache);
+
+	return (0);
+}
+
+static DefineSimpleCacheLookupMap(ent, idtoname)
+
+/*
+ * Name -> ID cache
+ */
+
+static struct cache_head *nametoid_table[ENT_HASHMAX];
+
+/*
+ * Fowler/Noll/Vo hash
+ *    http://www.isthe.com/chongo/tech/comp/fnv/
+ *
+ * XXX - adopt this to a true ENT_HASHBITS-bit FNV
+ */
+
+#define FNV_P_32 ((u_int32_t)0x01000193) /* 16777619 */
+#define FNV_1_32 ((u_int32_t)0x811c9dc5) /* 2166136261 */
+
+static inline int
+nametoid_hash(struct ent *ent)
+{
+	u_char *p, *end;
+	uint32_t hash = FNV_1_32;
+
+	end = ent->name + strlen(ent->name);
+
+	for (p = ent->name; p < end; p++) {
+		hash *= FNV_P_32;
+		hash ^= (u_int32_t)*p;
+	}
+
+	return (hash & ENT_HASHMASK);
+}
+
+void
+nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
+    int *blen)
+{
+ 	struct ent *ent = container_of(ch, struct ent, h);
+	struct idmap_msg *msg = (struct idmap_msg *)*bpp;
+
+	if (*blen < sizeof(*msg))
+		return;
+
+	memset(msg, 0, sizeof(*msg));
+
+	msg->im_conv = IDMAP_TRANS_NAMETOID;
+	msg->im_type = ent->type;
+	strlcpy(msg->im_name, ent->name, sizeof(msg->im_name));
+
+	*bpp += sizeof(*msg);
+	*blen -= sizeof(*msg);
+}
+
+static inline int
+nametoid_match(struct ent *a, struct ent *b)
+{
+	return (a->type == b->type && strcmp(a->name, b->name) == 0);
+}
+
+static struct ent *nametoid_lookup(struct ent *, int);
+int                nametoid_parse(struct cache_detail *, char *, int);
+
+struct cache_detail nametoid_cache = {
+	.hash_size     = ENT_HASHMAX,
+	.hash_table    = nametoid_table,
+	.name          = "nfs4.nametoid",
+	.cache_put     = ent_put,
+	.cache_request = nametoid_request,
+	.cache_parse   = nametoid_parse,
+};
+
+int
+nametoid_parse(struct cache_detail *cd, char *buf, int buflen)
+{
+	struct ent ent, *res;
+	struct idmap_msg *msg = (struct idmap_msg *)buf;
+
+	if (buflen != sizeof(*msg) ||
+	    validate_ascii(msg->im_name, IDMAP_NAMESZ) < 0 || msg->im_type > 1)
+		return (-EINVAL);
+
+	memset(&ent, 0, sizeof(ent));
+
+	if (msg->im_status & IDMAP_STATUS_FAIL)
+		set_bit(ENT_INVALID, &ent.flags);
+
+	ent.id = msg->im_id;
+	strlcpy(ent.name, msg->im_name, sizeof(ent.name));
+	ent.type = msg->im_type;
+
+	ent.h.flags = 0;
+	ent.h.expiry_time = CURRENT_TIME.tv_sec + 3600;
+
+	if ((res = nametoid_lookup(&ent, 2)) == NULL)
+		return (-ENOMEM);
+
+	ent_put(&res->h, &nametoid_cache);
+
+	return (0);
+}
+
+static DefineSimpleCacheLookupMap(ent, nametoid)
+
+/*
+ * Deferred request handling
+ */
+
+static struct cache_deferred_req *
+idmap_defer(struct cache_req *req)
+{
+	struct idmap_defer_req *mdr =
+	    container_of(req, struct idmap_defer_req, req);
+
+	mdr->deferred_req.revisit = idmap_revisit;
+
+	return (&mdr->deferred_req);
+}
+
+static void
+idmap_revisit(struct cache_deferred_req *dreq, int toomany)
+{
+	struct idmap_defer_req *mdr =
+	    container_of(dreq, struct idmap_defer_req, deferred_req);
+
+	if (test_bit(IDMAP_WAITANSWER, &mdr->flags)) {
+		if (!toomany)
+			clear_bit(IDMAP_WAITANSWER, &mdr->flags);
+
+		wake_up(&mdr->waitq);
+	}
+}
+
+/*
+ * Exported API
+ */
+
+void
+nfsd_idmap_init(void)
+{
+	cache_register(&idtoname_cache);
+	cache_register(&nametoid_cache);
+}
+
+void
+nfsd_idmap_shutdown(void)
+{
+	cache_unregister(&idtoname_cache);
+	cache_unregister(&nametoid_cache);
+}
+
+int
+nfsd_idmap(short trans, short type, uid_t *id, char *name)
+{
+	struct ent key, *res;
+	struct idmap_defer_req mdr;
+	DECLARE_WAITQUEUE(waitq, current);
+	struct ent *(*lookup_fn)(struct ent *, int);
+	struct cache_detail *lookup_cache;
+	int valid;
+
+	if (name == NULL || id == NULL)
+		return (-1);
+
+	memset(&key, 0, sizeof(key));
+
+	switch (trans) {
+	case IDMAP_TRANS_IDTONAME:
+		lookup_fn = idtoname_lookup;
+		lookup_cache = &idtoname_cache;
+		key.id = *id;
+		break;
+	case IDMAP_TRANS_NAMETOID:
+		lookup_fn = nametoid_lookup;
+		lookup_cache = &nametoid_cache;
+		strlcpy(key.name, name, sizeof(key.name));
+		break;
+	default:
+		return (-1);
+	}
+
+	key.type = type;
+
+	memset(&mdr, 0, sizeof(mdr));
+	init_waitqueue_head(&mdr.waitq);
+	mdr.req.defer = idmap_defer;
+
+ lookup:
+	if ((res = (*lookup_fn)(&key, 0)) != NULL) {
+		add_wait_queue(&mdr.waitq, &waitq);
+	again:
+		set_task_state(current, TASK_UNINTERRUPTIBLE);
+		set_bit(IDMAP_WAITANSWER, &mdr.flags);
+ 		if (cache_check(lookup_cache, &res->h, &mdr.req) != 0) {
+			schedule();
+
+			/*
+			 * If we haven't gotten the answer yet; we
+			 * were woken up due to an overpopulated
+			 * cache.  Try again!
+			 */
+
+			if (test_bit(IDMAP_WAITANSWER, &mdr.flags))
+				goto again;
+
+			goto lookup;
+		}
+
+		*id = res->id;
+		strlcpy(name, res->name, IDMAP_NAMESZ);
+		valid = !test_bit(ENT_INVALID, &res->flags);
+		ent_put(&res->h, lookup_cache);
+
+		set_task_state(current, TASK_RUNNING);
+
+		return (valid ? 0 : -1);
+	}
+
+	return (-1);
+}
+
+/* Utility */
+
+/* strlcpy() from OpenBSD. */
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters will
+ * be copied.  Always NUL terminates (unless siz == 0).  Returns
+ * strlen(src); if retval >= siz, truncation occurred.
+ */
+static size_t
+strlcpy(dst, src, siz)
+	char *dst;
+	const char *src;
+	size_t siz;
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';              /* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);    /* count does not include NUL */
+}
+
+static int
+validate_ascii(char *string, uint32_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (string[i] == '\0')
+			break;
+
+		if (string[i] & 0x80)
+			return (-1);
+	}
+
+	if (string[i] != '\0')
+		return (-i);
+
+	return (i);
+}
diff -puN fs/nfsd/nfsctl.c~idmap_server fs/nfsd/nfsctl.c
--- linux-2.5.68/fs/nfsd/nfsctl.c~idmap_server	2003-05-18 02:47:51.000000000 -0400
+++ linux-2.5.68-marius/fs/nfsd/nfsctl.c	2003-05-18 02:47:52.000000000 -0400
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 
 #include <linux/nfs.h>
+#include <linux/nfsd_idmap.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/cache.h>
@@ -513,6 +514,9 @@ static int __init init_nfsd(void)
 	nfsd_export_init();	/* Exports table */
 	nfsd_lockd_init();	/* lockd->nfsd callbacks */
 	nfs4_state_init();      /* NFSv4 State */
+#ifdef CONFIG_NFSD_V4
+	nfsd_idmap_init();      /* Name to ID mapping */
+#endif /* CONFIG_NFSD_V4 */	
 	if (proc_mkdir("fs/nfs", 0)) {
 		struct proc_dir_entry *entry;
 		entry = create_proc_entry("fs/nfs/exports", 0, NULL);
@@ -531,6 +535,9 @@ static void __exit exit_nfsd(void)
 	remove_proc_entry("fs/nfs", NULL);
 	nfsd_stat_shutdown();
 	nfsd_lockd_shutdown();
+#ifdef CONFIG_NFSD_V4
+	nfsd_idmap_shutdown();
+#endif /* CONFIG_NFSD_V4 */
 	nfs4_state_shutdown();
 	unregister_filesystem(&nfsd_fs_type);
 }
diff -puN /dev/null include/linux/nfsd_idmap.h
--- /dev/null	2002-12-13 17:52:05.000000000 -0500
+++ linux-2.5.68-marius/include/linux/nfsd_idmap.h	2003-05-18 02:47:52.000000000 -0400
@@ -0,0 +1,46 @@
+/*
+ *  include/linux/nfsd_idmap.h
+ *
+ *  Mapping of UID to name and vice versa.
+ *
+ *  Copyright (c) 2002, 2003 The Regents of the University of
+ *  Michigan.  All rights reserved.
+ *
+ *  Marius Aamodt Eriksen <marius@umich.edu>
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. Neither the name of the University nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef LINUX_NFSD_IDMAP_H
+#define LINUX_NFSD_IDMAP_H
+
+#include <linux/idmap.h>
+
+void nfsd_idmap_init(void);
+void nfsd_idmap_shutdown(void);
+int  nfsd_idmap(short, short, uid_t *, char *);
+
+#endif /* LINUX_NFSD_IDMAP_H */
diff -puN include/linux/sunrpc/name_lookup.h~idmap_server include/linux/sunrpc/name_lookup.h
--- linux-2.5.68/include/linux/sunrpc/name_lookup.h~idmap_server	2003-05-18 02:47:51.000000000 -0400
+++ linux-2.5.68-marius/include/linux/sunrpc/name_lookup.h	2003-05-18 02:47:52.000000000 -0400
@@ -3,33 +3,65 @@
  * map between user/group name and id for a given 'client' 
  */
 
+#include <linux/idmap.h>
+#include <linux/nfsd_idmap.h>
+
 struct name_ent {
-	char name[20];
+	char name[IDMAP_NAMESZ];
 };
 static inline int name_get_user(int uid, struct name_ent **namep)
 {
-	struct name_ent *n = kmalloc(sizeof(*n),GFP_KERNEL);
-	if (n) sprintf(n->name, "%d",uid);
+	struct name_ent *n = kmalloc(sizeof(*n), GFP_KERNEL);
+
+	if (n == NULL)
+		return (-ENOMEM);
+
 	*namep = n;
-	return n ? 0 : -ENOMEM;
+	return (nfsd_idmap(IDMAP_TRANS_IDTONAME, IDMAP_TYPE_USER, &uid, n->name));
 }
 static inline int name_get_group(int uid, struct name_ent **namep)
 {
-	struct name_ent *n = kmalloc(sizeof(*n),GFP_KERNEL);
-	if (n) sprintf(n->name, "%d",uid);
+	struct name_ent *n = kmalloc(sizeof(*n), GFP_KERNEL);
+
+	if (n == NULL)
+		return (-ENOMEM);
+
 	*namep = n;
-	return n ? 0 : -ENOMEM;
+	return (nfsd_idmap(IDMAP_TRANS_IDTONAME, IDMAP_TYPE_GROUP, &uid, n->name));
+
 }
 static inline int name_get_uid(char *name, int name_len, int *uidp)
 {
-	*uidp = simple_strtoul(name, NULL, 0);
-	return 0;
+	char *xname = kmalloc(name_len + 1, GFP_KERNEL);
+	int ret;
+
+	if (xname == NULL)
+		return (-ENOMEM);
+
+	memcpy(xname, name, name_len);
+	xname[name_len] = '\0';
+
+	ret = nfsd_idmap(IDMAP_TRANS_NAMETOID, IDMAP_TYPE_USER, uidp, xname);
+
+	kfree(xname);
+	return (ret);
 }
 
 static inline int name_get_gid(char *name, int name_len, int *gidp)
 {
-	*gidp = simple_strtoul(name, NULL, 0);
-	return 0;
+	char *xname = kmalloc(name_len + 1, GFP_KERNEL);
+	int ret;
+
+	if (xname == NULL)
+		return (-ENOMEM);
+
+	memcpy(xname, name, name_len);
+	xname[name_len] = '\0';
+
+	ret = nfsd_idmap(IDMAP_TRANS_NAMETOID, IDMAP_TYPE_GROUP, gidp, xname);
+
+	kfree(xname);
+	return (ret);
 }
 
 static inline void name_put(struct name_ent *ent) 

_
