--- ./fs/nfs/dir.c.orig	2005-02-06 18:05:03.000000000 +0100
+++ ./fs/nfs/dir.c	2005-02-14 21:38:40.000000000 +0100
@@ -1518,7 +1518,7 @@
 	if (!NFS_PROTO(inode)->access)
 		goto out_notsup;
 
-	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+	cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0);
 	res = nfs_do_access(inode, cred, mask);
 	put_rpccred(cred);
 	unlock_kernel();
--- ./fs/nfs/inode.c.orig	2005-02-14 21:36:53.000000000 +0100
+++ ./fs/nfs/inode.c	2005-02-14 21:38:41.000000000 +0100
@@ -920,7 +920,7 @@
 	struct nfs_open_context *ctx;
 	struct rpc_cred *cred;
 
-	if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL)
+	if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0)) == NULL)
 		return -ENOMEM;
 	ctx = alloc_nfs_open_context(filp->f_dentry, cred);
 	if (ctx == NULL)
@@ -1612,7 +1612,7 @@
 		}
 		clnt->cl_chatty   = 1;
 		clp->cl_rpcclient = clnt;
-		clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0);
+		clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0);
 		memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
 		nfs_idmap_new(clp);
 	}
--- ./fs/nfs/nfs4proc.c.orig	2005-02-14 21:36:53.000000000 +0100
+++ ./fs/nfs/nfs4proc.c	2005-02-14 21:38:41.000000000 +0100
@@ -773,7 +773,7 @@
 		BUG_ON(nd->intent.open.flags & O_CREAT);
 	}
 
-	cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
+	cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0);
 	state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred);
 	put_rpccred(cred);
 	if (IS_ERR(state))
@@ -788,7 +788,7 @@
 	struct nfs4_state *state;
 	struct inode *inode;
 
-	cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
+	cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0);
 	state = nfs4_open_delegated(dentry->d_inode, openflags, cred);
 	if (IS_ERR(state))
 		state = nfs4_do_open(dir, dentry, openflags, NULL, cred);
@@ -1008,7 +1008,7 @@
 	fattr->valid = 0;
 	
 	if (size_change) {
-		struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
+		struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0);
 		state = nfs4_find_state(inode, cred, FMODE_WRITE);
 		if (state == NULL) {
 			state = nfs4_open_delegated(dentry->d_inode,
@@ -1323,7 +1323,7 @@
 	struct nfs4_state *state = NULL;
 	struct rpc_cred *cred;
 
-	cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0);
+	cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0);
 	state = nfs4_do_open(dir, dentry, flags, sattr, cred);
 	put_rpccred(cred);
 	if (!IS_ERR(state)) {
@@ -2000,7 +2000,7 @@
 
 
 	/* Find our open stateid */
-	cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
+	cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0);
 	if (unlikely(cred == NULL))
 		return -ENOMEM;
 	ctx = alloc_nfs_open_context(dentry, cred);
--- ./fs/nfs/unlink.c.orig	2005-02-06 18:05:04.000000000 +0100
+++ ./fs/nfs/unlink.c	2005-02-14 21:38:41.000000000 +0100
@@ -183,7 +183,7 @@
 	spin_lock(&dentry->d_lock);
 	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
 	spin_unlock(&dentry->d_lock);
-	data->cred = rpcauth_lookupcred(clnt->cl_auth, 0);
+	data->cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0);
 
 	rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL);
 	status = 0;
--- ./fs/nfsd/nfs4callback.c.orig	2005-02-06 18:05:04.000000000 +0100
+++ ./fs/nfsd/nfs4callback.c	2005-02-14 22:36:56.000000000 +0100
@@ -351,19 +351,18 @@
 {
         struct auth_cred acred;
 	struct rpc_clnt *clnt = clp->cl_callback.cb_client;
+	struct svc_cred *cr = &clp->cl_cred;
         struct rpc_cred *ret = NULL;
 
 	if (!clnt)
 		goto out;
-        get_group_info(clp->cl_cred.cr_group_info);
-        acred.uid = clp->cl_cred.cr_uid;
-        acred.gid = clp->cl_cred.cr_gid;
-        acred.group_info = clp->cl_cred.cr_group_info;
-
+	acred.uid = cr->cr_uid;
         dprintk("NFSD:     looking up %s cred\n",
                 clnt->cl_auth->au_ops->au_name);
+	if (clnt->cl_auth->au_ops->cr_add_groups)
+		clnt->cl_auth->au_ops->cr_add_groups(&acred, cr->cr_gid, cr->cr_group_info, NULL);
         ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags);
-        put_group_info(clp->cl_cred.cr_group_info);
+	dprintk("NFSD:     cred %p\n", ret);
 out:
         return ret;
 }
--- ./include/linux/sunrpc/auth.h.orig	2005-02-14 21:37:34.000000000 +0100
+++ ./include/linux/sunrpc/auth.h	2005-02-14 22:14:43.000000000 +0100
@@ -24,11 +24,16 @@
 /* Maximum size (in bytes) of an rpc credential or verifier */
 #define RPC_MAX_AUTH_SIZE (400)
 
+struct rpc_groups {
+	int	ngroups;
+	gid_t	groups[RPC_MAXGROUPS];
+};
+
 /* Work around the lack of a VFS credential */
 struct auth_cred {
-	uid_t	uid;
-	gid_t	gid;
-	struct group_info *group_info;
+	uid_t			uid;
+	gid_t			gid;
+	struct rpc_groups	rg;
 };
 
 /*
@@ -92,6 +97,7 @@
 	void			(*destroy)(struct rpc_auth *);
 
 	struct rpc_cred *	(*crcreate)(struct rpc_auth*, struct auth_cred *, int);
+	void			(*cr_add_groups)(struct auth_cred *, gid_t, struct group_info *, struct rpc_groups *);
 };
 
 struct rpc_credops {
@@ -118,7 +124,7 @@
 struct rpc_auth *	rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
 void			rpcauth_destroy(struct rpc_auth *);
 struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
-struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, struct rpc_groups *, int);
 void			put_rpccred(struct rpc_cred *);
 u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
 u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
--- ./net/sunrpc/auth.c.orig	2005-02-14 21:37:34.000000000 +0100
+++ ./net/sunrpc/auth.c	2005-02-14 22:15:03.000000000 +0100
@@ -251,20 +251,19 @@
 }
 
 struct rpc_cred *
-rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
+rpcauth_lookupcred(struct rpc_auth *auth, struct rpc_groups *rg, int taskflags)
 {
 	struct auth_cred acred;
 	struct rpc_cred *ret;
 
-	get_group_info(current->group_info);
-	acred.uid = current->fsuid;
-	acred.gid = current->fsgid;
-	acred.group_info = current->group_info;
+	dprintk("RPC:      looking up %s cred\n", auth->au_ops->au_name);
 
-	dprintk("RPC:     looking up %s cred\n",
-		auth->au_ops->au_name);
+	acred.uid = current->fsuid;
+	if (auth->au_ops->cr_add_groups)
+		auth->au_ops->cr_add_groups(&acred, current->fsgid, current->group_info, rg);
 	ret = rpcauth_lookup_credcache(auth, &acred, taskflags);
-	put_group_info(current->group_info);
+
+	dprintk("RPC:      cred %p\n", ret);
 	return ret;
 }
 
--- ./net/sunrpc/auth_unix.c.orig	2005-02-14 21:36:53.000000000 +0100
+++ ./net/sunrpc/auth_unix.c	2005-02-14 22:37:33.000000000 +0100
@@ -30,6 +30,7 @@
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_AUTH
+# define RG(rg,i)		((i) < (rg).ngroups ? (int)(rg).groups[i] : -1)
 #endif
 
 static struct rpc_credops	unix_credops;
@@ -63,7 +64,6 @@
 unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
 {
 	struct unx_cred	*cred;
-	int		i;
 
 	dprintk("RPC:      allocating UNIX cred for uid %d gid %d\n",
 				acred->uid, acred->gid);
@@ -78,22 +78,55 @@
 		cred->uc_gid = 0;
 		cred->uc_gids[0] = NOGROUP;
 	} else {
-		int groups = acred->group_info->ngroups;
-		if (groups > RPC_MAXGROUPS)
-			groups = RPC_MAXGROUPS;
-
+		int n = acred->rg.ngroups;
 		cred->uc_uid = acred->uid;
 		cred->uc_gid = acred->gid;
-		for (i = 0; i < groups; i++)
-			cred->uc_gids[i] = GROUP_AT(acred->group_info, i);
-		if (i < RPC_MAXGROUPS)
-			cred->uc_gids[i] = NOGROUP;
+		memcpy(cred->uc_gids, acred->rg.groups, n * sizeof (gid_t));
+		if (n < RPC_MAXGROUPS)
+			cred->uc_gids[n] = NOGROUP;
 	}
 	cred->uc_base.cr_ops = &unix_credops;
 
 	return (struct rpc_cred *) cred;
 }
 
+/*
+ * Add groups to acred. When there are too many then try to be smart by
+ * picking only the relevant ones from our secondary group list.
+ */
+static void
+unx_add_groups(struct auth_cred *acred, gid_t gid, struct group_info *gi, struct rpc_groups *rg)
+{
+	int	i, n;
+	gid_t	gid;
+
+	acred->gid = gid;
+	get_group_info(gi);
+	n = gi->ngroups;
+	if (n <= RPC_MAXGROUPS)
+		rg = NULL;
+	else
+		n = RPC_MAXGROUPS;	/* too many groups for AUTH_UNIX */
+	if (rg) {
+		n = 0;	/* pick the few relevant groups we're a member of */
+		for (i = 0; i < rg->ngroups; ++i) {
+			gid = rg->groups[i];
+			if (in_group_p(gid))
+				acred->rg.groups[n++] = gid;
+		}
+		acred->rg.ngroups = n;
+		dprintk("RPC:      unx_add_groups(): rg=%d:%d,%d,%d -> %d:%d,%d,%d\n",
+			rg->ngroups, RG(*rg, 0), RG(*rg, 1), RG(*rg, 2),
+			n, RG(acred->rg, 0), RG(acred->rg, 1), RG(acred->rg, 2));
+	} else {
+		dprintk("RPC:      unx_add_groups(): ngroups=%d\n", gi->ngroups);
+		for (i = 0; i < n; ++i)
+			acred->rg.groups[i] = GROUP_AT(gi, i);
+		acred->rg.ngroups = n;
+	}
+	put_group_info(gi);
+}
+
 static void
 unx_destroy_cred(struct rpc_cred *cred)
 {
@@ -109,22 +142,19 @@
 unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int taskflags)
 {
 	struct unx_cred	*cred = (struct unx_cred *) rcred;
-	int		i;
 
 	if (!(taskflags & RPC_TASK_ROOTCREDS)) {
-		int groups;
+		int n = acred->rg.ngroups;
 
 		if (cred->uc_uid != acred->uid
 		 || cred->uc_gid != acred->gid)
 			return 0;
 
-		groups = acred->group_info->ngroups;
-		if (groups > RPC_MAXGROUPS)
-			groups = RPC_MAXGROUPS;
-		for (i = 0; i < groups ; i++)
-			if (cred->uc_gids[i] != GROUP_AT(acred->group_info, i))
-				return 0;
-		return 1;
+		if (n < RPC_MAXGROUPS && cred->uc_gids[n] != NOGROUP)
+			return 0;
+
+		return !memcmp(cred->uc_gids, acred->rg.groups,
+				n * sizeof (gid_t));
 	}
 	return (cred->uc_uid == 0
 	     && cred->uc_gid == 0
@@ -210,6 +240,7 @@
 	.create		= unx_create,
 	.destroy	= unx_destroy,
 	.crcreate	= unx_create_cred,
+	.cr_add_groups	= unx_add_groups,
 };
 
 static
--- ./net/sunrpc/clnt.c.orig	2005-02-14 21:37:34.000000000 +0100
+++ ./net/sunrpc/clnt.c	2005-02-14 21:38:41.000000000 +0100
@@ -430,7 +430,7 @@
 		/* we copied msg->rpc_cred, hold it */
 		get_rpccred(task->tk_msg.rpc_cred);
 	} else {
-		task->tk_msg.rpc_cred = rpcauth_lookupcred(task->tk_auth, task->tk_flags);
+		task->tk_msg.rpc_cred = rpcauth_lookupcred(task->tk_auth, NULL, task->tk_flags);
 		if (task->tk_msg.rpc_cred == NULL)
 			task->tk_status = -ENOMEM;
 	}