From: Neil Brown <neilb@cse.unsw.edu.au>

Tidy up new groups handling in nfsd.

Set up the group_info structure when decoding the RPC packet instead of in
nfsd.


---

 fs/nfsd/auth.c                 |   45 +++++++++--------------------------------
 fs/nfsd/nfs4state.c            |    7 +++---
 fs/nfsd/nfsfh.c                |    6 ++++-
 include/linux/sunrpc/svcauth.h |    2 -
 net/sunrpc/svcauth_unix.c      |   21 +++++++++++--------
 5 files changed, 33 insertions(+), 48 deletions(-)

diff -puN fs/nfsd/auth.c~nfsd-NGROUPS-fixes fs/nfsd/auth.c
--- 25/fs/nfsd/auth.c~nfsd-NGROUPS-fixes	2004-02-24 22:20:59.000000000 -0800
+++ 25-akpm/fs/nfsd/auth.c	2004-02-24 22:20:59.000000000 -0800
@@ -15,35 +15,21 @@
 int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
 {
 	struct svc_cred	*cred = &rqstp->rq_cred;
-	struct group_info *group_info;
-	int ngroups;
 	int i;
 	int ret;
 
-	ngroups = 0;
-	if (!(exp->ex_flags & NFSEXP_ALLSQUASH)) {
-		for (i = 0; i < SVC_CRED_NGROUPS; i++) {
-			if (cred->cr_groups[i] == (gid_t)NOGROUP)
-				break;
-			ngroups++;
-		}
-	}
-	group_info = groups_alloc(ngroups);
-	if (group_info == NULL)
-		return -ENOMEM;
-
 	if (exp->ex_flags & NFSEXP_ALLSQUASH) {
 		cred->cr_uid = exp->ex_anon_uid;
 		cred->cr_gid = exp->ex_anon_gid;
-		cred->cr_groups[0] = NOGROUP;
+		cred->cr_group_info->ngroups = 0;
 	} else if (exp->ex_flags & NFSEXP_ROOTSQUASH) {
 		if (!cred->cr_uid)
 			cred->cr_uid = exp->ex_anon_uid;
 		if (!cred->cr_gid)
 			cred->cr_gid = exp->ex_anon_gid;
-		for (i = 0; i < SVC_CRED_NGROUPS; i++)
-			if (!cred->cr_groups[i])
-				cred->cr_groups[i] = exp->ex_anon_gid;
+		for (i = 0; i < cred->cr_group_info->ngroups; i++)
+			if (!GROUP_AT(cred->cr_group_info, i))
+				GROUP_AT(cred->cr_group_info, i) = exp->ex_anon_gid;
 	}
 
 	if (cred->cr_uid != (uid_t) -1)
@@ -55,23 +41,12 @@ int nfsd_setuser(struct svc_rqst *rqstp,
 	else
 		current->fsgid = exp->ex_anon_gid;
 
-	for (i = 0; i < SVC_CRED_NGROUPS; i++) {
-		gid_t group = cred->cr_groups[i];
-		if (group == (gid_t) NOGROUP)
-			break;
-		GROUP_AT(group_info, i) = group;
+	ret = set_current_groups(cred->cr_group_info);
+	if ((cred->cr_uid)) {
+		cap_t(current->cap_effective) &= ~CAP_NFSD_MASK;
+	} else {
+		cap_t(current->cap_effective) |= (CAP_NFSD_MASK &
+						  current->cap_permitted);
 	}
-
-	ret = set_current_groups(group_info);
-	if (ret == 0) {
-		if ((cred->cr_uid)) {
-			cap_t(current->cap_effective) &= ~CAP_NFSD_MASK;
-		} else {
-			cap_t(current->cap_effective) |= (CAP_NFSD_MASK &
-							current->cap_permitted);
-		}
-	}
-	put_group_info(group_info);
-
 	return ret;
 }
diff -puN fs/nfsd/nfs4state.c~nfsd-NGROUPS-fixes fs/nfsd/nfs4state.c
--- 25/fs/nfsd/nfs4state.c~nfsd-NGROUPS-fixes	2004-02-24 22:20:59.000000000 -0800
+++ 25-akpm/fs/nfsd/nfs4state.c	2004-02-24 22:20:59.000000000 -0800
@@ -193,6 +193,8 @@ alloc_client(struct xdr_netobj name)
 static inline void
 free_client(struct nfs4_client *clp)
 {
+	if (clp->cl_cred.cr_group_info)
+		put_group_info(clp->cl_cred.cr_group_info);
 	kfree(clp->cl_name.data);
 	kfree(clp);
 }
@@ -240,12 +242,11 @@ copy_clid(struct nfs4_client *target, st
 
 static void
 copy_cred(struct svc_cred *target, struct svc_cred *source) {
-	int i;
 
 	target->cr_uid = source->cr_uid;
 	target->cr_gid = source->cr_gid;
-	for(i = 0; i < SVC_CRED_NGROUPS; i++)
-		target->cr_groups[i] = source->cr_groups[i];
+	target->cr_group_info = source->cr_group_info;
+	get_group_info(target->cr_group_info);
 }
 
 static int
diff -puN fs/nfsd/nfsfh.c~nfsd-NGROUPS-fixes fs/nfsd/nfsfh.c
--- 25/fs/nfsd/nfsfh.c~nfsd-NGROUPS-fixes	2004-02-24 22:20:59.000000000 -0800
+++ 25-akpm/fs/nfsd/nfsfh.c	2004-02-24 22:20:59.000000000 -0800
@@ -165,7 +165,11 @@ fh_verify(struct svc_rqst *rqstp, struct
 		}
 
 		/* Set user creds for this exportpoint */
-		nfsd_setuser(rqstp, exp);
+		error = nfsd_setuser(rqstp, exp);
+		if (error) {
+			error = nfserrno(error);
+			goto out;
+		}
 
 		/*
 		 * Look up the dentry using the NFS file handle.
diff -puN include/linux/sunrpc/svcauth.h~nfsd-NGROUPS-fixes include/linux/sunrpc/svcauth.h
--- 25/include/linux/sunrpc/svcauth.h~nfsd-NGROUPS-fixes	2004-02-24 22:20:59.000000000 -0800
+++ 25-akpm/include/linux/sunrpc/svcauth.h	2004-02-24 22:20:59.000000000 -0800
@@ -20,7 +20,7 @@
 struct svc_cred {
 	uid_t			cr_uid;
 	gid_t			cr_gid;
-	gid_t			cr_groups[SVC_CRED_NGROUPS];
+	struct group_info	*cr_group_info;
 };
 
 struct svc_rqst;		/* forward decl */
diff -puN net/sunrpc/svcauth_unix.c~nfsd-NGROUPS-fixes net/sunrpc/svcauth_unix.c
--- 25/net/sunrpc/svcauth_unix.c~nfsd-NGROUPS-fixes	2004-02-24 22:20:59.000000000 -0800
+++ 25-akpm/net/sunrpc/svcauth_unix.c	2004-02-24 22:20:59.000000000 -0800
@@ -357,7 +357,9 @@ svcauth_null_accept(struct svc_rqst *rqs
 	/* Signal that mapping to nobody uid/gid is required */
 	rqstp->rq_cred.cr_uid = (uid_t) -1;
 	rqstp->rq_cred.cr_gid = (gid_t) -1;
-	rqstp->rq_cred.cr_groups[0] = NOGROUP;
+	rqstp->rq_cred.cr_group_info = groups_alloc(0);
+	if (rqstp->rq_cred.cr_group_info == NULL)
+		return SVC_DROP; /* kmalloc failure - client must retry */
 
 	/* Put NULL verifier */
 	svc_putu32(resv, RPC_AUTH_NULL);
@@ -424,6 +426,9 @@ svcauth_unix_accept(struct svc_rqst *rqs
 	int		rv=0;
 	struct ip_map key, *ipm;
 
+	cred->cr_group_info = NULL;
+	rqstp->rq_client = NULL;
+
 	if ((len -= 3*4) < 0)
 		return SVC_GARBAGE;
 
@@ -440,13 +445,11 @@ svcauth_unix_accept(struct svc_rqst *rqs
 	slen = ntohl(svc_getu32(argv));			/* gids length */
 	if (slen > 16 || (len -= (slen + 2)*4) < 0)
 		goto badcred;
+	cred->cr_group_info = groups_alloc(slen);
+	if (cred->cr_group_info == NULL)
+		return SVC_DROP;
 	for (i = 0; i < slen; i++)
-		if (i < SVC_CRED_NGROUPS)
-			cred->cr_groups[i] = ntohl(svc_getu32(argv));
-		else
-			svc_getu32(argv);
-	if (i < SVC_CRED_NGROUPS)
-		cred->cr_groups[i] = NOGROUP;
+		GROUP_AT(cred->cr_group_info, i) = ntohl(svc_getu32(argv));
 
 	if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
 		*authp = rpc_autherr_badverf;
@@ -460,7 +463,6 @@ svcauth_unix_accept(struct svc_rqst *rqs
 
 	ipm = ip_map_lookup(&key, 0);
 
-	rqstp->rq_client = NULL;
 	if (ipm)
 		switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
 		case -EAGAIN:
@@ -501,6 +503,9 @@ svcauth_unix_release(struct svc_rqst *rq
 	if (rqstp->rq_client)
 		auth_domain_put(rqstp->rq_client);
 	rqstp->rq_client = NULL;
+	if (rqstp->rq_cred.cr_group_info)
+		put_group_info(rqstp->rq_cred.cr_group_info);
+	rqstp->rq_cred.cr_group_info = NULL;
 
 	return 0;
 }

_