diff -u linux/fs/nfs/dir.c.nfs-ngroups linux/fs/nfs/dir.c
--- linux/fs/nfs/dir.c.nfs-ngroups	Tue Dec  3 21:40:05 2002
+++ linux/fs/nfs/dir.c	Tue Dec  3 22:03:28 2002
@@ -505,6 +505,9 @@
 	if (NFS_STALE(inode))
 		goto out_bad;
 
+	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)
 		goto out_bad;
@@ -580,6 +583,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)
@@ -648,6 +654,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;
 
 	attr.ia_mode = mode;
 	attr.ia_valid = ATTR_MODE;
@@ -665,6 +674,7 @@
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	else
 		d_drop(dentry);
+ out:
 	return error;
 }
 
@@ -680,6 +690,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;
 
 	attr.ia_mode = mode;
 	attr.ia_valid = ATTR_MODE;
@@ -691,6 +704,7 @@
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	else
 		d_drop(dentry);
+ out:
 	return error;
 }
 
@@ -706,6 +720,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;
 
 	attr.ia_valid = ATTR_MODE;
 	attr.ia_mode = mode | S_IFDIR;
@@ -726,6 +743,7 @@
 		error = nfs_instantiate(dentry, &fhandle, &fattr);
 	else
 		d_drop(dentry);
+ out:
 	return error;
 }
 
@@ -735,9 +753,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);
+	}
 	if (!error)
 		dentry->d_inode->i_nlink = 0;
 
@@ -882,6 +903,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;
 
 	error = nfs_sillyrename(dir, dentry);
 	if (error && error != -EBUSY) {
@@ -890,6 +915,7 @@
 			nfs_renew_times(dentry);
 		}
 	}
+ out:
 	return error;
 }
 
@@ -905,6 +931,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;
@@ -951,6 +980,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.
@@ -961,6 +993,7 @@
 	nfs_zap_caches(dir);
 	NFS_CACHEINV(inode);
 	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
+ out:
 	return error;
 }
 
@@ -994,7 +1027,7 @@
 	struct inode *old_inode = old_dentry->d_inode;
 	struct inode *new_inode = new_dentry->d_inode;
 	struct dentry *dentry = NULL, *rehash = NULL;
-	int error = -EBUSY;
+	int error;
 
 	/*
 	 * To prevent any new references to the target during the rename,
@@ -1009,6 +1042,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,
 		 atomic_read(&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
@@ -1018,6 +1056,7 @@
 	 * silly-rename. If the silly-rename succeeds, the
 	 * copied dentry is hashed and becomes the new target.
 	 */
+	error = -EBUSY;
 	if (!new_inode)
 		goto go_ahead;
 	if (S_ISDIR(new_inode->i_mode))
@@ -1101,6 +1140,10 @@
 	    && error != -EACCES)
 		goto out;
 
+	error = rpc_register_group(inode->i_gid);
+	if (error)
+		goto out;
+
 	error = NFS_PROTO(inode)->access(inode, mask, 0);
 
 	if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv &&
diff -u linux/fs/nfs/inode.c.nfs-ngroups linux/fs/nfs/inode.c
--- linux/fs/nfs/inode.c.nfs-ngroups	Tue Dec  3 21:40:05 2002
+++ linux/fs/nfs/inode.c	Tue Dec  3 22:03:28 2002
@@ -760,6 +760,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;
+	}
 
 	filemap_fdatasync(inode->i_mapping);
 	error = nfs_wb_all(inode);
@@ -850,6 +855,11 @@
 {
 	struct rpc_auth *auth;
 	struct rpc_cred *cred;
+	int error;
+
+	error = rpc_register_group(inode->i_gid);
+	if (error)
+		goto out;
 
 	lock_kernel();
 	auth = NFS_CLIENT(inode)->cl_auth;
@@ -858,7 +868,8 @@
 	if (filp->f_mode & FMODE_WRITE)
 		nfs_set_mmcred(inode, cred);
 	unlock_kernel();
-	return 0;
+ out:
+	return error;
 }
 
 int nfs_release(struct inode *inode, struct file *filp)
@@ -903,6 +914,10 @@
 	}
 	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) {
 		dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) getattr failed, error=%d\n",
diff -u linux/kernel/fork.c.nfs-ngroups linux/kernel/fork.c
--- linux/kernel/fork.c.nfs-ngroups	Tue Dec  3 21:40:07 2002
+++ linux/kernel/fork.c	Tue Dec  3 22:03:28 2002
@@ -21,6 +21,7 @@
 #include <linux/completion.h>
 #include <linux/namespace.h>
 #include <linux/personality.h>
+#include <linux/sunrpc/gidcache.h>
 #include <linux/compiler.h>
 
 #include <asm/pgtable.h>
@@ -565,6 +566,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 also
@@ -686,8 +699,10 @@
 		goto bad_fork_cleanup_files;
 	if (copy_sighand(clone_flags, p))
 		goto bad_fork_cleanup_fs;
-	if (copy_mm(clone_flags, p))
+	if (copy_gidcache(p))
 		goto bad_fork_cleanup_sighand;
+	if (copy_mm(clone_flags, p))
+		goto bad_fork_cleanup_gidcache;
 	if (copy_namespace(clone_flags, p))
 		goto bad_fork_cleanup_mm;
 	retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
@@ -763,6 +778,8 @@
 	exit_namespace(p);
 bad_fork_cleanup_mm:
 	exit_mm(p);
+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	Tue Dec  3 21:40:07 2002
+++ linux/kernel/exit.c	Tue Dec  3 22:03:28 2002
@@ -16,6 +16,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_fs(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);
+}
+
 /*
  * We can use these to temporarily drop into
  * "lazy TLB" mode and back.
@@ -439,6 +453,7 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 	acct_process(code);
 #endif
+	__exit_gidcache(tsk);
 	__exit_mm(tsk);
 
 	lock_kernel();
diff -u linux/kernel/sys.c.nfs-ngroups linux/kernel/sys.c
--- linux/kernel/sys.c.nfs-ngroups	Tue Dec  3 21:39:42 2002
+++ linux/kernel/sys.c	Tue Dec  3 22:03:28 2002
@@ -972,6 +972,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/kernel/uid16.c.nfs-ngroups linux/kernel/uid16.c
--- linux/kernel/uid16.c.nfs-ngroups	Tue Jan 11 03:40:26 2000
+++ linux/kernel/uid16.c	Tue Dec  3 22:03:28 2002
@@ -139,6 +139,7 @@
 	for (i = 0 ; i < gidsetsize ; i++)
 		current->groups[i] = (gid_t)groups[i];
 	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	Tue Dec  3 21:40:07 2002
+++ linux/include/linux/sunrpc/clnt.h	Tue Dec  3 22:19:54 2002
@@ -127,6 +127,15 @@
 void		rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset);
 void		rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset);
 void		rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
+int		__rpc_register_group(gid_t gid);
+
+static __inline__
+int rpc_register_group(gid_t gid)
+{
+	if (current->ngroups <= RPC_NGROUPS)
+		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	Tue Dec  3 22:03:28 2002
@@ -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	Tue Dec  3 22:03:28 2002
+++ linux/include/linux/sunrpc/gidcache.h	Tue Dec  3 22:03:28 2002
@@ -0,0 +1,48 @@
+/*
+ * include/linux/sunrpc/gidcache.h
+ *
+ * Copyright (C) 2000-2004, Frank van Maarseveen <frankvm@frankvm.com>
+ */
+
+#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	Tue Dec  3 21:40:07 2002
+++ linux/include/linux/sched.h	Tue Dec  3 22:03:29 2002
@@ -378,6 +378,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;
@@ -792,6 +793,7 @@
 extern void exit_mm(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 reparent_to_init(void);
 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	Mon Sep 24 12:18:04 2001
+++ linux/net/sunrpc/auth_unix.c	Tue Dec  3 22:03:29 2002
@@ -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
@@ -80,18 +80,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;
 	}
 	cred->uc_base.cr_ops = &unix_credops;
 
@@ -138,7 +148,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
@@ -146,11 +157,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;
 	}
@@ -192,7 +211,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	Tue Dec  3 21:40:10 2002
+++ linux/net/sunrpc/clnt.c	Tue Dec  3 22:03:29 2002
@@ -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 <frankvm@frankvm.com>
  */
 
 #include <asm/system.h>
@@ -30,6 +35,7 @@
 #include <linux/utsname.h>
 
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/gidcache.h>
 
 #include <linux/nfs.h>
 
@@ -364,6 +370,91 @@
 }
 
 /*
+ * 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;
+	}
+	if (gid == current->fsgid)
+		return 0;
+
+	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;
+}
+
+/*
  * 1.	Reserve an RPC call slot
  */
 static void
diff -u linux/net/sunrpc/sunrpc_syms.c.nfs-ngroups linux/net/sunrpc/sunrpc_syms.c
--- linux/net/sunrpc/sunrpc_syms.c.nfs-ngroups	Tue Dec  3 21:40:10 2002
+++ linux/net/sunrpc/sunrpc_syms.c	Tue Dec  3 22:27:06 2002
@@ -51,6 +51,7 @@
 EXPORT_SYMBOL(rpc_delay);
 EXPORT_SYMBOL(rpc_restart_call);
 EXPORT_SYMBOL(rpc_setbufsize);
+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	Sat Apr 29 07:50:39 2000
+++ linux/net/sunrpc/svcauth.c	Tue Dec  3 22:03:29 2002
@@ -138,7 +138,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++);
