diff -ru a/fs/nfs/dir.c b/fs/nfs/dir.c
--- a/fs/nfs/dir.c	2005-02-06 18:05:03.000000000 +0100
+++ b/fs/nfs/dir.c	2005-02-14 22:41:49.000000000 +0100
@@ -580,6 +580,7 @@
 	struct nfs_fattr fattr;
 	unsigned long verifier;
 	int isopen = 0;
+	struct rpc_groups fsg;
 
 	parent = dget_parent(dentry);
 	lock_kernel();
@@ -615,7 +616,9 @@
 		goto out_bad;
 
 	verifier = nfs_save_change_attribute(dir);
-	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+	fsg.ngroups = 1;
+	fsg.groups[0] = dir->i_gid;
+	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr, &fsg);
 	if (error)
 		goto out_bad;
 	if (nfs_compare_fh(NFS_FH(inode), &fhandle))
@@ -709,6 +712,7 @@
 	int error;
 	struct nfs_fh fhandle;
 	struct nfs_fattr fattr;
+	struct rpc_groups fsg;
 
 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -728,7 +732,9 @@
 	if (nfs_is_exclusive_create(dir, nd))
 		goto no_entry;
 
-	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+	fsg.ngroups = 1;
+	fsg.groups[0] = dir->i_gid;
+	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr, &fsg);
 	if (error == -ENOENT)
 		goto no_entry;
 	if (error < 0) {
@@ -939,7 +945,7 @@
  * Code common to create, mkdir, and mknod.
  */
 static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
-				struct nfs_fattr *fattr)
+			   struct nfs_fattr *fattr, struct rpc_groups *fsg)
 {
 	struct inode *inode;
 	int error = -EACCES;
@@ -949,7 +955,7 @@
 		return 0;
 	if (fhandle->size == 0) {
 		struct inode *dir = dentry->d_parent->d_inode;
-		error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+		error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, fsg);
 		if (error)
 			goto out_err;
 	}
@@ -985,6 +991,7 @@
 	struct inode *inode;
 	int error;
 	int open_flags = 0;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 
 	dfprintk(VFS, "NFS: create(%s/%ld, %s\n", dir->i_sb->s_id, 
 		dir->i_ino, dentry->d_name.name);
@@ -997,7 +1004,7 @@
 
 	lock_kernel();
 	nfs_begin_data_update(dir);
-	inode = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags);
+	inode = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, &fsg);
 	nfs_end_data_update(dir);
 	if (!IS_ERR(inode)) {
 		d_instantiate(dentry, inode);
@@ -1022,6 +1029,7 @@
 	struct nfs_fattr fattr;
 	struct nfs_fh fhandle;
 	int error;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 
 	dfprintk(VFS, "NFS: mknod(%s/%ld, %s\n", dir->i_sb->s_id,
 		dir->i_ino, dentry->d_name.name);
@@ -1035,10 +1043,10 @@
 	lock_kernel();
 	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev,
-					&fhandle, &fattr);
+					&fhandle, &fattr, &fsg);
 	nfs_end_data_update(dir);
 	if (!error)
-		error = nfs_instantiate(dentry, &fhandle, &fattr);
+		error = nfs_instantiate(dentry, &fhandle, &fattr, &fsg);
 	else
 		d_drop(dentry);
 	unlock_kernel();
@@ -1054,6 +1062,7 @@
 	struct nfs_fattr fattr;
 	struct nfs_fh fhandle;
 	int error;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 
 	dfprintk(VFS, "NFS: mkdir(%s/%ld, %s\n", dir->i_sb->s_id,
 		dir->i_ino, dentry->d_name.name);
@@ -1073,10 +1082,10 @@
 #endif
 	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
-					&fattr);
+					&fattr, &fsg);
 	nfs_end_data_update(dir);
 	if (!error)
-		error = nfs_instantiate(dentry, &fhandle, &fattr);
+		error = nfs_instantiate(dentry, &fhandle, &fattr, &fsg);
 	else
 		d_drop(dentry);
 	unlock_kernel();
@@ -1086,13 +1095,14 @@
 static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	int error;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 
 	dfprintk(VFS, "NFS: rmdir(%s/%ld, %s\n", dir->i_sb->s_id,
 		dir->i_ino, dentry->d_name.name);
 
 	lock_kernel();
 	nfs_begin_data_update(dir);
-	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
+	error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name, &fsg);
 	/* Ensure the VFS deletes this inode */
 	if (error == 0 && dentry->d_inode != NULL)
 		dentry->d_inode->i_nlink = 0;
@@ -1112,6 +1122,7 @@
 	struct qstr    qsilly;
 	struct dentry *sdentry;
 	int            error = -EIO;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 
 	dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name, 
@@ -1158,11 +1169,11 @@
 	if (dentry->d_inode) {
 		nfs_begin_data_update(dentry->d_inode);
 		error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
-				dir, &qsilly);
+				dir, &qsilly, &fsg);
 		nfs_end_data_update(dentry->d_inode);
 	} else
 		error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
-				dir, &qsilly);
+				dir, &qsilly, &fsg);
 	nfs_end_data_update(dir);
 	if (!error) {
 		nfs_renew_times(dentry);
@@ -1187,6 +1198,7 @@
 {
 	struct inode *dir = dentry->d_parent->d_inode;
 	struct inode *inode = dentry->d_inode;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 	int error = -EBUSY;
 		
 	dfprintk(VFS, "NFS: safe_remove(%s/%s)\n",
@@ -1201,13 +1213,13 @@
 	nfs_begin_data_update(dir);
 	if (inode != NULL) {
 		nfs_begin_data_update(inode);
-		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
+		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name, &fsg);
 		/* The VFS may want to delete this inode */
 		if (error == 0)
 			inode->i_nlink--;
 		nfs_end_data_update(inode);
 	} else
-		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
+		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name, &fsg);
 	nfs_end_data_update(dir);
 out:
 	return error;
@@ -1260,6 +1272,7 @@
 	struct nfs_fh sym_fh;
 	struct qstr qsymname;
 	int error;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 
 	dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id,
 		dir->i_ino, dentry->d_name.name, symname);
@@ -1282,10 +1295,10 @@
 	lock_kernel();
 	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname,
-					  &attr, &sym_fh, &sym_attr);
+					  &attr, &sym_fh, &sym_attr, &fsg);
 	nfs_end_data_update(dir);
 	if (!error) {
-		error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+		error = nfs_instantiate(dentry, &sym_fh, &sym_attr, &fsg);
 	} else {
 		if (error == -EEXIST)
 			printk("nfs_proc_symlink: %s/%s already exists??\n",
@@ -1300,6 +1313,7 @@
 nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
 	struct inode *inode = old_dentry->d_inode;
+	struct rpc_groups fsg = { 1, { dir->i_gid } };
 	int error;
 
 	dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
@@ -1316,7 +1330,7 @@
 
 	nfs_begin_data_update(dir);
 	nfs_begin_data_update(inode);
-	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
+	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name, &fsg);
 	nfs_end_data_update(inode);
 	nfs_end_data_update(dir);
 	unlock_kernel();
@@ -1353,6 +1367,14 @@
 	struct inode *old_inode = old_dentry->d_inode;
 	struct inode *new_inode = new_dentry->d_inode;
 	struct dentry *dentry = NULL, *rehash = NULL;
+	struct rpc_groups fsg = {
+		.ngroups = 3,
+		.groups = {
+			old_dir->i_gid,
+			new_dir->i_gid,		/* old_dir != new_dir */
+			old_inode->i_gid	/* reparent a dir */
+		}
+	};
 	int error = -EBUSY;
 
 	/*
@@ -1425,7 +1447,7 @@
 	nfs_begin_data_update(new_dir);
 	nfs_begin_data_update(old_inode);
 	error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
-					   new_dir, &new_dentry->d_name);
+					   new_dir, &new_dentry->d_name, &fsg);
 	nfs_end_data_update(old_inode);
 	nfs_end_data_update(new_dir);
 	nfs_end_data_update(old_dir);
@@ -1486,6 +1508,8 @@
 	cache.cred = cred;
 	cache.jiffies = jiffies;
 	status = NFS_PROTO(inode)->access(inode, &cache);
+	dfprintk(VFS, "NFS: access()=%d for ino %lu, cred %p, mask 0x%x->0x%x\n",
+		 status, inode->i_ino, cred, mask, cache.mask);
 	if (status != 0)
 		return status;
 	nfs_access_add_cache(inode, &cache);
@@ -1499,6 +1523,7 @@
 {
 	struct rpc_cred *cred;
 	int res;
+	struct rpc_groups fsg = { 1, { inode->i_gid } };
 
 	if (mask == 0)
 		return 0;
@@ -1518,7 +1543,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, &fsg, 0);
 	res = nfs_do_access(inode, cred, mask);
 	put_rpccred(cred);
 	unlock_kernel();
diff -ru a/fs/nfs/file.c b/fs/nfs/file.c
--- a/fs/nfs/file.c	2005-02-06 18:05:04.000000000 +0100
+++ b/fs/nfs/file.c	2005-02-14 21:38:12.000000000 +0100
@@ -95,7 +95,6 @@
 		return res;
 
 	lock_kernel();
-	/* Do NFSv4 open() call */
 	if ((open = server->rpc_ops->file_open) != NULL)
 		res = open(inode, filp);
 	unlock_kernel();
diff -ru a/fs/nfs/inode.c b/fs/nfs/inode.c
--- a/fs/nfs/inode.c	2005-02-06 18:05:04.000000000 +0100
+++ b/fs/nfs/inode.c	2005-02-14 22:41:49.000000000 +0100
@@ -366,7 +366,10 @@
 
 	clnt->cl_intr     = (server->flags & NFS_MOUNT_INTR) ? 1 : 0;
 	clnt->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0;
-	clnt->cl_droppriv = (server->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0;
+	if (server->flags & NFS_MOUNT_BROKEN_SUID) {
+		printk(KERN_WARNING "NFS: mount option \"broken_suid\" ignored.\n");
+		server->flags &= ~NFS_MOUNT_BROKEN_SUID;
+	}
 	clnt->cl_chatty   = 1;
 
 	return clnt;
@@ -538,7 +541,6 @@
 		{ NFS_MOUNT_NOCTO, ",nocto", "" },
 		{ NFS_MOUNT_NOAC, ",noac", "" },
 		{ NFS_MOUNT_NONLM, ",nolock", ",lock" },
-		{ NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" },
 		{ 0, NULL, NULL }
 	};
 	struct proc_nfs_info *nfs_infop;
@@ -743,6 +745,7 @@
 {
 	struct inode *inode = dentry->d_inode;
 	struct nfs_fattr fattr;
+	struct rpc_groups fsg;
 	int error;
 
 	if (attr->ia_valid & ATTR_SIZE) {
@@ -763,7 +766,11 @@
 			filemap_fdatawait(inode->i_mapping);
 		nfs_wb_all(inode);
 	}
-	error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
+	fsg.ngroups = 0;
+	fsg.groups[fsg.ngroups++] = inode->i_gid;	/* ATTR_SIZE */
+	if (attr->ia_valid & ATTR_GID)
+		fsg.groups[fsg.ngroups++] = attr->ia_gid;
+	error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr, &fsg);
 	if (error == 0) {
 		nfs_refresh_inode(inode, &fattr);
 		if ((attr->ia_valid & ATTR_MODE) != 0) {
@@ -837,7 +844,7 @@
 	if (ctx != NULL) {
 		atomic_set(&ctx->count, 1);
 		ctx->dentry = dget(dentry);
-		ctx->cred = get_rpccred(cred);
+		ctx->cred = cred;
 		ctx->state = NULL;
 		ctx->lockowner = current->files;
 		ctx->error = 0;
@@ -917,11 +924,11 @@
 {
 	struct nfs_open_context *ctx;
 	struct rpc_cred *cred;
+	struct rpc_groups fsg = { 1, { inode->i_gid } };
 
-	if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL)
+	if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, &fsg, 0)) == NULL)
 		return -ENOMEM;
 	ctx = alloc_nfs_open_context(filp->f_dentry, cred);
-	put_rpccred(cred);
 	if (ctx == NULL)
 		return -ENOMEM;
 	ctx->mode = filp->f_mode;
@@ -1611,7 +1618,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);
 	}
diff -ru a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
--- a/fs/nfs/nfs3proc.c	2005-02-06 18:05:04.000000000 +0100
+++ b/fs/nfs/nfs3proc.c	2005-02-14 22:41:49.000000000 +0100
@@ -24,13 +24,21 @@
 
 /* A wrapper to handle the EJUKEBOX error message */
 static int
-nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
+nfs3_rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
+	      struct rpc_cred *cred, int flags)
 {
-	sigset_t oldset;
-	int res;
+	struct rpc_message msg = {
+		.rpc_proc	= &nfs3_procedures[proc],
+		.rpc_argp	= argp,
+		.rpc_resp	= resp,
+		.rpc_cred	= cred,
+	};
+	sigset_t		oldset;
+	int			res;
+
 	rpc_clnt_sigmask(clnt, &oldset);
 	do {
-		res = rpc_call_sync(clnt, msg, flags);
+		res = rpc_call_sync(clnt, &msg, flags);
 		if (res != -EJUKEBOX)
 			break;
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -41,21 +49,19 @@
 	return res;
 }
 
-static inline int
-nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
+static int
+nfs3_rpc(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
+	 struct rpc_groups *fsg)
 {
-	struct rpc_message msg = {
-		.rpc_proc	= &nfs3_procedures[proc],
-		.rpc_argp	= argp,
-		.rpc_resp	= resp,
-	};
-	return nfs3_rpc_wrapper(clnt, &msg, flags);
-}
+	struct rpc_cred	*cred;
+	int		res;
 
-#define rpc_call(clnt, proc, argp, resp, flags) \
-		nfs3_rpc_call_wrapper(clnt, proc, argp, resp, flags)
-#define rpc_call_sync(clnt, msg, flags) \
-		nfs3_rpc_wrapper(clnt, msg, flags)
+	if ((cred = rpcauth_lookupcred(clnt->cl_auth, fsg, 0)) == NULL)
+		return -ENOMEM;
+	res = nfs3_rpc_call(clnt, proc, argp, resp, cred, 0);
+	put_rpccred(cred);
+	return res;
+}
 
 static int
 nfs3_async_handle_jukebox(struct rpc_task *task)
@@ -79,10 +85,11 @@
 
 	dprintk("%s: call  fsinfo\n", __FUNCTION__);
 	info->fattr->valid = 0;
-	status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
+	status = nfs3_rpc(server->client_sys, NFS3PROC_FSINFO, fhandle, info, NULL);
 	dprintk("%s: reply fsinfo: %d\n", __FUNCTION__, status);
 	if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
-		status = rpc_call(server->client_sys, NFS3PROC_GETATTR, fhandle, info->fattr, 0);
+		status = nfs3_rpc(server->client_sys, NFS3PROC_GETATTR, fhandle,
+				  info->fattr, NULL);
 		dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
 	}
 	return status;
@@ -99,15 +106,14 @@
 
 	dprintk("NFS call  getattr\n");
 	fattr->valid = 0;
-	status = rpc_call(server->client, NFS3PROC_GETATTR,
-			  fhandle, fattr, 0);
+	status = nfs3_rpc(server->client, NFS3PROC_GETATTR, fhandle, fattr, NULL);
 	dprintk("NFS reply getattr: %d\n", status);
 	return status;
 }
 
 static int
 nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
-			struct iattr *sattr)
+			struct iattr *sattr, struct rpc_groups *fsg)
 {
 	struct inode *inode = dentry->d_inode;
 	struct nfs3_sattrargs	arg = {
@@ -118,14 +124,15 @@
 
 	dprintk("NFS call  setattr\n");
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
+	status = nfs3_rpc(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, fsg);
 	dprintk("NFS reply setattr: %d\n", status);
 	return status;
 }
 
 static int
 nfs3_proc_lookup(struct inode *dir, struct qstr *name,
-		 struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+		 struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+		 struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr;
 	struct nfs3_diropargs	arg = {
@@ -143,10 +150,10 @@
 	dprintk("NFS call  lookup %s\n", name->name);
 	dir_attr.valid = 0;
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, fsg);
 	if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
-		status = rpc_call(NFS_CLIENT(dir), NFS3PROC_GETATTR,
-			 fhandle, fattr, 0);
+		status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_GETATTR,
+				  fhandle, fattr, NULL);
 	dprintk("NFS reply lookup: %d\n", status);
 	if (status >= 0)
 		status = nfs_refresh_inode(dir, &dir_attr);
@@ -162,12 +169,6 @@
 	struct nfs3_accessres	res = {
 		.fattr		= &fattr,
 	};
-	struct rpc_message msg = {
-		.rpc_proc	= &nfs3_procedures[NFS3PROC_ACCESS],
-		.rpc_argp	= &arg,
-		.rpc_resp	= &res,
-		.rpc_cred	= entry->cred
-	};
 	int mode = entry->mask;
 	int status;
 
@@ -187,7 +188,8 @@
 		if (mode & MAY_EXEC)
 			arg.access |= NFS3_ACCESS_EXECUTE;
 	}
-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+	status = nfs3_rpc_call(NFS_CLIENT(inode), NFS3PROC_ACCESS,
+			  &arg, &res, entry->cred, 0);
 	nfs_refresh_inode(inode, &fattr);
 	if (status == 0) {
 		entry->mask = 0;
@@ -216,8 +218,7 @@
 
 	dprintk("NFS call  readlink\n");
 	fattr.valid = 0;
-	status = rpc_call(NFS_CLIENT(inode), NFS3PROC_READLINK,
-			  &args, &fattr, 0);
+	status = nfs3_rpc(NFS_CLIENT(inode), NFS3PROC_READLINK, &args, &fattr, NULL);
 	nfs_refresh_inode(inode, &fattr);
 	dprintk("NFS reply readlink: %d\n", status);
 	return status;
@@ -228,18 +229,13 @@
 	int			flags = rdata->flags;
 	struct inode *		inode = rdata->inode;
 	struct nfs_fattr *	fattr = rdata->res.fattr;
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs3_procedures[NFS3PROC_READ],
-		.rpc_argp	= &rdata->args,
-		.rpc_resp	= &rdata->res,
-		.rpc_cred	= rdata->cred,
-	};
 	int			status;
 
 	dprintk("NFS call  read %d @ %Ld\n", rdata->args.count,
 			(long long) rdata->args.offset);
 	fattr->valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+	status = nfs3_rpc_call(NFS_CLIENT(inode), NFS3PROC_READ,
+			  &rdata->args, &rdata->res, rdata->cred, flags);
 	if (status >= 0)
 		nfs_refresh_inode(inode, fattr);
 	dprintk("NFS reply read: %d\n", status);
@@ -251,18 +247,13 @@
 	int			rpcflags = wdata->flags;
 	struct inode *		inode = wdata->inode;
 	struct nfs_fattr *	fattr = wdata->res.fattr;
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs3_procedures[NFS3PROC_WRITE],
-		.rpc_argp	= &wdata->args,
-		.rpc_resp	= &wdata->res,
-		.rpc_cred	= wdata->cred,
-	};
 	int			status;
 
 	dprintk("NFS call  write %d @ %Ld\n", wdata->args.count,
 			(long long) wdata->args.offset);
 	fattr->valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
+	status = nfs3_rpc_call(NFS_CLIENT(inode), NFS3PROC_WRITE,
+			  &wdata->args, &wdata->res, wdata->cred, rpcflags);
 	if (status >= 0)
 		nfs_refresh_inode(inode, fattr);
 	dprintk("NFS reply write: %d\n", status);
@@ -273,18 +264,13 @@
 {
 	struct inode *		inode = cdata->inode;
 	struct nfs_fattr *	fattr = cdata->res.fattr;
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs3_procedures[NFS3PROC_COMMIT],
-		.rpc_argp	= &cdata->args,
-		.rpc_resp	= &cdata->res,
-		.rpc_cred	= cdata->cred,
-	};
 	int			status;
 
 	dprintk("NFS call  commit %d @ %Ld\n", cdata->args.count,
 			(long long) cdata->args.offset);
 	fattr->valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+	status = nfs3_rpc_call(NFS_CLIENT(inode), NFS3PROC_COMMIT,
+			  &cdata->args, &cdata->res, cdata->cred, 0);
 	if (status >= 0)
 		nfs_refresh_inode(inode, fattr);
 	dprintk("NFS reply commit: %d\n", status);
@@ -297,7 +283,7 @@
  */
 static struct inode *
 nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
-		 int flags)
+		 int flags, struct rpc_groups *fsg)
 {
 	struct nfs_fh		fhandle;
 	struct nfs_fattr	fattr;
@@ -326,7 +312,7 @@
 again:
 	dir_attr.valid = 0;
 	fattr.valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 
 	/* If the server doesn't support the exclusive creation semantics,
@@ -353,7 +339,7 @@
 	if (status != 0)
 		goto out;
 	if (fhandle.size == 0 || !(fattr.valid & NFS_ATTR_FATTR)) {
-		status = nfs3_proc_lookup(dir, &dentry->d_name, &fhandle, &fattr);
+		status = nfs3_proc_lookup(dir, &dentry->d_name, &fhandle, &fattr, fsg);
 		if (status != 0)
 			goto out;
 	}
@@ -376,8 +362,8 @@
 		 * not sure this buys us anything (and I'd have
 		 * to revamp the NFSv3 XDR code) */
 		fattr.valid = 0;
-		status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SETATTR,
-						&arg, &fattr, 0);
+		status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_SETATTR,
+				  &arg, &fattr, NULL);
 		dprintk("NFS reply setattr (post-create): %d\n", status);
 	}
 	if (status == 0) {
@@ -392,7 +378,7 @@
 }
 
 static int
-nfs3_proc_remove(struct inode *dir, struct qstr *name)
+nfs3_proc_remove(struct inode *dir, struct qstr *name, struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr;
 	struct nfs3_diropargs	arg = {
@@ -400,16 +386,11 @@
 		.name		= name->name,
 		.len		= name->len
 	};
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs3_procedures[NFS3PROC_REMOVE],
-		.rpc_argp	= &arg,
-		.rpc_resp	= &dir_attr,
-	};
 	int			status;
 
 	dprintk("NFS call  remove %s\n", name->name);
 	dir_attr.valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_REMOVE, &arg, &dir_attr, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 	dprintk("NFS reply remove: %d\n", status);
 	return status;
@@ -454,7 +435,8 @@
 
 static int
 nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
-		 struct inode *new_dir, struct qstr *new_name)
+		 struct inode *new_dir, struct qstr *new_name,
+		 struct rpc_groups *fsg)
 {
 	struct nfs_fattr	old_dir_attr, new_dir_attr;
 	struct nfs3_renameargs	arg = {
@@ -474,7 +456,7 @@
 	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
 	old_dir_attr.valid = 0;
 	new_dir_attr.valid = 0;
-	status = rpc_call(NFS_CLIENT(old_dir), NFS3PROC_RENAME, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(old_dir), NFS3PROC_RENAME, &arg, &res, fsg);
 	nfs_refresh_inode(old_dir, &old_dir_attr);
 	nfs_refresh_inode(new_dir, &new_dir_attr);
 	dprintk("NFS reply rename: %d\n", status);
@@ -482,7 +464,8 @@
 }
 
 static int
-nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
+nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name,
+	       struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr, fattr;
 	struct nfs3_linkargs	arg = {
@@ -500,7 +483,7 @@
 	dprintk("NFS call  link %s\n", name->name);
 	dir_attr.valid = 0;
 	fattr.valid = 0;
-	status = rpc_call(NFS_CLIENT(inode), NFS3PROC_LINK, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(inode), NFS3PROC_LINK, &arg, &res, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 	nfs_refresh_inode(inode, &fattr);
 	dprintk("NFS reply link: %d\n", status);
@@ -510,7 +493,7 @@
 static int
 nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
 		  struct iattr *sattr, struct nfs_fh *fhandle,
-		  struct nfs_fattr *fattr)
+		  struct nfs_fattr *fattr, struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr;
 	struct nfs3_symlinkargs	arg = {
@@ -533,7 +516,7 @@
 	dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
 	dir_attr.valid = 0;
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SYMLINK, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_SYMLINK, &arg, &res, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 	dprintk("NFS reply symlink: %d\n", status);
 	return status;
@@ -541,7 +524,8 @@
 
 static int
 nfs3_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr,
-		struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+		struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+		struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr;
 	struct nfs3_mkdirargs	arg = {
@@ -560,14 +544,14 @@
 	dprintk("NFS call  mkdir %s\n", name->name);
 	dir_attr.valid = 0;
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 	dprintk("NFS reply mkdir: %d\n", status);
 	return status;
 }
 
 static int
-nfs3_proc_rmdir(struct inode *dir, struct qstr *name)
+nfs3_proc_rmdir(struct inode *dir, struct qstr *name, struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr;
 	struct nfs3_diropargs	arg = {
@@ -579,7 +563,7 @@
 
 	dprintk("NFS call  rmdir %s\n", name->name);
 	dir_attr.valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_RMDIR, &arg, &dir_attr, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_RMDIR, &arg, &dir_attr, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 	dprintk("NFS reply rmdir: %d\n", status);
 	return status;
@@ -614,24 +598,19 @@
 		.verf		= verf,
 		.plus		= plus
 	};
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs3_procedures[NFS3PROC_READDIR],
-		.rpc_argp	= &arg,
-		.rpc_resp	= &res,
-		.rpc_cred	= cred
-	};
 	int			status;
+	u32			proc = NFS3PROC_READDIR;
 
 	lock_kernel();
 
 	if (plus)
-		msg.rpc_proc = &nfs3_procedures[NFS3PROC_READDIRPLUS];
+		proc = NFS3PROC_READDIRPLUS;
 
 	dprintk("NFS call  readdir%s %d\n",
 			plus? "plus" : "", (unsigned int) cookie);
 
 	dir_attr.valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+	status = nfs3_rpc_call(NFS_CLIENT(dir), proc, &arg, &res, cred, 0);
 	nfs_refresh_inode(dir, &dir_attr);
 	dprintk("NFS reply readdir: %d\n", status);
 	unlock_kernel();
@@ -640,7 +619,8 @@
 
 static int
 nfs3_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
-		dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr)
+		dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr,
+		struct rpc_groups *fsg)
 {
 	struct nfs_fattr	dir_attr;
 	struct nfs3_mknodargs	arg = {
@@ -669,7 +649,7 @@
 			MAJOR(rdev), MINOR(rdev));
 	dir_attr.valid = 0;
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0);
+	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, fsg);
 	nfs_refresh_inode(dir, &dir_attr);
 	dprintk("NFS reply mknod: %d\n", status);
 	return status;
@@ -683,7 +663,7 @@
 
 	dprintk("NFS call  fsstat\n");
 	stat->fattr->valid = 0;
-	status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, stat, 0);
+	status = nfs3_rpc(server->client, NFS3PROC_FSSTAT, fhandle, stat, NULL);
 	dprintk("NFS reply statfs: %d\n", status);
 	return status;
 }
@@ -696,7 +676,7 @@
 
 	dprintk("NFS call  fsinfo\n");
 	info->fattr->valid = 0;
-	status = rpc_call(server->client_sys, NFS3PROC_FSINFO, fhandle, info, 0);
+	status = nfs3_rpc(server->client_sys, NFS3PROC_FSINFO, fhandle, info, NULL);
 	dprintk("NFS reply fsinfo: %d\n", status);
 	return status;
 }
@@ -709,7 +689,7 @@
 
 	dprintk("NFS call  pathconf\n");
 	info->fattr->valid = 0;
-	status = rpc_call(server->client, NFS3PROC_PATHCONF, fhandle, info, 0);
+	status = nfs3_rpc(server->client, NFS3PROC_PATHCONF, fhandle, info, NULL);
 	dprintk("NFS reply pathconf: %d\n", status);
 	return status;
 }
diff -ru a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
--- a/fs/nfs/nfs4proc.c	2005-02-06 18:05:04.000000000 +0100
+++ b/fs/nfs/nfs4proc.c	2005-02-14 22:41:49.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);
@@ -997,7 +997,7 @@
  */
 static int
 nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
-		  struct iattr *sattr)
+		  struct iattr *sattr, struct rpc_groups *fsg)
 {
 	struct inode *		inode = dentry->d_inode;
 	int			size_change = sattr->ia_valid & ATTR_SIZE;
@@ -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,
@@ -1070,7 +1070,9 @@
 	return status;
 }
 
-static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
+		struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+		struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -1317,13 +1319,13 @@
 
 static struct inode *
 nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
-                 int flags)
+		 int flags, struct rpc_groups *fsg)
 {
 	struct inode *inode;
 	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, fsg, 0);
 	state = nfs4_do_open(dir, dentry, flags, sattr, cred);
 	put_rpccred(cred);
 	if (!IS_ERR(state)) {
@@ -1364,7 +1366,8 @@
 	return status;
 }
 
-static int nfs4_proc_remove(struct inode *dir, struct qstr *name)
+static int nfs4_proc_remove(struct inode *dir, struct qstr *name,
+		struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -1441,7 +1444,8 @@
 }
 
 static int nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
-		struct inode *new_dir, struct qstr *new_name)
+		struct inode *new_dir, struct qstr *new_name,
+		struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -1476,7 +1480,8 @@
 	return status;
 }
 
-static int nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
+static int nfs4_proc_link(struct inode *inode, struct inode *dir,
+		struct qstr *name, struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -1526,7 +1531,7 @@
 
 static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
 		struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
-		struct nfs_fattr *fattr)
+		struct nfs_fattr *fattr, struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -1574,7 +1579,7 @@
 
 static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
 		struct iattr *sattr, struct nfs_fh *fhandle,
-		struct nfs_fattr *fattr)
+		struct nfs_fattr *fattr, struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -1682,7 +1687,7 @@
 
 static int nfs4_proc_mknod(struct inode *dir, struct qstr *name,
 		struct iattr *sattr, dev_t rdev, struct nfs_fh *fh,
-		struct nfs_fattr *fattr)
+		struct nfs_fattr *fattr, struct rpc_groups *fsg)
 {
 	struct nfs4_exception exception = { };
 	int err;
@@ -2000,11 +2005,10 @@
 
 
 	/* 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);
-	put_rpccred(cred);
 	if (unlikely(ctx == NULL))
 		return -ENOMEM;
 	status = -EIO; /* ERACE actually */
diff -ru a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
--- a/fs/nfs/nfsroot.c	2004-05-10 04:32:54.000000000 +0200
+++ b/fs/nfs/nfsroot.c	2005-02-14 21:36:53.000000000 +0100
@@ -124,7 +124,6 @@
 	Opt_soft, Opt_hard, Opt_intr,
 	Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac, 
 	Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp,
-	Opt_broken_suid,
 	/* Error token */
 	Opt_err
 };
@@ -159,7 +158,6 @@
 	{Opt_udp, "udp"},
 	{Opt_tcp, "proto=tcp"},
 	{Opt_tcp, "tcp"},
-	{Opt_broken_suid, "broken_suid"},
 	{Opt_err, NULL}
 	
 };
@@ -268,9 +266,6 @@
 			case Opt_tcp:
 				nfs_data.flags |= NFS_MOUNT_TCP;
 				break;
-			case Opt_broken_suid:
-				nfs_data.flags |= NFS_MOUNT_BROKEN_SUID;
-				break;
 			default : 
 				return 0;
 		}
diff -ru a/fs/nfs/proc.c b/fs/nfs/proc.c
--- a/fs/nfs/proc.c	2005-02-06 18:05:04.000000000 +0100
+++ b/fs/nfs/proc.c	2005-02-14 22:41:49.000000000 +0100
@@ -6,7 +6,7 @@
  *  OS-independent nfs remote procedure call functions
  *
  *  Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
- *  so at last we can have decent(ish) throughput off a 
+ *  so at last we can have decent(ish) throughput off a
  *  Sun server.
  *
  *  Coding optimized and cleaned up by Florian La Roche.
@@ -49,6 +49,33 @@
 
 extern struct rpc_procinfo nfs_procedures[];
 
+static __inline__ int
+nfs2_rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
+	      struct rpc_cred *cred, int flags)
+{
+	struct rpc_message msg = {
+		.rpc_proc	= &nfs_procedures[proc],
+		.rpc_argp	= argp,
+		.rpc_resp	= resp,
+		.rpc_cred	= cred,
+	};
+	return rpc_call_sync(clnt, &msg, flags);
+}
+
+static int
+nfs2_rpc(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
+	 struct rpc_groups *fsg)
+{
+	struct rpc_cred	*cred;
+	int		res;
+
+	if ((cred = rpcauth_lookupcred(clnt->cl_auth, fsg, 0)) == NULL)
+		return -ENOMEM;
+	res = nfs2_rpc_call(clnt, proc, argp, resp, cred, 0);
+	put_rpccred(cred);
+	return res;
+}
+
 /*
  * Bare-bones access to getattr: this is for nfs_read_super.
  */
@@ -62,12 +89,12 @@
 
 	dprintk("%s: call getattr\n", __FUNCTION__);
 	fattr->valid = 0;
-	status = rpc_call(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, 0);
+	status = nfs2_rpc(server->client_sys, NFSPROC_GETATTR, fhandle, fattr, NULL);
 	dprintk("%s: reply getattr: %d\n", __FUNCTION__, status);
 	if (status)
 		return status;
 	dprintk("%s: call statfs\n", __FUNCTION__);
-	status = rpc_call(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, 0);
+	status = nfs2_rpc(server->client_sys, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
 	dprintk("%s: reply statfs: %d\n", __FUNCTION__, status);
 	if (status)
 		return status;
@@ -94,18 +121,17 @@
 
 	dprintk("NFS call  getattr\n");
 	fattr->valid = 0;
-	status = rpc_call(server->client, NFSPROC_GETATTR,
-				fhandle, fattr, 0);
+	status = nfs2_rpc(server->client, NFSPROC_GETATTR, fhandle, fattr, NULL);
 	dprintk("NFS reply getattr: %d\n", status);
 	return status;
 }
 
 static int
 nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
-		 struct iattr *sattr)
+		 struct iattr *sattr, struct rpc_groups *fsg)
 {
 	struct inode *inode = dentry->d_inode;
-	struct nfs_sattrargs	arg = { 
+	struct nfs_sattrargs	arg = {
 		.fh	= NFS_FH(inode),
 		.sattr	= sattr
 	};
@@ -113,14 +139,15 @@
 
 	dprintk("NFS call  setattr\n");
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
+	status = nfs2_rpc(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, fsg);
 	dprintk("NFS reply setattr: %d\n", status);
 	return status;
 }
 
 static int
 nfs_proc_lookup(struct inode *dir, struct qstr *name,
-		struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+		struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+		struct rpc_groups *fsg)
 {
 	struct nfs_diropargs	arg = {
 		.fh		= NFS_FH(dir),
@@ -135,7 +162,7 @@
 
 	dprintk("NFS call  lookup %s\n", name->name);
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFSPROC_LOOKUP, &arg, &res, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_LOOKUP, &arg, &res, fsg);
 	dprintk("NFS reply lookup: %d\n", status);
 	return status;
 }
@@ -152,7 +179,7 @@
 	int			status;
 
 	dprintk("NFS call  readlink\n");
-	status = rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, &args, NULL, 0);
+	status = nfs2_rpc(NFS_CLIENT(inode), NFSPROC_READLINK, &args, NULL, NULL);
 	dprintk("NFS reply readlink: %d\n", status);
 	return status;
 }
@@ -162,18 +189,13 @@
 	int			flags = rdata->flags;
 	struct inode *		inode = rdata->inode;
 	struct nfs_fattr *	fattr = rdata->res.fattr;
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs_procedures[NFSPROC_READ],
-		.rpc_argp	= &rdata->args,
-		.rpc_resp	= &rdata->res,
-		.rpc_cred	= rdata->cred,
-	};
 	int			status;
 
 	dprintk("NFS call  read %d @ %Ld\n", rdata->args.count,
 			(long long) rdata->args.offset);
 	fattr->valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+	status = nfs2_rpc_call(NFS_CLIENT(inode), NFSPROC_READ, &rdata->args,
+			       &rdata->res, rdata->cred, flags);
 	if (status >= 0) {
 		nfs_refresh_inode(inode, fattr);
 		/* Emulate the eof flag, which isn't normally needed in NFSv2
@@ -191,18 +213,13 @@
 	int			flags = wdata->flags;
 	struct inode *		inode = wdata->inode;
 	struct nfs_fattr *	fattr = wdata->res.fattr;
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs_procedures[NFSPROC_WRITE],
-		.rpc_argp	= &wdata->args,
-		.rpc_resp	= &wdata->res,
-		.rpc_cred	= wdata->cred,
-	};
 	int			status;
 
 	dprintk("NFS call  write %d @ %Ld\n", wdata->args.count,
 			(long long) wdata->args.offset);
 	fattr->valid = 0;
-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+	status = nfs2_rpc_call(NFS_CLIENT(inode), NFSPROC_WRITE, &wdata->args,
+			       &wdata->res, wdata->cred, flags);
 	if (status >= 0) {
 		nfs_refresh_inode(inode, fattr);
 		wdata->res.count = wdata->args.count;
@@ -214,7 +231,7 @@
 
 static struct inode *
 nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
-		int flags)
+		int flags, struct rpc_groups *fsg)
 {
 	struct nfs_fh		fhandle;
 	struct nfs_fattr	fattr;
@@ -232,7 +249,7 @@
 
 	fattr.valid = 0;
 	dprintk("NFS call  create %s\n", dentry->d_name.name);
-	status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, fsg);
 	dprintk("NFS reply create: %d\n", status);
 	if (status == 0) {
 		struct inode *inode;
@@ -249,7 +266,8 @@
  */
 static int
 nfs_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
-	       dev_t rdev, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+	       dev_t rdev, struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+	       struct rpc_groups *fsg)
 {
 	struct nfs_createargs	arg = {
 		.fh		= NFS_FH(dir),
@@ -275,35 +293,29 @@
 	}
 
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, fsg);
 
 	if (status == -EINVAL && S_ISFIFO(mode)) {
 		sattr->ia_mode = mode;
 		fattr->valid = 0;
-		status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+		status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, fsg);
 	}
 	dprintk("NFS reply mknod: %d\n", status);
 	return status;
 }
-  
+
 static int
-nfs_proc_remove(struct inode *dir, struct qstr *name)
+nfs_proc_remove(struct inode *dir, struct qstr *name, struct rpc_groups *fsg)
 {
 	struct nfs_diropargs	arg = {
 		.fh		= NFS_FH(dir),
 		.name		= name->name,
 		.len		= name->len
 	};
-	struct rpc_message	msg = { 
-		.rpc_proc	= &nfs_procedures[NFSPROC_REMOVE],
-		.rpc_argp	= &arg,
-		.rpc_resp	= NULL,
-		.rpc_cred	= NULL
-	};
 	int			status;
 
 	dprintk("NFS call  remove %s\n", name->name);
-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_REMOVE, &arg, NULL, fsg);
 
 	dprintk("NFS reply remove: %d\n", status);
 	return status;
@@ -337,7 +349,8 @@
 
 static int
 nfs_proc_rename(struct inode *old_dir, struct qstr *old_name,
-		struct inode *new_dir, struct qstr *new_name)
+		struct inode *new_dir, struct qstr *new_name,
+		struct rpc_groups *fsg)
 {
 	struct nfs_renameargs	arg = {
 		.fromfh		= NFS_FH(old_dir),
@@ -350,13 +363,14 @@
 	int			status;
 
 	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
-	status = rpc_call(NFS_CLIENT(old_dir), NFSPROC_RENAME, &arg, NULL, 0);
+	status = nfs2_rpc(NFS_CLIENT(old_dir), NFSPROC_RENAME, &arg, NULL, fsg);
 	dprintk("NFS reply rename: %d\n", status);
 	return status;
 }
 
 static int
-nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
+nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name,
+	      struct rpc_groups *fsg)
 {
 	struct nfs_linkargs	arg = {
 		.fromfh		= NFS_FH(inode),
@@ -367,7 +381,7 @@
 	int			status;
 
 	dprintk("NFS call  link %s\n", name->name);
-	status = rpc_call(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, 0);
+	status = nfs2_rpc(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, fsg);
 	dprintk("NFS reply link: %d\n", status);
 	return status;
 }
@@ -375,7 +389,7 @@
 static int
 nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
 		 struct iattr *sattr, struct nfs_fh *fhandle,
-		 struct nfs_fattr *fattr)
+		 struct nfs_fattr *fattr, struct rpc_groups *fsg)
 {
 	struct nfs_symlinkargs	arg = {
 		.fromfh		= NFS_FH(dir),
@@ -392,14 +406,15 @@
 	dprintk("NFS call  symlink %s -> %s\n", name->name, path->name);
 	fattr->valid = 0;
 	fhandle->size = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, fsg);
 	dprintk("NFS reply symlink: %d\n", status);
 	return status;
 }
 
 static int
 nfs_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr,
-	       struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+	       struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+	       struct rpc_groups *fsg)
 {
 	struct nfs_createargs	arg = {
 		.fh		= NFS_FH(dir),
@@ -415,13 +430,13 @@
 
 	dprintk("NFS call  mkdir %s\n", name->name);
 	fattr->valid = 0;
-	status = rpc_call(NFS_CLIENT(dir), NFSPROC_MKDIR, &arg, &res, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_MKDIR, &arg, &res, fsg);
 	dprintk("NFS reply mkdir: %d\n", status);
 	return status;
 }
 
 static int
-nfs_proc_rmdir(struct inode *dir, struct qstr *name)
+nfs_proc_rmdir(struct inode *dir, struct qstr *name, struct rpc_groups *fsg)
 {
 	struct nfs_diropargs	arg = {
 		.fh		= NFS_FH(dir),
@@ -431,7 +446,7 @@
 	int			status;
 
 	dprintk("NFS call  rmdir %s\n", name->name);
-	status = rpc_call(NFS_CLIENT(dir), NFSPROC_RMDIR, &arg, NULL, 0);
+	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_RMDIR, &arg, NULL, fsg);
 	dprintk("NFS reply rmdir: %d\n", status);
 	return status;
 }
@@ -454,18 +469,12 @@
 		.count		= count,
 		.pages		= &page
 	};
-	struct rpc_message	msg = {
-		.rpc_proc	= &nfs_procedures[NFSPROC_READDIR],
-		.rpc_argp	= &arg,
-		.rpc_resp	= NULL,
-		.rpc_cred	= cred
-	};
 	int			status;
 
 	lock_kernel();
 
 	dprintk("NFS call  readdir %d\n", (unsigned int)cookie);
-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+	status = nfs2_rpc_call(NFS_CLIENT(dir), NFSPROC_READDIR, &arg, NULL, cred, 0);
 
 	dprintk("NFS reply readdir: %d\n", status);
 	unlock_kernel();
@@ -481,7 +490,7 @@
 
 	dprintk("NFS call  statfs\n");
 	stat->fattr->valid = 0;
-	status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0);
+	status = nfs2_rpc(server->client, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
 	dprintk("NFS reply statfs: %d\n", status);
 	if (status)
 		goto out;
@@ -504,7 +513,7 @@
 
 	dprintk("NFS call  fsinfo\n");
 	info->fattr->valid = 0;
-	status = rpc_call(server->client, NFSPROC_STATFS, fhandle, &fsinfo, 0);
+	status = nfs2_rpc(server->client, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
 	dprintk("NFS reply fsinfo: %d\n", status);
 	if (status)
 		goto out;
diff -ru a/fs/nfs/unlink.c b/fs/nfs/unlink.c
--- a/fs/nfs/unlink.c	2005-02-06 18:05:04.000000000 +0100
+++ b/fs/nfs/unlink.c	2005-02-14 22:41:49.000000000 +0100
@@ -160,6 +160,7 @@
 	struct nfs_unlinkdata	*data;
 	struct rpc_task	*task;
 	struct rpc_clnt	*clnt = NFS_CLIENT(dir->d_inode);
+	struct rpc_groups fsg = { 1, { dir->d_inode->i_gid } };
 	int		status = -ENOMEM;
 
 	data = kmalloc(sizeof(*data), GFP_KERNEL);
@@ -183,7 +184,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, &fsg, 0);
 
 	rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL);
 	status = 0;
diff -ru a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
--- a/fs/nfsd/nfs4callback.c	2005-02-06 18:05:04.000000000 +0100
+++ b/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;
 }
diff -ru a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
--- a/include/linux/nfs_xdr.h	2005-02-06 18:05:11.000000000 +0100
+++ b/include/linux/nfs_xdr.h	2005-02-14 22:41:49.000000000 +0100
@@ -660,7 +660,7 @@
 struct nfs_access_entry;
 
 /*
- * RPC procedure vector for NFSv2/NFSv3 demuxing
+ * RPC procedure vector for NFSv2/NFSv3/NFSv4 demuxing
  */
 struct nfs_rpc_ops {
 	int	version;		/* Protocol version */
@@ -672,9 +672,9 @@
 	int	(*getattr) (struct nfs_server *, struct nfs_fh *,
 			    struct nfs_fattr *);
 	int	(*setattr) (struct dentry *, struct nfs_fattr *,
-			    struct iattr *);
-	int	(*lookup)  (struct inode *, struct qstr *,
-			    struct nfs_fh *, struct nfs_fattr *);
+			    struct iattr *, struct rpc_groups *);
+	int	(*lookup)  (struct inode *, struct qstr *, struct nfs_fh *,
+			    struct nfs_fattr *, struct rpc_groups *);
 	int	(*access)  (struct inode *, struct nfs_access_entry *);
 	int	(*readlink)(struct inode *, struct page *, unsigned int,
 			    unsigned int);
@@ -682,24 +682,27 @@
 	int	(*write)   (struct nfs_write_data *);
 	int	(*commit)  (struct nfs_write_data *);
 	struct inode *	(*create)  (struct inode *, struct dentry *,
-			    struct iattr *, int);
-	int	(*remove)  (struct inode *, struct qstr *);
+			    struct iattr *, int, struct rpc_groups *);
+	int	(*remove)  (struct inode *, struct qstr *, struct rpc_groups *);
 	int	(*unlink_setup)  (struct rpc_message *,
 			    struct dentry *, struct qstr *);
 	int	(*unlink_done) (struct dentry *, struct rpc_task *);
 	int	(*rename)  (struct inode *, struct qstr *,
-			    struct inode *, struct qstr *);
-	int	(*link)    (struct inode *, struct inode *, struct qstr *);
+			    struct inode *, struct qstr *, struct rpc_groups *);
+	int	(*link)    (struct inode *, struct inode *, struct qstr *,
+			    struct rpc_groups *);
 	int	(*symlink) (struct inode *, struct qstr *, struct qstr *,
 			    struct iattr *, struct nfs_fh *,
-			    struct nfs_fattr *);
+			    struct nfs_fattr *, struct rpc_groups *);
 	int	(*mkdir)   (struct inode *, struct qstr *, struct iattr *,
-			    struct nfs_fh *, struct nfs_fattr *);
-	int	(*rmdir)   (struct inode *, struct qstr *);
+			    struct nfs_fh *, struct nfs_fattr *,
+			    struct rpc_groups *);
+	int	(*rmdir)   (struct inode *, struct qstr *, struct rpc_groups *);
 	int	(*readdir) (struct dentry *, struct rpc_cred *,
 			    u64, struct page *, unsigned int, int);
 	int	(*mknod)   (struct inode *, struct qstr *, struct iattr *,
-			    dev_t, struct nfs_fh *, struct nfs_fattr *);
+			    dev_t, struct nfs_fh *, struct nfs_fattr *,
+			    struct rpc_groups *);
 	int	(*statfs)  (struct nfs_server *, struct nfs_fh *,
 			    struct nfs_fsstat *);
 	int	(*fsinfo)  (struct nfs_server *, struct nfs_fh *,
diff -ru a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
--- a/include/linux/sunrpc/auth.h	2005-02-06 18:05:11.000000000 +0100
+++ b/include/linux/sunrpc/auth.h	2005-02-14 23:07:16.000000000 +0100
@@ -12,6 +12,7 @@
 #ifdef __KERNEL__
 
 #include <linux/config.h>
+#include <linux/sched.h>
 #include <linux/sunrpc/sched.h>
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/xdr.h>
@@ -24,11 +25,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,13 +98,14 @@
 	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 {
 	void			(*crdestroy)(struct rpc_cred *);
 
 	int			(*crmatch)(struct auth_cred *, struct rpc_cred *, int);
-	u32 *			(*crmarshal)(struct rpc_task *, u32 *, int);
+	u32 *			(*crmarshal)(struct rpc_task *, u32 *);
 	int			(*crrefresh)(struct rpc_task *);
 	u32 *			(*crvalidate)(struct rpc_task *, u32 *);
 	int			(*crwrap_req)(struct rpc_task *, kxdrproc_t,
@@ -118,11 +125,8 @@
 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_bindcred(struct rpc_task *);
-void			rpcauth_holdcred(struct rpc_task *);
+struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, struct rpc_groups *, int);
 void			put_rpccred(struct rpc_cred *);
-void			rpcauth_unbindcred(struct rpc_task *);
 u32 *			rpcauth_marshcred(struct rpc_task *, u32 *);
 u32 *			rpcauth_checkverf(struct rpc_task *, u32 *);
 int			rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, u32 *data, void *obj);
diff -ru a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
--- a/include/linux/sunrpc/clnt.h	2004-05-10 04:32:39.000000000 +0200
+++ b/include/linux/sunrpc/clnt.h	2005-02-14 21:36:53.000000000 +0100
@@ -51,7 +51,6 @@
 				cl_intr     : 1,/* interruptible */
 				cl_chatty   : 1,/* be verbose */
 				cl_autobind : 1,/* use getport() */
-				cl_droppriv : 1,/* enable NFS suid hack */
 				cl_oneshot  : 1,/* dispose after use */
 				cl_dead     : 1;/* abandoned */
 
diff -ru a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h
--- a/include/linux/sunrpc/msg_prot.h	2004-05-10 04:32:54.000000000 +0200
+++ b/include/linux/sunrpc/msg_prot.h	2005-02-14 21:36:53.000000000 +0100
@@ -75,6 +75,7 @@
 #define RPC_PMAP_PORT		111
 
 #define RPC_MAXNETNAMELEN	256
+#define RPC_MAXGROUPS		16
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_MSGPROT_H_ */
diff -ru a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
--- a/include/linux/sunrpc/sched.h	2005-02-06 18:05:11.000000000 +0100
+++ b/include/linux/sunrpc/sched.h	2005-02-14 21:36:53.000000000 +0100
@@ -53,9 +53,8 @@
 	struct rpc_message	tk_msg;		/* RPC call info */
 	__u32 *			tk_buffer;	/* XDR buffer */
 	size_t			tk_bufsize;
-	__u8			tk_garb_retry,
-				tk_cred_retry,
-				tk_suid_retry;
+	__u8			tk_garb_retry;
+	__u8			tk_cred_retry;
 
 	unsigned long		tk_cookie;	/* Cookie for batching tasks */
 
@@ -118,9 +117,7 @@
  */
 #define RPC_TASK_ASYNC		0x0001		/* is an async task */
 #define RPC_TASK_SWAPPER	0x0002		/* is swapping in/out */
-#define RPC_TASK_SETUID		0x0004		/* is setuid process */
 #define RPC_TASK_CHILD		0x0008		/* is child of other task */
-#define RPC_CALL_REALUID	0x0010		/* try using real uid */
 #define RPC_CALL_MAJORSEEN	0x0020		/* major timeout seen */
 #define RPC_TASK_ROOTCREDS	0x0040		/* force root creds */
 #define RPC_TASK_DYNAMIC	0x0080		/* task was kmalloc'ed */
@@ -129,7 +126,6 @@
 #define RPC_TASK_NOINTR		0x0400		/* uninterruptible task */
 
 #define RPC_IS_ASYNC(t)		((t)->tk_flags & RPC_TASK_ASYNC)
-#define RPC_IS_SETUID(t)	((t)->tk_flags & RPC_TASK_SETUID)
 #define RPC_IS_CHILD(t)		((t)->tk_flags & RPC_TASK_CHILD)
 #define RPC_IS_SWAPPER(t)	((t)->tk_flags & RPC_TASK_SWAPPER)
 #define RPC_DO_ROOTOVERRIDE(t)	((t)->tk_flags & RPC_TASK_ROOTCREDS)
diff -ru a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h
--- a/include/linux/sunrpc/svcauth.h	2004-08-15 12:38:54.000000000 +0200
+++ b/include/linux/sunrpc/svcauth.h	2005-02-14 21:36:53.000000000 +0100
@@ -16,7 +16,6 @@
 #include <linux/sunrpc/cache.h>
 #include <linux/hash.h>
 
-#define SVC_CRED_NGROUPS	32
 struct svc_cred {
 	uid_t			cr_uid;
 	gid_t			cr_gid;
diff -ru a/net/sunrpc/auth.c b/net/sunrpc/auth.c
--- a/net/sunrpc/auth.c	2005-02-06 18:05:16.000000000 +0100
+++ b/net/sunrpc/auth.c	2005-02-14 22:15:03.000000000 +0100
@@ -247,59 +247,27 @@
 		}
 	}
 
-	return (struct rpc_cred *) cred;
+	return cred;
 }
 
 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);
-	ret = rpcauth_lookup_credcache(auth, &acred, taskflags);
-	put_group_info(current->group_info);
-	return ret;
-}
-
-struct rpc_cred *
-rpcauth_bindcred(struct rpc_task *task)
-{
-	struct rpc_auth *auth = task->tk_auth;
-	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;
+	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);
 
-	dprintk("RPC: %4d looking up %s cred\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name);
-	task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, &acred, task->tk_flags);
-	if (task->tk_msg.rpc_cred == 0)
-		task->tk_status = -ENOMEM;
-	ret = task->tk_msg.rpc_cred;
-	put_group_info(current->group_info);
+	dprintk("RPC:      cred %p\n", ret);
 	return ret;
 }
 
 void
-rpcauth_holdcred(struct rpc_task *task)
-{
-	dprintk("RPC: %4d holding %s cred %p\n",
-		task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
-	if (task->tk_msg.rpc_cred)
-		get_rpccred(task->tk_msg.rpc_cred);
-}
-
-void
 put_rpccred(struct rpc_cred *cred)
 {
 	if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
@@ -314,19 +282,6 @@
 	spin_unlock(&rpc_credcache_lock);
 }
 
-void
-rpcauth_unbindcred(struct rpc_task *task)
-{
-	struct rpc_auth	*auth = task->tk_auth;
-	struct rpc_cred	*cred = task->tk_msg.rpc_cred;
-
-	dprintk("RPC: %4d releasing %s cred %p\n",
-		task->tk_pid, auth->au_ops->au_name, cred);
-
-	put_rpccred(cred);
-	task->tk_msg.rpc_cred = NULL;
-}
-
 u32 *
 rpcauth_marshcred(struct rpc_task *task, u32 *p)
 {
@@ -335,8 +290,7 @@
 
 	dprintk("RPC: %4d marshaling %s cred %p\n",
 		task->tk_pid, auth->au_ops->au_name, cred);
-	return cred->cr_ops->crmarshal(task, p,
-				task->tk_flags & RPC_CALL_REALUID);
+	return cred->cr_ops->crmarshal(task, p);
 }
 
 u32 *
diff -ru a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
--- a/net/sunrpc/auth_gss/auth_gss.c	2005-02-06 18:05:17.000000000 +0100
+++ b/net/sunrpc/auth_gss/auth_gss.c	2005-02-14 21:36:53.000000000 +0100
@@ -63,8 +63,6 @@
 # define RPCDBG_FACILITY	RPCDBG_AUTH
 #endif
 
-#define NFS_NGROUPS	16
-
 #define GSS_CRED_EXPIRE		(60 * HZ)	/* XXX: reasonable? */
 #define GSS_CRED_SLACK		1024		/* XXX: unused */
 /* length of a krb5 verifier (48), plus data added before arguments when
@@ -692,7 +690,7 @@
 * Maybe we should keep a cached credential for performance reasons.
 */
 static u32 *
-gss_marshal(struct rpc_task *task, u32 *p, int ruid)
+gss_marshal(struct rpc_task *task, u32 *p)
 {
 	struct rpc_cred *cred = task->tk_msg.rpc_cred;
 	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred,
diff -ru a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c
--- a/net/sunrpc/auth_null.c	2005-02-06 18:05:17.000000000 +0100
+++ b/net/sunrpc/auth_null.c	2005-02-14 21:36:53.000000000 +0100
@@ -84,7 +84,7 @@
  * Marshal credential.
  */
 static u32 *
-nul_marshal(struct rpc_task *task, u32 *p, int ruid)
+nul_marshal(struct rpc_task *task, u32 *p)
 {
 	*p++ = htonl(RPC_AUTH_NULL);
 	*p++ = 0;
diff -ru a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
--- a/net/sunrpc/auth_unix.c	2005-02-06 18:05:17.000000000 +0100
+++ b/net/sunrpc/auth_unix.c	2005-02-14 23:38:29.000000000 +0100
@@ -14,14 +14,10 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
 
-#define NFS_NGROUPS	16
-
 struct unx_cred {
 	struct rpc_cred		uc_base;
 	gid_t			uc_gid;
-	uid_t			uc_puid;		/* process uid */
-	gid_t			uc_pgid;		/* process gid */
-	gid_t			uc_gids[NFS_NGROUPS];
+	gid_t			uc_gids[RPC_MAXGROUPS];
 };
 #define uc_uid			uc_base.cr_uid
 #define uc_count		uc_base.cr_count
@@ -34,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;
@@ -67,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,28 +74,58 @@
 	atomic_set(&cred->uc_count, 0);
 	cred->uc_flags = RPCAUTH_CRED_UPTODATE;
 	if (flags & RPC_TASK_ROOTCREDS) {
-		cred->uc_uid = cred->uc_puid = 0;
-		cred->uc_gid = cred->uc_pgid = 0;
+		cred->uc_uid = 0;
+		cred->uc_gid = 0;
 		cred->uc_gids[0] = NOGROUP;
 	} else {
-		int groups = acred->group_info->ngroups;
-		if (groups > NFS_NGROUPS)
-			groups = NFS_NGROUPS;
-
+		int n = acred->rg.ngroups;
 		cred->uc_uid = acred->uid;
 		cred->uc_gid = acred->gid;
-		cred->uc_puid = current->uid;
-		cred->uc_pgid = current->gid;
-		for (i = 0; i < groups; i++)
-			cred->uc_gids[i] = GROUP_AT(acred->group_info, i);
-		if (i < NFS_NGROUPS)
-		  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;
+
+	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)
 {
@@ -115,27 +141,22 @@
 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
-		 || cred->uc_puid != current->uid
-		 || cred->uc_pgid != current->gid)
+		 || cred->uc_gid != acred->gid)
 			return 0;
 
-		groups = acred->group_info->ngroups;
-		if (groups > NFS_NGROUPS)
-			groups = NFS_NGROUPS;
-		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_puid == 0
-	     && cred->uc_gid == 0 && cred->uc_pgid == 0
+	return (cred->uc_uid == 0
+	     && cred->uc_gid == 0
 	     && cred->uc_gids[0] == (gid_t) NOGROUP);
 }
 
@@ -144,7 +165,7 @@
  * Maybe we should keep a cached credential for performance reasons.
  */
 static u32 *
-unx_marshal(struct rpc_task *task, u32 *p, int ruid)
+unx_marshal(struct rpc_task *task, u32 *p)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 	struct unx_cred	*cred = (struct unx_cred *) task->tk_msg.rpc_cred;
@@ -160,16 +181,10 @@
 	 */
 	p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
 
-	/* Note: we don't use real uid if it involves raising privilege */
-	if (ruid && cred->uc_puid != 0 && cred->uc_pgid != 0) {
-		*p++ = htonl((u32) cred->uc_puid);
-		*p++ = htonl((u32) cred->uc_pgid);
-	} else {
-		*p++ = htonl((u32) cred->uc_uid);
-		*p++ = htonl((u32) cred->uc_gid);
-	}
+	*p++ = htonl((u32) cred->uc_uid);
+	*p++ = htonl((u32) cred->uc_gid);
 	hold = p++;
-	for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
+	for (i = 0; i < RPC_MAXGROUPS && 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 */
@@ -224,6 +239,7 @@
 	.create		= unx_create,
 	.destroy	= unx_destroy,
 	.crcreate	= unx_create_cred,
+	.cr_add_groups	= unx_add_groups,
 };
 
 static
diff -ru a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
--- a/net/sunrpc/clnt.c	2005-02-06 18:05:17.000000000 +0100
+++ b/net/sunrpc/clnt.c	2005-02-14 21:38:41.000000000 +0100
@@ -425,11 +425,15 @@
 {
 	task->tk_msg   = *msg;
 	task->tk_flags |= flags;
-	/* Bind the user cred */
-	if (task->tk_msg.rpc_cred != NULL) {
-		rpcauth_holdcred(task);
-	} else
-		rpcauth_bindcred(task);
+
+	if (task->tk_msg.rpc_cred) {
+		/* 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, NULL, task->tk_flags);
+		if (task->tk_msg.rpc_cred == NULL)
+			task->tk_status = -ENOMEM;
+	}
 
 	if (task->tk_status == 0)
 		task->tk_action = call_start;
@@ -871,21 +875,6 @@
 		goto out_retry;
 	}
 
-	/*
-	 * The following is an NFS-specific hack to cater for setuid
-	 * processes whose uid is mapped to nobody on the server.
-	 */
-	if (task->tk_client->cl_droppriv && 
-            (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
-		if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
-			dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
-			task->tk_flags ^= RPC_CALL_REALUID;
-			task->tk_action = call_bind;
-			task->tk_suid_retry--;
-			goto out_retry;
-		}
-	}
-
 	task->tk_action = NULL;
 
 	if (decode)
diff -ru a/net/sunrpc/sched.c b/net/sunrpc/sched.c
--- a/net/sunrpc/sched.c	2005-02-06 18:05:17.000000000 +0100
+++ b/net/sunrpc/sched.c	2005-02-14 21:37:34.000000000 +0100
@@ -747,13 +747,10 @@
 	task->tk_client = clnt;
 	task->tk_flags  = flags;
 	task->tk_exit   = callback;
-	if (current->uid != current->fsuid || current->gid != current->fsgid)
-		task->tk_flags |= RPC_TASK_SETUID;
 
 	/* Initialize retry counters */
 	task->tk_garb_retry = 2;
 	task->tk_cred_retry = 2;
-	task->tk_suid_retry = 1;
 
 	task->tk_priority = RPC_PRIORITY_NORMAL;
 	task->tk_cookie = (unsigned long)current;
@@ -854,8 +851,10 @@
 	/* Release resources */
 	if (task->tk_rqstp)
 		xprt_release(task);
-	if (task->tk_msg.rpc_cred)
-		rpcauth_unbindcred(task);
+	if (task->tk_msg.rpc_cred) {
+		put_rpccred(task->tk_msg.rpc_cred);
+		task->tk_msg.rpc_cred = NULL;
+	}
 	rpc_free(task);
 	if (task->tk_client) {
 		rpc_release_client(task->tk_client);
diff -ru a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
--- a/net/sunrpc/svcauth_unix.c	2005-02-06 18:05:17.000000000 +0100
+++ b/net/sunrpc/svcauth_unix.c	2005-02-14 21:36:53.000000000 +0100
@@ -459,7 +459,7 @@
 	cred->cr_uid = ntohl(svc_getu32(argv));		/* uid */
 	cred->cr_gid = ntohl(svc_getu32(argv));		/* gid */
 	slen = ntohl(svc_getu32(argv));			/* gids length */
-	if (slen > 16 || (len -= (slen + 2)*4) < 0)
+	if (slen > RPC_MAXGROUPS || (len -= (slen + 2)*4) < 0)
 		goto badcred;
 	cred->cr_group_info = groups_alloc(slen);
 	if (cred->cr_group_info == NULL)
