diff -u linux/fs/nfs/dir.c.nfs-ngroups linux/fs/nfs/dir.c
--- linux/fs/nfs/dir.c.nfs-ngroups	Wed May  2 20:48:37 2001
+++ linux/fs/nfs/dir.c	Wed May  2 21:26:52 2001
@@ -570,6 +570,9 @@
 	/*
 	 * Do a new lookup and check the dentry attributes.
 	 */
+	if (__rpc_register_group(dir->i_gid))
+		goto out_bad;		/* no way to pass -ENOMEM back */
+
 	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
 	if (error < 0)
 		goto out_bad;
@@ -643,6 +646,9 @@
 
 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
+	error = __rpc_register_group(dir->i_gid);
+	if (error)
+		goto out;
 
 	error = -ENAMETOOLONG;
 	if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
@@ -708,6 +714,9 @@
 
 	dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	error = __rpc_register_group(dir->i_gid);
+	if (error)
+		goto out;
 
 #ifdef NFSD_BROKEN_UID
 	/* We set uid/gid in the request because IBM's broken nfsd
@@ -738,6 +747,7 @@
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	if (error || fhandle.size == 0)
 		d_drop(dentry);
+ out:
 	return error;
 }
 
@@ -753,6 +763,9 @@
 
 	dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	error = __rpc_register_group(dir->i_gid);
+	if (error)
+		goto out;
 
 #ifdef NFSD_BROKEN_UID
 	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
@@ -772,6 +785,7 @@
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	if (error || fhandle.size == 0)
 		d_drop(dentry);
+ out:
 	return error;
 }
 
@@ -787,6 +801,9 @@
 
 	dfprintk(VFS, "NFS: mkdir(%x/%ld, %s)\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	error = __rpc_register_group(dir->i_gid);
+	if (error)
+		goto out;
 
 #ifdef NFSD_BROKEN_UID
 	attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
@@ -805,6 +822,7 @@
 
 	if (error || fhandle.size == 0)
 		d_drop(dentry);
+ out:
 	return error;
 }
 
@@ -814,9 +832,12 @@
 
 	dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	error = __rpc_register_group(dir->i_gid);
 
-	nfs_zap_caches(dir);
-	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
+	if (!error) {
+		nfs_zap_caches(dir);
+		error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
+	}
 
 	/* Free the inode */
 	if (!error)
@@ -1030,6 +1051,10 @@
 
 	dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name);
+	if (  (error = __rpc_register_group(dentry->d_inode->i_gid))
+	   || (error = __rpc_register_group(dir->i_gid))
+	   )
+		goto out;
 
 	if (dentry->d_inode)
 		nfs_wb_all(dentry->d_inode);
@@ -1040,6 +1065,7 @@
 			nfs_renew_times(dentry);
 		}
 	}
+ out:
 	return error;
 }
 
@@ -1054,6 +1080,9 @@
 
 	dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
 		dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+	error = __rpc_register_group(dir->i_gid);
+	if (error)
+		goto out;
 
 	error = -ENAMETOOLONG;
 	maxlen = (NFS_PROTO(dir)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
@@ -1110,6 +1139,9 @@
 	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
 		dentry->d_parent->d_name.name, dentry->d_name.name);
+	error = __rpc_register_group(dir->i_gid);
+	if (error)
+		goto out;
 
 	/*
 	 * Drop the dentry in advance to force a new lookup.
@@ -1120,6 +1152,7 @@
 	nfs_zap_caches(dir);
 	NFS_CACHEINV(inode);
 	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
+ out:
 	return error;
 }
 
@@ -1172,6 +1205,11 @@
 		old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
 		new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
 		new_dentry->d_count);
+	if (  (error = __rpc_register_group(old_inode->i_gid))
+	   || (error = __rpc_register_group(old_dir->i_gid))
+	   || (error = __rpc_register_group(new_dir->i_gid))
+	   )
+		goto out;
 
 	/*
 	 * First check whether the target is busy ... we can't
@@ -1265,6 +1303,10 @@
 	if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
 	    && (current->fsuid != 0) && (current->fsgid != 0)
 	    && error != -EACCES)
+		goto out;
+
+	error = __rpc_register_group(inode->i_gid);
+	if (error)
 		goto out;
 
 	error = NFS_PROTO(inode)->access(inode, mask, 0);
diff -u linux/fs/nfs/inode.c.nfs-ngroups linux/fs/nfs/inode.c
--- linux/fs/nfs/inode.c.nfs-ngroups	Wed May  2 20:48:37 2001
+++ linux/fs/nfs/inode.c	Wed May  2 21:49:38 2001
@@ -770,6 +770,11 @@
 
 	if (!S_ISREG(inode->i_mode))
 		attr->ia_valid &= ~ATTR_SIZE;
+	if (attr->ia_valid & ATTR_GID) {
+		error = __rpc_register_group(attr->ia_gid);
+		if (error)
+			goto out;
+	}
 
 	error = nfs_wb_all(inode);
 	if (error < 0)
@@ -840,11 +845,19 @@
 
 int nfs_open(struct inode *inode, struct file *filp)
 {
-	struct rpc_auth	*auth = NFS_CLIENT(inode)->cl_auth;
-	struct rpc_cred	*cred = rpcauth_lookupcred(auth, 0);
+	struct rpc_auth	*auth;
+	struct rpc_cred	*cred;
+	int error;
+
+	error = __rpc_register_group(inode->i_gid);
+	if (error)
+		goto out;
 
+	auth = NFS_CLIENT(inode)->cl_auth;
+	cred = rpcauth_lookupcred(auth, 0);
 	filp->private_data = cred;
-	return 0;
+ out:
+	return error;
 }
 
 int nfs_release(struct inode *inode, struct file *filp)
@@ -882,6 +895,10 @@
 			return NFS_STALE(inode) ? -ESTALE : 0;
 	}
 	NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
+
+	status = __rpc_register_group(inode->i_gid);
+	if (status)
+		goto out;
 
 	status = NFS_PROTO(inode)->getattr(inode, &fattr);
 	if (status) {
diff -u linux/kernel/fork.c.nfs-ngroups linux/kernel/fork.c
--- linux/kernel/fork.c.nfs-ngroups	Wed May  2 20:48:40 2001
+++ linux/kernel/fork.c	Wed May  2 21:08:40 2001
@@ -17,6 +17,7 @@
 #include <linux/smp_lock.h>
 #include <linux/module.h>
 #include <linux/vmalloc.h>
+#include <linux/sunrpc/gidcache.h>
 
 #include <asm/pgtable.h>
 #include <asm/mmu_context.h>
@@ -563,6 +564,18 @@
 	p->flags = new_flags;
 }
 
+static inline int copy_gidcache(struct task_struct *p)
+{
+	struct rpc_gidcache *gidcache = p->gidcache;
+
+	if (gidcache) {
+		p->gidcache = rpc_gidcache_dup(gidcache);
+		if (p->gidcache == NULL)
+			return -1;
+	}
+	return 0;
+}
+
 /*
  *  Ok, this is the main fork-routine. It copies the system process
  * information (task[nr]) and sets up the necessary registers. It
@@ -675,8 +688,10 @@
 		goto bad_fork_cleanup_files;
 	if (copy_sighand(clone_flags, p))
 		goto bad_fork_cleanup_fs;
-	if (copy_mm(nr, clone_flags, p))
+	if (copy_gidcache(p))
 		goto bad_fork_cleanup_sighand;
+	if (copy_mm(nr, clone_flags, p))
+		goto bad_fork_cleanup_gidcache;
 	retval = copy_thread(nr, clone_flags, usp, p, regs);
 	if (retval)
 		goto bad_fork_cleanup_mm;
@@ -732,6 +747,8 @@
 bad_fork_cleanup_mm:
 	mmput(p->mm);
 	p->mm = NULL;
+bad_fork_cleanup_gidcache:
+	exit_gidcache(p);
 bad_fork_cleanup_sighand:
 	exit_sighand(p);
 bad_fork_cleanup_fs:
diff -u linux/kernel/exit.c.nfs-ngroups linux/kernel/exit.c
--- linux/kernel/exit.c.nfs-ngroups	Wed May  2 20:48:40 2001
+++ linux/kernel/exit.c	Wed May  2 21:08:40 2001
@@ -13,6 +13,7 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 #include <linux/acct.h>
 #endif
+#include <linux/sunrpc/gidcache.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -272,6 +273,19 @@
 	__exit_mm(tsk);
 }
 
+static inline void __exit_gidcache(struct task_struct *tsk)
+{
+	if (tsk->gidcache) {
+		rpc_gidcache_del(tsk->gidcache);
+		tsk->gidcache = NULL;
+	}
+}
+
+void exit_gidcache(struct task_struct *tsk)
+{
+	__exit_gidcache(tsk);
+}
+
 /*
  * Send signals to all our closest relatives so that they know
  * to properly mourn us..
@@ -390,6 +404,7 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 	acct_process(code);
 #endif
+	__exit_gidcache(tsk);
 	sem_exit();
 	__exit_mm(tsk);
 #if CONFIG_AP1000
diff -u linux/kernel/sys.c.nfs-ngroups linux/kernel/sys.c
--- linux/kernel/sys.c.nfs-ngroups	Wed May  2 20:48:40 2001
+++ linux/kernel/sys.c	Wed May  2 21:08:40 2001
@@ -818,6 +818,7 @@
 	if(copy_from_user(current->groups, grouplist, gidsetsize * sizeof(gid_t)))
 		return -EFAULT;
 	current->ngroups = gidsetsize;
+	exit_gidcache(current);
 	return 0;
 }
 
diff -u linux/include/linux/sunrpc/clnt.h.nfs-ngroups linux/include/linux/sunrpc/clnt.h
--- linux/include/linux/sunrpc/clnt.h.nfs-ngroups	Wed May  2 20:48:39 2001
+++ linux/include/linux/sunrpc/clnt.h	Wed May  2 21:08:40 2001
@@ -123,6 +123,15 @@
 void		rpc_restart_call(struct rpc_task *);
 void		rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
 void		rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
+int		rpc_register_group(gid_t gid);
+
+static __inline__
+int __rpc_register_group(gid_t gid)
+{
+	if (current->ngroups <= RPC_NGROUPS || gid == current->fsgid)
+		return 0;
+	return rpc_register_group(gid);
+}
 
 static __inline__
 int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
diff -u linux/include/linux/sunrpc/msg_prot.h.nfs-ngroups linux/include/linux/sunrpc/msg_prot.h
--- linux/include/linux/sunrpc/msg_prot.h.nfs-ngroups	Mon Apr  7 20:35:32 1997
+++ linux/include/linux/sunrpc/msg_prot.h	Wed May  2 21:08:40 2001
@@ -57,6 +57,7 @@
 #define RPC_PMAP_PORT		111
 
 #define RPC_MAXNETNAMELEN	256
+#define RPC_NGROUPS		16
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_MSGPROT_H_ */
diff -u linux/include/linux/sunrpc/gidcache.h.nfs-ngroups linux/include/linux/sunrpc/gidcache.h
--- linux/include/linux/sunrpc/gidcache.h.nfs-ngroups	Wed May  2 20:48:42 2001
+++ linux/include/linux/sunrpc/gidcache.h	Wed May  2 21:08:40 2001
@@ -0,0 +1,48 @@
+/*
+ * include/linux/sunrpc/gidcache.h
+ *
+ * Copyright (C) 2000-2002, Frank van Maarseveen <F.vanMaarseveen@inter.NL.net>
+ */
+
+#ifndef _LINUX_SUNRPC_RPCGIDCACHE_H_
+#define _LINUX_SUNRPC_RPCGIDCACHE_H_
+
+#ifdef __KERNEL__
+
+#include <linux/sunrpc/msg_prot.h>
+
+struct rpc_gidcache {
+	int		ngroups;
+	gid_t		groups[RPC_NGROUPS];
+	unsigned short	lru[RPC_NGROUPS];
+};
+
+static inline struct rpc_gidcache *rpc_gidcache_new(void)
+{
+	struct rpc_gidcache	*gidcache;
+
+	gidcache = kmalloc(sizeof(struct rpc_gidcache), GFP_KERNEL);
+	if (gidcache) {
+		gidcache->ngroups = 0;
+	}
+	return gidcache;
+}
+
+static inline void rpc_gidcache_del(struct rpc_gidcache *gidcache)
+{
+	kfree(gidcache);
+}
+
+static inline struct rpc_gidcache *rpc_gidcache_dup(struct rpc_gidcache *p)
+{
+	struct rpc_gidcache	*gidcache;
+
+	gidcache = kmalloc(sizeof(struct rpc_gidcache), GFP_KERNEL);
+	if (gidcache) {
+		memcpy(gidcache, p, sizeof(*gidcache));
+	}
+	return gidcache;
+}
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SUNRPC_RPCGIDCACHE_H_ */
diff -u linux/include/linux/sched.h.nfs-ngroups linux/include/linux/sched.h
--- linux/include/linux/sched.h.nfs-ngroups	Wed May  2 20:48:39 2001
+++ linux/include/linux/sched.h	Wed May  2 21:08:40 2001
@@ -316,6 +316,7 @@
 	kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
 	int keep_capabilities:1;
 	struct user_struct *user;
+	struct rpc_gidcache *gidcache;
 /* limits */
 	struct rlimit rlim[RLIM_NLIMITS];
 	unsigned short used_math;
@@ -411,6 +412,7 @@
 /* caps */	CAP_INIT_EFF_SET,CAP_INIT_INH_SET,CAP_FULL_SET, \
 /* keep_caps */	0,						\
 /* user */	NULL,						\
+/* gidcache */	NULL,						\
 /* rlimits */   INIT_RLIMITS, \
 /* math */	0, \
 /* comm */	"swapper", \
@@ -725,6 +727,7 @@
 extern void exit_fs(struct task_struct *);
 extern void exit_files(struct task_struct *);
 extern void exit_sighand(struct task_struct *);
+extern void exit_gidcache(struct task_struct *);
 
 extern void daemonize(void);
 
diff -u linux/net/sunrpc/auth_unix.c.nfs-ngroups linux/net/sunrpc/auth_unix.c
--- linux/net/sunrpc/auth_unix.c.nfs-ngroups	Wed May  2 20:48:19 2001
+++ linux/net/sunrpc/auth_unix.c	Wed May  2 21:08:40 2001
@@ -12,13 +12,13 @@
 #include <linux/in.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/gidcache.h>
 
-#define NFS_NGROUPS	16
 struct unx_cred {
 	struct rpc_cred		uc_base;
 	uid_t			uc_fsuid;
 	gid_t			uc_gid, uc_fsgid;
-	gid_t			uc_gids[NFS_NGROUPS];
+	gid_t			uc_gids[RPC_NGROUPS];
 };
 #define uc_uid			uc_base.cr_uid
 #define uc_count		uc_base.cr_count
@@ -27,7 +27,7 @@
 
 #define UNX_CRED_EXPIRE		(60 * HZ)
 
-#define UNX_WRITESLACK		(9 + NFS_NGROUPS + (UNX_MAXNODENAME >> 2))
+#define UNX_WRITESLACK		(9 + RPC_NGROUPS + (UNX_MAXNODENAME >> 2))
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_AUTH
@@ -78,18 +78,28 @@
 		cred->uc_gid = cred->uc_fsgid = 0;
 		cred->uc_gids[0] = NOGROUP;
 	} else {
-		int groups = current->ngroups;
-		if (groups > NFS_NGROUPS)
-			groups = NFS_NGROUPS;
+		int	ngroups;
+		gid_t	*groups;
 
+		if (current->gidcache) {
+			ngroups = current->gidcache->ngroups;
+			groups = current->gidcache->groups;
+		} else {
+			ngroups = current->ngroups;
+			groups = current->groups;
+		}
+		if (ngroups > RPC_NGROUPS) {
+			ngroups = RPC_NGROUPS;
+			printk(KERN_WARNING "unx_create_cred: too many groups.\n");
+		}
 		cred->uc_uid = current->uid;
 		cred->uc_gid = current->gid;
 		cred->uc_fsuid = current->fsuid;
 		cred->uc_fsgid = current->fsgid;
-		for (i = 0; i < groups; i++)
-			cred->uc_gids[i] = (gid_t) current->groups[i];
-		if (i < NFS_NGROUPS)
-		  cred->uc_gids[i] = NOGROUP;
+		for (i = 0; i < ngroups; i++)
+			cred->uc_gids[i] = groups[i];
+		if (i < RPC_NGROUPS)
+			cred->uc_gids[i] = NOGROUP;
 	}
 
 	return (struct rpc_cred *) cred;
@@ -135,7 +145,8 @@
 	int		i;
 
 	if (!(taskflags & RPC_TASK_ROOTCREDS)) {
-		int groups;
+		int	ngroups;
+		gid_t	*groups;
 
 		if (cred->uc_uid != current->uid
 		 || cred->uc_gid != current->gid
@@ -143,11 +154,19 @@
 		 || cred->uc_fsgid != current->fsgid)
 			return 0;
 
-		groups = current->ngroups;
-		if (groups > NFS_NGROUPS)
-			groups = NFS_NGROUPS;
-		for (i = 0; i < groups ; i++)
-			if (cred->uc_gids[i] != (gid_t) current->groups[i])
+		if (current->gidcache) {
+			ngroups = current->gidcache->ngroups;
+			groups = current->gidcache->groups;
+		} else {
+			ngroups = current->ngroups;
+			groups = current->groups;
+		}
+		if (ngroups > RPC_NGROUPS) {
+			ngroups = RPC_NGROUPS;
+			printk(KERN_WARNING "unx_match: too many groups.\n");
+		}
+		for (i = 0; i < ngroups ; i++)
+			if (cred->uc_gids[i] != groups[i])
 				return 0;
 		return 1;
 	}
@@ -189,7 +208,7 @@
 		*p++ = htonl((u32) cred->uc_fsgid);
 	}
 	hold = p++;
-	for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
+	for (i = 0; i < RPC_NGROUPS && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
 		*p++ = htonl((u32) cred->uc_gids[i]);
 	*hold = htonl(p - hold - 1);		/* gid array length */
 	*base = htonl((p - base - 1) << 2);	/* cred length */
diff -u linux/net/sunrpc/clnt.c.nfs-ngroups linux/net/sunrpc/clnt.c
--- linux/net/sunrpc/clnt.c.nfs-ngroups	Wed May  2 20:48:41 2001
+++ linux/net/sunrpc/clnt.c	Wed May  2 21:08:40 2001
@@ -19,6 +19,11 @@
  *
  *  Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
  *  Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
+ *
+ *  CHANGES
+ *  2000-07-26	Added rpc_register_group(), allowing a way to get around the
+ *		RPC limit of 16 groups on NFS mounted filesystems (AUTH_UNIX).
+ *		-- Frank van Maarseveen <F.vanMaarseveen@inter.NL.net>
  */
 
 #include <asm/system.h>
@@ -32,6 +37,7 @@
 #include <linux/interrupt.h>
 
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/gidcache.h>
 
 
 #define RPC_SLACK_SPACE		512	/* total overkill */
@@ -349,6 +355,89 @@
 
 	task->tk_action = call_reserve;
 	rpcproc_count(task->tk_client, task->tk_msg.rpc_proc)++;
+}
+
+/*
+ * Update the per-process group id cache.
+ *
+ * In rpc_gidcache, groups[n] corresponds with lru[n]. All lru[]
+ * numbers are unique and identify the order in which the groups have been
+ * registered by this function, numbering from 1 to ngroups. The oldest
+ * registered group has value 1. The groups array is kept sorted to avoid
+ * blowing up the RPC credential cache.
+ * The memmoves below could be replaced by something fancier but it would only
+ * make a difference in really awkward situations. RPC_NGROUPS is 16 at most.
+ * 
+ * This function is called via __rpc_register_group(), but only when the
+ * process is a member of too many groups for RPC.
+ */
+int
+rpc_register_group(gid_t gid)
+{
+	int			ngroups, i, j, n;
+	gid_t			*groups;
+	unsigned short		*lru;
+	struct rpc_gidcache	*gidcache = current->gidcache;
+
+	if (gidcache == NULL) {
+		gidcache = rpc_gidcache_new();
+		if (gidcache == NULL)
+			return -ENOMEM;
+		current->gidcache = gidcache;
+	}
+
+	ngroups = gidcache->ngroups;
+	groups = gidcache->groups;
+	lru = gidcache->lru;
+	for (i = 0; i < ngroups; ++i) {
+		if (groups[i] == gid)
+			break;
+	}
+	if (i < ngroups) {
+		unsigned short order = lru[i];
+		if (order != (unsigned short)ngroups) {
+			for (j = 0; j < ngroups; ++j) {
+				if (lru[j] > order)
+					--lru[j];
+			}
+			lru[i] = (unsigned short)ngroups;
+		}
+		return 0;	/* ok: group already cached, lru[] updated */
+	}
+	if (!in_group_p(gid))
+		return 0;	/* ignore: we're not a member */
+
+	/*
+	 * Delete the oldest group (lru[] == 1) when all groups are in use.
+	 */
+	if (ngroups == RPC_NGROUPS) {
+		j = 0;
+		for (i = 0; i < ngroups; ++i) {
+			if (!--lru[i])
+				j = i;
+		}
+		n = ngroups - (j + 1);
+		memmove(&groups[j], &groups[j + 1], n * sizeof(*groups));
+		memmove(&lru[j], &lru[j + 1], n * sizeof(*lru));
+		--ngroups;
+	}
+
+	/*
+	 * Insert the new group. First find the insertion point.
+	 */
+	for (i = 0; i < ngroups; ++i) {
+		if (groups[i] > gid) {
+			break;
+		}
+	}
+	n = ngroups - i;
+	memmove(&groups[i + 1], &groups[i], n * sizeof(*groups));
+	memmove(&lru[i + 1], &lru[i], n * sizeof(*lru));
+	++ngroups;
+	groups[i] = gid;
+	lru[i] = (unsigned short)ngroups;
+	gidcache->ngroups = ngroups;
+	return 0;
 }
 
 /*
diff -u linux/net/sunrpc/sunrpc_syms.c.nfs-ngroups linux/net/sunrpc/sunrpc_syms.c
--- linux/net/sunrpc/sunrpc_syms.c.nfs-ngroups	Wed May  2 20:48:19 2001
+++ linux/net/sunrpc/sunrpc_syms.c	Wed May  2 21:08:40 2001
@@ -52,6 +52,7 @@
 EXPORT_SYMBOL(rpc_clnt_sigunmask);
 EXPORT_SYMBOL(rpc_delay);
 EXPORT_SYMBOL(rpc_restart_call);
+EXPORT_SYMBOL(rpc_register_group);
 
 /* Client transport */
 EXPORT_SYMBOL(xprt_create_proto);
diff -u linux/net/sunrpc/svcauth.c.nfs-ngroups linux/net/sunrpc/svcauth.c
--- linux/net/sunrpc/svcauth.c.nfs-ngroups	Wed May  2 20:46:57 2001
+++ linux/net/sunrpc/svcauth.c	Wed May  2 21:08:40 2001
@@ -139,7 +139,7 @@
 	cred->cr_gid = ntohl(*bufp++);		/* gid */
 
 	slen = ntohl(*bufp++);			/* gids length */
-	if (slen > 16 || (len -= slen + 2) < 0)
+	if (slen > RPC_NGROUPS || (len -= slen + 2) < 0)
 		goto badcred;
 	for (i = 0; i < NGROUPS && i < slen; i++)
 		cred->cr_groups[i] = ntohl(*bufp++);
