From: Tim Hockin <thockin@sun.com>

Avoid performing a syscall from inside nfsd.


---

 25-akpm/fs/nfsd/auth.c            |   43 ++++++++++++++++++++++++++------------
 25-akpm/include/linux/nfsd/auth.h |    2 -
 25-akpm/kernel/sys.c              |    6 +++++
 3 files changed, 37 insertions(+), 14 deletions(-)

diff -puN fs/nfsd/auth.c~increase-NGROUPS-nfsd-cleanup fs/nfsd/auth.c
--- 25/fs/nfsd/auth.c~increase-NGROUPS-nfsd-cleanup	Thu Feb  5 16:04:50 2004
+++ 25-akpm/fs/nfsd/auth.c	Thu Feb  5 16:04:52 2004
@@ -10,15 +10,27 @@
 #include <linux/sunrpc/svcauth.h>
 #include <linux/nfsd/nfsd.h>
 
-extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist);
-
 #define	CAP_NFSD_MASK (CAP_FS_MASK|CAP_TO_MASK(CAP_SYS_RESOURCE))
-void
-nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
+
+int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
 {
 	struct svc_cred	*cred = &rqstp->rq_cred;
-	int		i;
-	gid_t		groups[SVC_CRED_NGROUPS];
+	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;
@@ -42,19 +54,24 @@ nfsd_setuser(struct svc_rqst *rqstp, str
 		current->fsgid = cred->cr_gid;
 	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;
-		groups[i] = group;
+		GROUP_AT(group_info, i) = group;
 	}
-	sys_setgroups(i, groups);
 
-	if ((cred->cr_uid)) {
-		cap_t(current->cap_effective) &= ~CAP_NFSD_MASK;
+	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);
+		}
 	} else {
-		cap_t(current->cap_effective) |= (CAP_NFSD_MASK &
-						  current->cap_permitted);
+		put_group_info(group_info);
 	}
-
+	return ret;
 }
diff -puN kernel/sys.c~increase-NGROUPS-nfsd-cleanup kernel/sys.c
--- 25/kernel/sys.c~increase-NGROUPS-nfsd-cleanup	Thu Feb  5 16:04:50 2004
+++ 25-akpm/kernel/sys.c	Thu Feb  5 16:04:52 2004
@@ -1130,6 +1130,8 @@ out_undo_partial_alloc:
 	return NULL;
 }
 
+EXPORT_SYMBOL(groups_alloc);
+
 void groups_free(struct group_info *group_info)
 {
 	if (group_info->blocks[0] != group_info->small_block) {
@@ -1140,6 +1142,8 @@ void groups_free(struct group_info *grou
 	kfree(group_info);
 }
 
+EXPORT_SYMBOL(groups_free);
+
 /* export the group_info to a user-space array */
 static int groups_to_user(gid_t __user *grouplist,
     struct group_info *group_info)
@@ -1250,6 +1254,8 @@ int set_current_groups(struct group_info
 	return 0;
 }
 
+EXPORT_SYMBOL(set_current_groups);
+
 asmlinkage long sys_getgroups(int gidsetsize, gid_t __user *grouplist)
 {
 	int i = 0;
diff -puN include/linux/nfsd/auth.h~increase-NGROUPS-nfsd-cleanup include/linux/nfsd/auth.h
--- 25/include/linux/nfsd/auth.h~increase-NGROUPS-nfsd-cleanup	Thu Feb  5 16:04:50 2004
+++ 25-akpm/include/linux/nfsd/auth.h	Thu Feb  5 16:04:50 2004
@@ -21,7 +21,7 @@
  * Set the current process's fsuid/fsgid etc to those of the NFS
  * client user
  */
-void		nfsd_setuser(struct svc_rqst *, struct svc_export *);
+int nfsd_setuser(struct svc_rqst *, struct svc_export *);
 
 #endif /* __KERNEL__ */
 #endif /* LINUX_NFSD_AUTH_H */

_