Commits

Remy Blank committed fbbffe6

Added nfs-ngroups patches for 2.6.36 - 2.6.39.

Comments (0)

Files changed (5)

 2.6.33-nfs-ngroups-4.58.patch           =sys-kernel/gentoo-sources-2.6.33*
 2.6.34-nfs-ngroups-4.58.patch           =sys-kernel/gentoo-sources-2.6.34*
 2.6.35-nfs-ngroups-4.58.patch           =sys-kernel/gentoo-sources-2.6.35*
+2.6.36-nfs-ngroups-4.58.patch           =sys-kernel/gentoo-sources-2.6.36*
+2.6.37.1-nfs-ngroups-4.58.patch         =sys-kernel/gentoo-sources-2.6.37*
+2.6.38-nfs-ngroups-4.58.patch           =sys-kernel/gentoo-sources-2.6.38*
+2.6.39-nfs-ngroups-4.58.patch           =sys-kernel/gentoo-sources-2.6.39*
 2.6.29-em28xx-amux.patch                =sys-kernel/gentoo-sources-2.6.29*
 2.6.31-em28xx-amux.patch                =sys-kernel/gentoo-sources-2.6.31*
 2.6.34-em28xx-amux.patch                >=sys-kernel/gentoo-sources-2.6.34

patches/2.6.36-nfs-ngroups-4.58.patch

+diff -Nurp a/fs/nfs/dir.c b/fs/nfs/dir.c
+--- a/fs/nfs/dir.c	2010-11-02 17:39:16.550412363 +0100
++++ b/fs/nfs/dir.c	2010-11-08 16:36:46.531873574 +0100
+@@ -790,6 +790,7 @@ static int nfs_lookup_revalidate(struct
+ 	struct dentry *parent;
+ 	struct nfs_fh *fhandle = NULL;
+ 	struct nfs_fattr *fattr = NULL;
++	struct rpc_groups fsg;
+ 	int error;
+ 
+ 	parent = dget_parent(dentry);
+@@ -829,7 +830,9 @@ static int nfs_lookup_revalidate(struct
+ 	if (fhandle == NULL || fattr == NULL)
+ 		goto out_error;
+ 
+-	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))
+@@ -943,6 +946,7 @@ static struct dentry *nfs_lookup(struct
+ 	struct inode *inode = NULL;
+ 	struct nfs_fh *fhandle = NULL;
+ 	struct nfs_fattr *fattr = NULL;
++	struct rpc_groups fsg;
+ 	int error;
+ 
+ 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+@@ -974,7 +978,9 @@ static struct dentry *nfs_lookup(struct
+ 	parent = dentry->d_parent;
+ 	/* Protect against concurrent sillydeletes */
+ 	nfs_block_sillyrename(parent);
+-	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) {
+@@ -1209,7 +1215,7 @@ out_renew:
+  * Code common to create, mkdir, and mknod.
+  */
+ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
+-				struct nfs_fattr *fattr)
++				struct nfs_fattr *fattr, struct rpc_groups *fsg)
+ {
+ 	struct dentry *parent = dget_parent(dentry);
+ 	struct inode *dir = parent->d_inode;
+@@ -1222,7 +1228,7 @@ int nfs_instantiate(struct dentry *dentr
+ 	if (dentry->d_inode)
+ 		goto out;
+ 	if (fhandle->size == 0) {
+-		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_error;
+ 	}
+@@ -1259,6 +1265,7 @@ static int nfs_create(struct inode *dir,
+ 	struct iattr attr;
+ 	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);
+@@ -1269,7 +1276,7 @@ static int nfs_create(struct inode *dir,
+ 	if ((nd->flags & LOOKUP_CREATE) != 0)
+ 		open_flags = nd->intent.open.flags;
+ 
+-	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
++	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd, &fsg);
+ 	if (error != 0)
+ 		goto out_err;
+ 	return 0;
+@@ -1286,6 +1293,7 @@ nfs_mknod(struct inode *dir, struct dent
+ {
+ 	struct iattr attr;
+ 	int status;
++	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);
+@@ -1296,7 +1304,7 @@ nfs_mknod(struct inode *dir, struct dent
+ 	attr.ia_mode = mode;
+ 	attr.ia_valid = ATTR_MODE;
+ 
+-	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
++	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev, &fsg);
+ 	if (status != 0)
+ 		goto out_err;
+ 	return 0;
+@@ -1312,6 +1320,7 @@ static int nfs_mkdir(struct inode *dir,
+ {
+ 	struct iattr attr;
+ 	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);
+@@ -1319,7 +1328,7 @@ static int nfs_mkdir(struct inode *dir,
+ 	attr.ia_valid = ATTR_MODE;
+ 	attr.ia_mode = mode | S_IFDIR;
+ 
+-	error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
++	error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr, &fsg);
+ 	if (error != 0)
+ 		goto out_err;
+ 	return 0;
+@@ -1337,11 +1346,12 @@ static void nfs_dentry_handle_enoent(str
+ 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);
+ 
+-	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)
+ 		clear_nlink(dentry->d_inode);
+@@ -1361,6 +1371,7 @@ static int nfs_sillyrename(struct inode
+ 	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, 
+@@ -1405,11 +1416,11 @@ static int nfs_sillyrename(struct inode
+ 	qsilly.len  = strlen(silly);
+ 	if (dentry->d_inode) {
+ 		error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
+-				dir, &qsilly);
++				dir, &qsilly, &fsg);
+ 		nfs_mark_for_revalidate(dentry->d_inode);
+ 	} else
+ 		error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
+-				dir, &qsilly);
++				dir, &qsilly, &fsg);
+ 	if (!error) {
+ 		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+ 		d_move(dentry, sdentry);
+@@ -1432,6 +1443,7 @@ static int nfs_safe_remove(struct dentry
+ {
+ 	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",
+@@ -1445,13 +1457,13 @@ static int nfs_safe_remove(struct dentry
+ 
+ 	if (inode != NULL) {
+ 		nfs_inode_return_delegation(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)
+ 			nfs_drop_nlink(inode);
+ 		nfs_mark_for_revalidate(inode);
+ 	} else
+-		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
++		error = NFS_PROTO(dir)->remove(dir, &dentry->d_name, &fsg);
+ 	if (error == -ENOENT)
+ 		nfs_dentry_handle_enoent(dentry);
+ out:
+@@ -1518,6 +1530,7 @@ static int nfs_symlink(struct inode *dir
+ 	struct iattr attr;
+ 	unsigned int pathlen = strlen(symname);
+ 	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);
+@@ -1538,7 +1551,7 @@ static int nfs_symlink(struct inode *dir
+ 		memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
+ 	kunmap_atomic(kaddr, KM_USER0);
+ 
+-	error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
++	error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr, &fsg);
+ 	if (error != 0) {
+ 		dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
+ 			dir->i_sb->s_id, dir->i_ino,
+@@ -1569,6 +1582,7 @@ static int
+ 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",
+@@ -1578,7 +1592,7 @@ nfs_link(struct dentry *old_dentry, stru
+ 	nfs_inode_return_delegation(inode);
+ 
+ 	d_drop(dentry);
+-	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
++	error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name, &fsg);
+ 	if (error == 0) {
+ 		atomic_inc(&inode->i_count);
+ 		d_add(dentry, inode);
+@@ -1616,6 +1630,14 @@ static int nfs_rename(struct inode *old_
+ 	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;
+ 
+ 	dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
+@@ -1664,7 +1686,7 @@ static int nfs_rename(struct inode *old_
+ 		nfs_inode_return_delegation(new_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_mark_for_revalidate(old_inode);
+ out:
+ 	if (rehash)
+@@ -1908,6 +1930,8 @@ static int nfs_do_access(struct inode *i
+ 	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) {
+ 		if (status == -ESTALE) {
+ 			nfs_zap_caches(inode);
+@@ -1977,7 +2001,12 @@ force_lookup:
+ 	if (!NFS_PROTO(inode)->access)
+ 		goto out_notsup;
+ 
+-	cred = rpc_lookup_cred();
++	if (NFS_PROTO(inode)->version > 3)
++		cred = rpc_lookup_cred(NULL);
++	else {
++		struct rpc_groups fsg = { 1, { inode->i_gid } };
++		cred = rpc_lookup_cred(&fsg);
++	}
+ 	if (!IS_ERR(cred)) {
+ 		res = nfs_do_access(inode, cred, mask);
+ 		put_rpccred(cred);
+diff -Nurp a/fs/nfs/inode.c b/fs/nfs/inode.c
+--- a/fs/nfs/inode.c	2010-11-02 17:39:16.635411165 +0100
++++ b/fs/nfs/inode.c	2010-11-08 16:36:46.544873390 +0100
+@@ -401,6 +401,7 @@ nfs_setattr(struct dentry *dentry, struc
+ {
+ 	struct inode *inode = dentry->d_inode;
+ 	struct nfs_fattr *fattr;
++	struct rpc_groups fsg;
+ 	int error = -ENOMEM;
+ 
+ 	nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
+@@ -431,7 +432,11 @@ nfs_setattr(struct dentry *dentry, struc
+ 	 */
+ 	if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
+ 		nfs_inode_return_delegation(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);
+ 	nfs_free_fattr(fattr);
+@@ -727,7 +732,12 @@ int nfs_open(struct inode *inode, struct
+ 	struct nfs_open_context *ctx;
+ 	struct rpc_cred *cred;
+ 
+-	cred = rpc_lookup_cred();
++	if (NFS_PROTO(inode)->version > 3)
++		cred = rpc_lookup_cred(NULL);
++	else {
++		struct rpc_groups fsg = { 1, { inode->i_gid } };
++		cred = rpc_lookup_cred(&fsg);
++	}
+ 	if (IS_ERR(cred))
+ 		return PTR_ERR(cred);
+ 	ctx = alloc_nfs_open_context(&filp->f_path, cred);
+diff -Nurp a/fs/nfs/namespace.c b/fs/nfs/namespace.c
+--- a/fs/nfs/namespace.c	2010-08-02 10:27:04.000000000 +0200
++++ b/fs/nfs/namespace.c	2010-11-08 16:36:46.567873065 +0100
+@@ -107,6 +107,7 @@ static void * nfs_follow_mountpoint(stru
+ 	struct dentry *parent;
+ 	struct nfs_fh *fh = NULL;
+ 	struct nfs_fattr *fattr = NULL;
++	struct rpc_groups fsg;
+ 	int err;
+ 
+ 	dprintk("--> nfs_follow_mountpoint()\n");
+@@ -127,9 +128,11 @@ static void * nfs_follow_mountpoint(stru
+ 
+ 	/* Look it up again */
+ 	parent = dget_parent(nd->path.dentry);
++	fsg.ngroups = 1;
++	fsg.groups[0] = parent->d_inode->i_gid;
+ 	err = server->nfs_client->rpc_ops->lookup(parent->d_inode,
+ 						  &nd->path.dentry->d_name,
+-						  fh, fattr);
++						  fh, fattr, &fsg);
+ 	dput(parent);
+ 	if (err != 0)
+ 		goto out_err;
+diff -Nurp a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
+--- a/fs/nfs/nfs3proc.c	2010-08-02 10:27:04.000000000 +0200
++++ b/fs/nfs/nfs3proc.c	2010-11-08 16:36:46.577872925 +0100
+@@ -25,11 +25,19 @@
+ 
+ /* A wrapper to handle the EJUKEBOX and EKEYEXPIRED error messages */
+ 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)
+ {
++	struct rpc_message msg = {
++		.rpc_proc	= &nfs3_procedures[proc],
++		.rpc_argp	= argp,
++		.rpc_resp	= resp,
++		.rpc_cred	= cred,
++	};
+ 	int res;
++
+ 	do {
+-		res = rpc_call_sync(clnt, msg, flags);
++		res = rpc_call_sync(clnt, &msg, flags);
+ 		if (res != -EJUKEBOX && res != -EKEYEXPIRED)
+ 			break;
+ 		schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
+@@ -38,7 +46,20 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt,
+ 	return res;
+ }
+ 
+-#define rpc_call_sync(clnt, msg, flags)	nfs3_rpc_wrapper(clnt, msg, flags)
++static int
++nfs3_rpc(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
++	 struct rpc_groups *fsg)
++{
++	struct rpc_cred	*cred;
++	int		res;
++
++	cred = rpcauth_lookupcred(clnt->cl_auth, fsg, 0);
++	if (IS_ERR(cred))
++		return PTR_ERR(cred);
++	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, struct inode *inode)
+@@ -57,21 +78,14 @@ static int
+ do_proc_get_root(struct rpc_clnt *client, struct nfs_fh *fhandle,
+ 		 struct nfs_fsinfo *info)
+ {
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_FSINFO],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= info,
+-	};
+ 	int	status;
+ 
+ 	dprintk("%s: call  fsinfo\n", __func__);
+ 	nfs_fattr_init(info->fattr);
+-	status = rpc_call_sync(client, &msg, 0);
++	status = nfs3_rpc(client, NFS3PROC_FSINFO, fhandle, info, NULL);
+ 	dprintk("%s: reply fsinfo: %d\n", __func__, status);
+ 	if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
+-		msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
+-		msg.rpc_resp = info->fattr;
+-		status = rpc_call_sync(client, &msg, 0);
++		status = nfs3_rpc(client, NFS3PROC_GETATTR, fhandle, info->fattr, NULL);
+ 		dprintk("%s: reply getattr: %d\n", __func__, status);
+ 	}
+ 	return status;
+@@ -99,41 +113,41 @@ static int
+ nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+ 		struct nfs_fattr *fattr)
+ {
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_GETATTR],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= fattr,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  getattr\n");
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(server->client, &msg, 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 = {
+ 		.fh		= NFS_FH(inode),
+ 		.sattr		= sattr,
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_SETATTR],
+-		.rpc_argp	= &arg,
+-		.rpc_resp	= fattr,
+-	};
+ 	int	status;
++	struct rpc_cred	*cred;
++	struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ 
+ 	dprintk("NFS call  setattr\n");
+-	if (sattr->ia_valid & ATTR_FILE)
+-		msg.rpc_cred = nfs_file_cred(sattr->ia_file);
++	if (sattr->ia_valid & ATTR_FILE) {
++		cred = nfs_file_cred(sattr->ia_file);
++		get_rpccred(cred);
++	} else {
++		/* truncate() requires write access */
++		cred = rpcauth_lookupcred(clnt->cl_auth, fsg, 0);
++		if (IS_ERR(cred))
++			return PTR_ERR(cred);
++	}
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++	status = nfs3_rpc_call(clnt, NFS3PROC_SETATTR, &arg, fattr, cred, 0);
++	put_rpccred(cred);
+ 	if (status == 0)
+ 		nfs_setattr_update_inode(inode, sattr);
+ 	dprintk("NFS reply setattr: %d\n", status);
+@@ -142,7 +156,8 @@ nfs3_proc_setattr(struct dentry *dentry,
+ 
+ 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 nfs3_diropargs	arg = {
+ 		.fh		= NFS_FH(dir),
+@@ -153,11 +168,6 @@ nfs3_proc_lookup(struct inode *dir, stru
+ 		.fh		= fhandle,
+ 		.fattr		= fattr
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_LOOKUP],
+-		.rpc_argp	= &arg,
+-		.rpc_resp	= &res,
+-	};
+ 	int			status;
+ 
+ 	dprintk("NFS call  lookup %s\n", name->name);
+@@ -166,13 +176,11 @@ nfs3_proc_lookup(struct inode *dir, stru
+ 		return -ENOMEM;
+ 
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, fsg);
+ 	nfs_refresh_inode(dir, res.dir_attr);
+ 	if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR)) {
+-		msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
+-		msg.rpc_argp = fhandle;
+-		msg.rpc_resp = fattr;
+-		status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++		status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_GETATTR,
++				  fhandle, fattr, NULL);
+ 	}
+ 	nfs_free_fattr(res.dir_attr);
+ 	dprintk("NFS reply lookup: %d\n", status);
+@@ -185,12 +193,6 @@ static int nfs3_proc_access(struct inode
+ 		.fh		= NFS_FH(inode),
+ 	};
+ 	struct nfs3_accessres	res;
+-	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 = -ENOMEM;
+ 
+@@ -214,7 +216,8 @@ static int nfs3_proc_access(struct inode
+ 	if (res.fattr == NULL)
+ 		goto out;
+ 
+-	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, res.fattr);
+ 	if (status == 0) {
+ 		entry->mask = 0;
+@@ -241,19 +244,14 @@ static int nfs3_proc_readlink(struct ino
+ 		.pglen		= pglen,
+ 		.pages		= &page
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_READLINK],
+-		.rpc_argp	= &args,
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  readlink\n");
+ 	fattr = nfs_alloc_fattr();
+ 	if (fattr == NULL)
+ 		goto out;
+-	msg.rpc_resp = fattr;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(inode), NFS3PROC_READLINK, &args, fattr, NULL);
+ 	nfs_refresh_inode(inode, fattr);
+ 	nfs_free_fattr(fattr);
+ out:
+@@ -262,7 +260,8 @@ out:
+ }
+ 
+ struct nfs3_createdata {
+-	struct rpc_message msg;
++	u32 proc;
++	struct rpc_groups *fsg;
+ 	union {
+ 		struct nfs3_createargs create;
+ 		struct nfs3_mkdirargs mkdir;
+@@ -281,8 +280,6 @@ static struct nfs3_createdata *nfs3_allo
+ 
+ 	data = kzalloc(sizeof(*data), GFP_KERNEL);
+ 	if (data != NULL) {
+-		data->msg.rpc_argp = &data->arg;
+-		data->msg.rpc_resp = &data->res;
+ 		data->res.fh = &data->fh;
+ 		data->res.fattr = &data->fattr;
+ 		data->res.dir_attr = &data->dir_attr;
+@@ -296,10 +293,10 @@ static int nfs3_do_create(struct inode *
+ {
+ 	int status;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(dir), data->proc, &data->arg, &data->res, data->fsg);
+ 	nfs_post_op_update_inode(dir, data->res.dir_attr);
+ 	if (status == 0)
+-		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
++		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->fsg);
+ 	return status;
+ }
+ 
+@@ -313,7 +310,7 @@ static void nfs3_free_createdata(struct
+  */
+ static int
+ nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
+-		 int flags, struct nameidata *nd)
++		 int flags, struct nameidata *nd, struct rpc_groups *fsg)
+ {
+ 	struct nfs3_createdata *data;
+ 	mode_t mode = sattr->ia_mode;
+@@ -325,7 +322,8 @@ nfs3_proc_create(struct inode *dir, stru
+ 	if (data == NULL)
+ 		goto out;
+ 
+-	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_CREATE];
++	data->proc = NFS3PROC_CREATE;
++	data->fsg = fsg;
+ 	data->arg.create.fh = NFS_FH(dir);
+ 	data->arg.create.name = dentry->d_name.name;
+ 	data->arg.create.len = dentry->d_name.len;
+@@ -379,7 +377,7 @@ nfs3_proc_create(struct inode *dir, stru
+ 		/* Note: we could use a guarded setattr here, but I'm
+ 		 * not sure this buys us anything (and I'd have
+ 		 * to revamp the NFSv3 XDR code) */
+-		status = nfs3_proc_setattr(dentry, data->res.fattr, sattr);
++		status = nfs3_proc_setattr(dentry, data->res.fattr, sattr, NULL);
+ 		nfs_post_op_update_inode(dentry->d_inode, data->res.fattr);
+ 		dprintk("NFS reply setattr (post-create): %d\n", status);
+ 		if (status != 0)
+@@ -393,7 +391,7 @@ out:
+ }
+ 
+ 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_removeargs arg = {
+ 		.fh = NFS_FH(dir),
+@@ -401,11 +399,6 @@ nfs3_proc_remove(struct inode *dir, stru
+ 		.name.name = name->name,
+ 	};
+ 	struct nfs_removeres res;
+-	struct rpc_message msg = {
+-		.rpc_proc = &nfs3_procedures[NFS3PROC_REMOVE],
+-		.rpc_argp = &arg,
+-		.rpc_resp = &res,
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  remove %s\n", name->name);
+@@ -413,7 +406,7 @@ nfs3_proc_remove(struct inode *dir, stru
+ 	if (res.dir_attr == NULL)
+ 		goto out;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_REMOVE, &arg, &res, fsg);
+ 	nfs_post_op_update_inode(dir, res.dir_attr);
+ 	nfs_free_fattr(res.dir_attr);
+ out:
+@@ -440,7 +433,8 @@ nfs3_proc_unlink_done(struct rpc_task *t
+ 
+ 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 nfs3_renameargs	arg = {
+ 		.fromfh		= NFS_FH(old_dir),
+@@ -451,11 +445,6 @@ nfs3_proc_rename(struct inode *old_dir,
+ 		.tolen		= new_name->len
+ 	};
+ 	struct nfs3_renameres res;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_RENAME],
+-		.rpc_argp	= &arg,
+-		.rpc_resp	= &res,
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
+@@ -465,7 +454,7 @@ nfs3_proc_rename(struct inode *old_dir,
+ 	if (res.fromattr == NULL || res.toattr == NULL)
+ 		goto out;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(old_dir), NFS3PROC_RENAME, &arg, &res, fsg);
+ 	nfs_post_op_update_inode(old_dir, res.fromattr);
+ 	nfs_post_op_update_inode(new_dir, res.toattr);
+ out:
+@@ -476,7 +465,8 @@ out:
+ }
+ 
+ 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 nfs3_linkargs	arg = {
+ 		.fromfh		= NFS_FH(inode),
+@@ -485,11 +475,6 @@ nfs3_proc_link(struct inode *inode, stru
+ 		.tolen		= name->len
+ 	};
+ 	struct nfs3_linkres	res;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_LINK],
+-		.rpc_argp	= &arg,
+-		.rpc_resp	= &res,
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  link %s\n", name->name);
+@@ -498,7 +483,7 @@ nfs3_proc_link(struct inode *inode, stru
+ 	if (res.fattr == NULL || res.dir_attr == NULL)
+ 		goto out;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(inode), NFS3PROC_LINK, &arg, &res, fsg);
+ 	nfs_post_op_update_inode(dir, res.dir_attr);
+ 	nfs_post_op_update_inode(inode, res.fattr);
+ out:
+@@ -510,7 +495,7 @@ out:
+ 
+ static int
+ nfs3_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
+-		  unsigned int len, struct iattr *sattr)
++		  unsigned int len, struct iattr *sattr, struct rpc_groups *fsg)
+ {
+ 	struct nfs3_createdata *data;
+ 	int status = -ENOMEM;
+@@ -523,7 +508,8 @@ nfs3_proc_symlink(struct inode *dir, str
+ 	data = nfs3_alloc_createdata();
+ 	if (data == NULL)
+ 		goto out;
+-	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_SYMLINK];
++	data->proc = NFS3PROC_SYMLINK;
++	data->fsg = fsg;
+ 	data->arg.symlink.fromfh = NFS_FH(dir);
+ 	data->arg.symlink.fromname = dentry->d_name.name;
+ 	data->arg.symlink.fromlen = dentry->d_name.len;
+@@ -540,7 +526,8 @@ out:
+ }
+ 
+ static int
+-nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
++nfs3_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
++		struct rpc_groups *fsg)
+ {
+ 	struct nfs3_createdata *data;
+ 	int mode = sattr->ia_mode;
+@@ -554,7 +541,8 @@ nfs3_proc_mkdir(struct inode *dir, struc
+ 	if (data == NULL)
+ 		goto out;
+ 
+-	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKDIR];
++	data->proc = NFS3PROC_MKDIR;
++	data->fsg = fsg;
+ 	data->arg.mkdir.fh = NFS_FH(dir);
+ 	data->arg.mkdir.name = dentry->d_name.name;
+ 	data->arg.mkdir.len = dentry->d_name.len;
+@@ -572,7 +560,7 @@ out:
+ }
+ 
+ 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 = {
+@@ -580,10 +568,6 @@ nfs3_proc_rmdir(struct inode *dir, struc
+ 		.name		= name->name,
+ 		.len		= name->len
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_RMDIR],
+-		.rpc_argp	= &arg,
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  rmdir %s\n", name->name);
+@@ -591,8 +575,7 @@ nfs3_proc_rmdir(struct inode *dir, struc
+ 	if (dir_attr == NULL)
+ 		goto out;
+ 
+-	msg.rpc_resp = dir_attr;
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs3_rpc(NFS_CLIENT(dir), NFS3PROC_RMDIR, &arg, dir_attr, fsg);
+ 	nfs_post_op_update_inode(dir, dir_attr);
+ 	nfs_free_fattr(dir_attr);
+ out:
+@@ -627,16 +610,11 @@ nfs3_proc_readdir(struct dentry *dentry,
+ 		.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 = -ENOMEM;
++	u32			proc = NFS3PROC_READDIR;
+ 
+ 	if (plus)
+-		msg.rpc_proc = &nfs3_procedures[NFS3PROC_READDIRPLUS];
++		proc = NFS3PROC_READDIRPLUS;
+ 
+ 	dprintk("NFS call  readdir%s %d\n",
+ 			plus? "plus" : "", (unsigned int) cookie);
+@@ -645,7 +623,7 @@ nfs3_proc_readdir(struct dentry *dentry,
+ 	if (res.dir_attr == NULL)
+ 		goto out;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs3_rpc_call(NFS_CLIENT(dir), proc, &arg, &res, cred, 0);
+ 
+ 	nfs_invalidate_atime(dir);
+ 	nfs_refresh_inode(dir, res.dir_attr);
+@@ -658,7 +636,7 @@ out:
+ 
+ static int
+ nfs3_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
+-		dev_t rdev)
++		dev_t rdev, struct rpc_groups *fsg)
+ {
+ 	struct nfs3_createdata *data;
+ 	mode_t mode = sattr->ia_mode;
+@@ -673,7 +651,8 @@ nfs3_proc_mknod(struct inode *dir, struc
+ 	if (data == NULL)
+ 		goto out;
+ 
+-	data->msg.rpc_proc = &nfs3_procedures[NFS3PROC_MKNOD];
++	data->proc = NFS3PROC_MKNOD;
++	data->fsg = fsg;
+ 	data->arg.mknod.fh = NFS_FH(dir);
+ 	data->arg.mknod.name = dentry->d_name.name;
+ 	data->arg.mknod.len = dentry->d_name.len;
+@@ -712,16 +691,11 @@ static int
+ nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+ 		 struct nfs_fsstat *stat)
+ {
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_FSSTAT],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= stat,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  fsstat\n");
+ 	nfs_fattr_init(stat->fattr);
+-	status = rpc_call_sync(server->client, &msg, 0);
++	status = nfs3_rpc(server->client, NFS3PROC_FSSTAT, fhandle, stat, NULL);
+ 	dprintk("NFS reply statfs: %d\n", status);
+ 	return status;
+ }
+@@ -730,16 +704,11 @@ static int
+ do_proc_fsinfo(struct rpc_clnt *client, struct nfs_fh *fhandle,
+ 		 struct nfs_fsinfo *info)
+ {
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_FSINFO],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= info,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  fsinfo\n");
+ 	nfs_fattr_init(info->fattr);
+-	status = rpc_call_sync(client, &msg, 0);
++	status = nfs3_rpc(client, NFS3PROC_FSINFO, fhandle, info, NULL);
+ 	dprintk("NFS reply fsinfo: %d\n", status);
+ 	return status;
+ }
+@@ -764,16 +733,11 @@ static int
+ nfs3_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
+ 		   struct nfs_pathconf *info)
+ {
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs3_procedures[NFS3PROC_PATHCONF],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= info,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  pathconf\n");
+ 	nfs_fattr_init(info->fattr);
+-	status = rpc_call_sync(server->client, &msg, 0);
++	status = nfs3_rpc(server->client, NFS3PROC_PATHCONF, fhandle, info, NULL);
+ 	dprintk("NFS reply pathconf: %d\n", status);
+ 	return status;
+ }
+diff -Nurp a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
+--- a/fs/nfs/nfs4proc.c	2010-11-02 17:39:16.679410545 +0100
++++ b/fs/nfs/nfs4proc.c	2010-11-08 16:36:46.606872514 +0100
+@@ -2050,7 +2050,7 @@ nfs4_atomic_open(struct inode *dir, stru
+ 		BUG_ON(open_flags & O_CREAT);
+ 	}
+ 
+-	cred = rpc_lookup_cred();
++	cred = rpc_lookup_cred(NULL);
+ 	if (IS_ERR(cred))
+ 		return (struct dentry *)cred;
+ 	parent = dentry->d_parent;
+@@ -2086,7 +2086,7 @@ nfs4_open_revalidate(struct inode *dir,
+ 	struct nfs4_state *state;
+ 	fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
+ 
+-	cred = rpc_lookup_cred();
++	cred = rpc_lookup_cred(NULL);
+ 	if (IS_ERR(cred))
+ 		return PTR_ERR(cred);
+ 	state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
+@@ -2330,7 +2330,7 @@ static int nfs4_proc_getattr(struct nfs_
+  */
+ 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;
+ 	struct rpc_cred *cred = NULL;
+@@ -2416,7 +2416,9 @@ static int _nfs4_proc_lookup(struct inod
+ 	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;
+@@ -2568,7 +2570,7 @@ static int nfs4_proc_readlink(struct ino
+ 
+ static int
+ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
+-                 int flags, struct nameidata *nd)
++                 int flags, struct nameidata *nd, struct rpc_groups *fsg)
+ {
+ 	struct path path = {
+ 		.mnt = nd->path.mnt,
+@@ -2579,7 +2581,7 @@ nfs4_proc_create(struct inode *dir, stru
+ 	fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
+ 	int status = 0;
+ 
+-	cred = rpc_lookup_cred();
++	cred = rpc_lookup_cred(NULL);
+ 	if (IS_ERR(cred)) {
+ 		status = PTR_ERR(cred);
+ 		goto out;
+@@ -2635,7 +2637,8 @@ out:
+ 	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;
+@@ -2711,7 +2714,8 @@ out:
+ }
+ 
+ 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;
+@@ -2760,7 +2764,8 @@ out:
+ 	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;
+@@ -2816,7 +2821,7 @@ static int nfs4_do_create(struct inode *
+ 	if (status == 0) {
+ 		update_changeattr(dir, &data->res.dir_cinfo);
+ 		nfs_post_op_update_inode(dir, data->res.dir_fattr);
+-		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
++		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
+ 	}
+ 	return status;
+ }
+@@ -2852,7 +2857,8 @@ out:
+ }
+ 
+ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
+-		struct page *page, unsigned int len, struct iattr *sattr)
++		struct page *page, unsigned int len, struct iattr *sattr,
++		struct rpc_groups *fsg)
+ {
+ 	struct nfs4_exception exception = { };
+ 	int err;
+@@ -2883,7 +2889,7 @@ out:
+ }
+ 
+ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
+-		struct iattr *sattr)
++		struct iattr *sattr, struct rpc_groups *fsg)
+ {
+ 	struct nfs4_exception exception = { };
+ 	int err;
+@@ -2980,7 +2986,7 @@ out:
+ }
+ 
+ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
+-		struct iattr *sattr, dev_t rdev)
++		struct iattr *sattr, dev_t rdev, struct rpc_groups *fsg)
+ {
+ 	struct nfs4_exception exception = { };
+ 	int err;
+diff -Nurp a/fs/nfs/proc.c b/fs/nfs/proc.c
+--- a/fs/nfs/proc.c	2010-08-02 10:27:04.000000000 +0200
++++ b/fs/nfs/proc.c	2010-11-08 16:36:46.614872402 +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.
+@@ -51,12 +51,20 @@
+  * support the NFSERR_JUKEBOX error code, but we handle this situation in the
+  * same way that we handle that error with NFSv3.
+  */
+-static int
+-nfs_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
++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,
++	};
+ 	int res;
++
+ 	do {
+-		res = rpc_call_sync(clnt, msg, flags);
++		res = rpc_call_sync(clnt, &msg, flags);
+ 		if (res != -EKEYEXPIRED)
+ 			break;
+ 		schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
+@@ -65,7 +73,20 @@ nfs_rpc_wrapper(struct rpc_clnt *clnt, s
+ 	return res;
+ }
+ 
+-#define rpc_call_sync(clnt, msg, flags)	nfs_rpc_wrapper(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;
++
++	cred = rpcauth_lookupcred(clnt->cl_auth, fsg, 0);
++	if (IS_ERR(cred))
++		return PTR_ERR(cred);
++	res = nfs2_rpc_call(clnt, proc, argp, resp, cred, 0);
++	put_rpccred(cred);
++	return res;
++}
+ 
+ static int
+ nfs_async_handle_expired_key(struct rpc_task *task)
+@@ -87,29 +108,22 @@ nfs_proc_get_root(struct nfs_server *ser
+ {
+ 	struct nfs_fattr *fattr = info->fattr;
+ 	struct nfs2_fsstat fsinfo;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_GETATTR],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= fattr,
+-	};
+ 	int status;
+ 
+ 	dprintk("%s: call getattr\n", __func__);
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(server->client, &msg, 0);
++	status = nfs2_rpc(server->client, NFSPROC_GETATTR, fhandle, fattr, NULL);
+ 	/* Retry with default authentication if different */
+ 	if (status && server->nfs_client->cl_rpcclient != server->client)
+-		status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0);
++		status = nfs2_rpc(server->nfs_client->cl_rpcclient, NFSPROC_GETATTR, fhandle, fattr, NULL);
+ 	dprintk("%s: reply getattr: %d\n", __func__, status);
+ 	if (status)
+ 		return status;
+ 	dprintk("%s: call statfs\n", __func__);
+-	msg.rpc_proc = &nfs_procedures[NFSPROC_STATFS];
+-	msg.rpc_resp = &fsinfo;
+-	status = rpc_call_sync(server->client, &msg, 0);
++	status = nfs2_rpc(server->client, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
+ 	/* Retry with default authentication if different */
+ 	if (status && server->nfs_client->cl_rpcclient != server->client)
+-		status = rpc_call_sync(server->nfs_client->cl_rpcclient, &msg, 0);
++		status = nfs2_rpc(server->nfs_client->cl_rpcclient, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
+ 	dprintk("%s: reply statfs: %d\n", __func__, status);
+ 	if (status)
+ 		return status;
+@@ -132,44 +146,44 @@ static int
+ nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+ 		struct nfs_fattr *fattr)
+ {
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_GETATTR],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= fattr,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  getattr\n");
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(server->client, &msg, 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
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_SETATTR],
+-		.rpc_argp	= &arg,
+-		.rpc_resp	= fattr,
+-	};
+ 	int	status;
++	struct rpc_cred	*cred;
++	struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ 
+ 	/* Mask out the non-modebit related stuff from attr->ia_mode */
+ 	sattr->ia_mode &= S_IALLUGO;
+ 
+ 	dprintk("NFS call  setattr\n");
+-	if (sattr->ia_valid & ATTR_FILE)
+-		msg.rpc_cred = nfs_file_cred(sattr->ia_file);
++	if (sattr->ia_valid & ATTR_FILE) {
++		cred = nfs_file_cred(sattr->ia_file);
++		get_rpccred(cred);
++	} else {
++		/* truncate() requires write access */
++		cred = rpcauth_lookupcred(clnt->cl_auth, fsg, 0);
++		if (IS_ERR(cred))
++			return PTR_ERR(cred);
++	}
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++	status = nfs2_rpc_call(clnt, NFSPROC_SETATTR, &arg, fattr, cred, 0);
++	put_rpccred(cred);
+ 	if (status == 0)
+ 		nfs_setattr_update_inode(inode, sattr);
+ 	dprintk("NFS reply setattr: %d\n", status);
+@@ -178,7 +192,8 @@ nfs_proc_setattr(struct dentry *dentry,
+ 
+ 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),
+@@ -189,16 +204,11 @@ nfs_proc_lookup(struct inode *dir, struc
+ 		.fh		= fhandle,
+ 		.fattr		= fattr
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_LOOKUP],
+-		.rpc_argp	= &arg,
+-		.rpc_resp	= &res,
+-	};
+ 	int			status;
+ 
+ 	dprintk("NFS call  lookup %s\n", name->name);
+ 	nfs_fattr_init(fattr);
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_LOOKUP, &arg, &res, fsg);
+ 	dprintk("NFS reply lookup: %d\n", status);
+ 	return status;
+ }
+@@ -212,14 +222,10 @@ static int nfs_proc_readlink(struct inod
+ 		.pglen		= pglen,
+ 		.pages		= &page
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_READLINK],
+-		.rpc_argp	= &args,
+-	};
+ 	int			status;
+ 
+ 	dprintk("NFS call  readlink\n");
+-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(inode), NFSPROC_READLINK, &args, NULL, NULL);
+ 	dprintk("NFS reply readlink: %d\n", status);
+ 	return status;
+ }
+@@ -258,24 +264,19 @@ static void nfs_free_createdata(const st
+ 
+ static int
+ nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
+-		int flags, struct nameidata *nd)
++		int flags, struct nameidata *nd, struct rpc_groups *fsg)
+ {
+ 	struct nfs_createdata *data;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_CREATE],
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  create %s\n", dentry->d_name.name);
+ 	data = nfs_alloc_createdata(dir, dentry, sattr);
+ 	if (data == NULL)
+ 		goto out;
+-	msg.rpc_argp = &data->arg;
+-	msg.rpc_resp = &data->res;
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_CREATE, &data->arg, &data->res, fsg);
+ 	nfs_mark_for_revalidate(dir);
+ 	if (status == 0)
+-		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
++		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, fsg);
+ 	nfs_free_createdata(data);
+ out:
+ 	dprintk("NFS reply create: %d\n", status);
+@@ -287,12 +288,9 @@ out:
+  */
+ static int
+ nfs_proc_mknod(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
+-	       dev_t rdev)
++	       dev_t rdev, struct rpc_groups *fsg)
+ {
+ 	struct nfs_createdata *data;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_CREATE],
+-	};
+ 	umode_t mode;
+ 	int status = -ENOMEM;
+ 
+@@ -310,19 +308,17 @@ nfs_proc_mknod(struct inode *dir, struct
+ 	data = nfs_alloc_createdata(dir, dentry, sattr);
+ 	if (data == NULL)
+ 		goto out;
+-	msg.rpc_argp = &data->arg;
+-	msg.rpc_resp = &data->res;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_CREATE, &data->arg, &data->res, fsg);
+ 	nfs_mark_for_revalidate(dir);
+ 
+ 	if (status == -EINVAL && S_ISFIFO(mode)) {
+ 		sattr->ia_mode = mode;
+ 		nfs_fattr_init(data->res.fattr);
+-		status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++		status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_CREATE, &data->arg, &data->res, fsg);
+ 	}
+ 	if (status == 0)
+-		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
++		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, fsg);
+ 	nfs_free_createdata(data);
+ out:
+ 	dprintk("NFS reply mknod: %d\n", status);
+@@ -330,21 +326,17 @@ out:
+ }
+   
+ 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_removeargs arg = {
+ 		.fh = NFS_FH(dir),
+ 		.name.len = name->len,
+ 		.name.name = name->name,
+ 	};
+-	struct rpc_message msg = { 
+-		.rpc_proc = &nfs_procedures[NFSPROC_REMOVE],
+-		.rpc_argp = &arg,
+-	};
+ 	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);
+ 	nfs_mark_for_revalidate(dir);
+ 
+ 	dprintk("NFS reply remove: %d\n", status);
+@@ -367,7 +359,8 @@ static int nfs_proc_unlink_done(struct r
+ 
+ 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),
+@@ -377,14 +370,10 @@ nfs_proc_rename(struct inode *old_dir, s
+ 		.toname		= new_name->name,
+ 		.tolen		= new_name->len
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_RENAME],
+-		.rpc_argp	= &arg,
+-	};
+ 	int			status;
+ 
+ 	dprintk("NFS call  rename %s -> %s\n", old_name->name, new_name->name);
+-	status = rpc_call_sync(NFS_CLIENT(old_dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(old_dir), NFSPROC_RENAME, &arg, NULL, fsg);
+ 	nfs_mark_for_revalidate(old_dir);
+ 	nfs_mark_for_revalidate(new_dir);
+ 	dprintk("NFS reply rename: %d\n", status);
+@@ -392,7 +381,8 @@ nfs_proc_rename(struct inode *old_dir, s
+ }
+ 
+ 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),
+@@ -400,14 +390,10 @@ nfs_proc_link(struct inode *inode, struc
+ 		.toname		= name->name,
+ 		.tolen		= name->len
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_LINK],
+-		.rpc_argp	= &arg,
+-	};
+ 	int			status;
+ 
+ 	dprintk("NFS call  link %s\n", name->name);
+-	status = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, fsg);
+ 	nfs_mark_for_revalidate(inode);
+ 	nfs_mark_for_revalidate(dir);
+ 	dprintk("NFS reply link: %d\n", status);
+@@ -416,7 +402,7 @@ nfs_proc_link(struct inode *inode, struc
+ 
+ static int
+ nfs_proc_symlink(struct inode *dir, struct dentry *dentry, struct page *page,
+-		 unsigned int len, struct iattr *sattr)
++		 unsigned int len, struct iattr *sattr, struct rpc_groups *fsg)
+ {
+ 	struct nfs_fh *fh;
+ 	struct nfs_fattr *fattr;
+@@ -428,10 +414,6 @@ nfs_proc_symlink(struct inode *dir, stru
+ 		.pathlen	= len,
+ 		.sattr		= sattr
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_SYMLINK],
+-		.rpc_argp	= &arg,
+-	};
+ 	int status = -ENAMETOOLONG;
+ 
+ 	dprintk("NFS call  symlink %s\n", dentry->d_name.name);
+@@ -445,7 +427,7 @@ nfs_proc_symlink(struct inode *dir, stru
+ 	if (fh == NULL || fattr == NULL)
+ 		goto out;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, fsg);
+ 	nfs_mark_for_revalidate(dir);
+ 
+ 	/*
+@@ -454,7 +436,7 @@ nfs_proc_symlink(struct inode *dir, stru
+ 	 * should fill in the data with a LOOKUP call on the wire.
+ 	 */
+ 	if (status == 0)
+-		status = nfs_instantiate(dentry, fh, fattr);
++		status = nfs_instantiate(dentry, fh, fattr, fsg);
+ 
+ 	nfs_free_fattr(fattr);
+ 	nfs_free_fhandle(fh);
+@@ -464,25 +446,21 @@ out:
+ }
+ 
+ static int
+-nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr)
++nfs_proc_mkdir(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
++	       struct rpc_groups *fsg)
+ {
+ 	struct nfs_createdata *data;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_MKDIR],
+-	};
+ 	int status = -ENOMEM;
+ 
+ 	dprintk("NFS call  mkdir %s\n", dentry->d_name.name);
+ 	data = nfs_alloc_createdata(dir, dentry, sattr);
+ 	if (data == NULL)
+ 		goto out;
+-	msg.rpc_argp = &data->arg;
+-	msg.rpc_resp = &data->res;
+ 
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_MKDIR, &data->arg, &data->res, fsg);
+ 	nfs_mark_for_revalidate(dir);
+ 	if (status == 0)
+-		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
++		status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, fsg);
+ 	nfs_free_createdata(data);
+ out:
+ 	dprintk("NFS reply mkdir: %d\n", status);
+@@ -490,21 +468,17 @@ out:
+ }
+ 
+ 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),
+ 		.name		= name->name,
+ 		.len		= name->len
+ 	};
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_RMDIR],
+-		.rpc_argp	= &arg,
+-	};
+ 	int			status;
+ 
+ 	dprintk("NFS call  rmdir %s\n", name->name);
+-	status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
++	status = nfs2_rpc(NFS_CLIENT(dir), NFSPROC_RMDIR, &arg, NULL, fsg);
+ 	nfs_mark_for_revalidate(dir);
+ 	dprintk("NFS reply rmdir: %d\n", status);
+ 	return status;
+@@ -528,15 +502,10 @@ nfs_proc_readdir(struct dentry *dentry,
+ 		.count		= count,
+ 		.pages		= &page,
+ 	};
+-	struct rpc_message	msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_READDIR],
+-		.rpc_argp	= &arg,
+-		.rpc_cred	= cred,
+-	};
+ 	int			status;
+ 
+ 	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);
+ 
+ 	nfs_invalidate_atime(dir);
+ 
+@@ -549,16 +518,11 @@ nfs_proc_statfs(struct nfs_server *serve
+ 			struct nfs_fsstat *stat)
+ {
+ 	struct nfs2_fsstat fsinfo;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_STATFS],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= &fsinfo,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  statfs\n");
+ 	nfs_fattr_init(stat->fattr);
+-	status = rpc_call_sync(server->client, &msg, 0);
++	status = nfs2_rpc(server->client, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
+ 	dprintk("NFS reply statfs: %d\n", status);
+ 	if (status)
+ 		goto out;
+@@ -577,16 +541,11 @@ nfs_proc_fsinfo(struct nfs_server *serve
+ 			struct nfs_fsinfo *info)
+ {
+ 	struct nfs2_fsstat fsinfo;
+-	struct rpc_message msg = {
+-		.rpc_proc	= &nfs_procedures[NFSPROC_STATFS],
+-		.rpc_argp	= fhandle,
+-		.rpc_resp	= &fsinfo,
+-	};
+ 	int	status;
+ 
+ 	dprintk("NFS call  fsinfo\n");
+ 	nfs_fattr_init(info->fattr);
+-	status = rpc_call_sync(server->client, &msg, 0);
++	status = nfs2_rpc(server->client, NFSPROC_STATFS, fhandle, &fsinfo, NULL);
+ 	dprintk("NFS reply fsinfo: %d\n", status);
+ 	if (status)
+ 		goto out;
+diff -Nurp a/fs/nfs/unlink.c b/fs/nfs/unlink.c
+--- a/fs/nfs/unlink.c	2010-11-02 17:39:16.757409445 +0100
++++ b/fs/nfs/unlink.c	2010-11-08 16:36:46.623872274 +0100
+@@ -254,7 +254,12 @@ nfs_async_unlink(struct inode *dir, stru
+ 	if (data == NULL)
+ 		goto out;
+ 
+-	data->cred = rpc_lookup_cred();
++	if (NFS_PROTO(dir)->version > 3)
++		data->cred = rpc_lookup_cred(NULL);
++	else {
++		struct rpc_groups fsg = { 1, { dir->i_gid } };
++		data->cred = rpc_lookup_cred(&fsg);
++	}
+ 	if (IS_ERR(data->cred)) {
+ 		status = PTR_ERR(data->cred);
+ 		goto out_free;
+diff -Nurp a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
+--- a/include/linux/nfs_fs.h	2010-11-02 17:39:22.342330707 +0100
++++ b/include/linux/nfs_fs.h	2010-11-08 16:36:46.673871568 +0100
+@@ -448,7 +448,8 @@ extern const struct file_operations nfs_
+ extern const struct dentry_operations nfs_dentry_operations;
+ 
+ extern void nfs_force_lookup_revalidate(struct inode *dir);
+-extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr);
++extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh,
++			   struct nfs_fattr *fattr, struct rpc_groups *fsg);
+ extern int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags);
+ extern void nfs_access_zap_cache(struct inode *inode);
+ 
+diff -Nurp a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
+--- a/include/linux/nfs_xdr.h	2010-11-02 17:39:22.375330242 +0100
++++ b/include/linux/nfs_xdr.h	2010-11-08 16:36:46.685871398 +0100
+@@ -1009,7 +1009,7 @@ struct nfs_write_data {
+ struct nfs_access_entry;
+ 
+ /*
+- * RPC procedure vector for NFSv2/NFSv3 demuxing
++ * RPC procedure vector for NFSv2/NFSv3/NFSv4 demuxing
+  */
+ struct nfs_rpc_ops {
+ 	u32	version;		/* Protocol version */
+@@ -1025,28 +1025,32 @@ struct nfs_rpc_ops {
+ 	int	(*getattr) (struct nfs_server *, struct nfs_fh *,
+ 			    struct nfs_fattr *);
+ 	int	(*setattr) (struct dentry *, struct nfs_fattr *,
+-			    struct iattr *);
++			    struct iattr *, struct rpc_groups *);
+ 	int	(*lookup)  (struct inode *, struct qstr *,
+-			    struct nfs_fh *, struct nfs_fattr *);
++			    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);
+ 	int	(*create)  (struct inode *, struct dentry *,
+-			    struct iattr *, int, struct nameidata *);
+-	int	(*remove)  (struct inode *, struct qstr *);
++			    struct iattr *, int, struct nameidata *,
++			    struct rpc_groups *);
++	int	(*remove)  (struct inode *, struct qstr *, struct rpc_groups *);
+ 	void	(*unlink_setup)  (struct rpc_message *, struct inode *dir);
+ 	int	(*unlink_done) (struct rpc_task *, struct inode *);
+ 	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 dentry *, struct page *,
+-			    unsigned int, struct iattr *);
+-	int	(*mkdir)   (struct inode *, struct dentry *, struct iattr *);
+-	int	(*rmdir)   (struct inode *, struct qstr *);
++			    unsigned int, struct iattr *, struct rpc_groups *);
++	int	(*mkdir)   (struct inode *, struct dentry *, struct iattr *,
++			    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 dentry *, struct iattr *,
+-			    dev_t);
++			    dev_t, struct rpc_groups *);
+ 	int	(*statfs)  (struct nfs_server *, struct nfs_fh *,
+ 			    struct nfs_fsstat *);
+ 	int	(*fsinfo)  (struct nfs_server *, struct nfs_fh *,
+diff -Nurp a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
+--- a/include/linux/sunrpc/auth.h	2010-11-02 17:39:23.166319089 +0100
++++ b/include/linux/sunrpc/auth.h	2010-11-08 16:44:15.844521768 +0100
+@@ -15,17 +15,23 @@
+ #include <linux/sunrpc/msg_prot.h>
+ #include <linux/sunrpc/xdr.h>
+ 
++#include <linux/sched.h>
+ #include <asm/atomic.h>
+ #include <linux/rcupdate.h>
+ 
+ /* size of the nodename buffer */
+ #define UNX_MAXNODENAME	32
+ 
++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;
++	struct rpc_groups rg;
+ 	unsigned char machine_cred : 1;
+ };
+ 
+@@ -126,7 +132,7 @@ void __exit		rpcauth_remove_module(void)
+ void __exit		rpc_destroy_generic_auth(void);
+ void 			rpc_destroy_authunix(void);
+ 
+-struct rpc_cred *	rpc_lookup_cred(void);
++struct rpc_cred *	rpc_lookup_cred(struct rpc_groups *);
+ struct rpc_cred *	rpc_lookup_machine_cred(void);
+ int			rpcauth_register(const struct rpc_authops *);
+ int			rpcauth_unregister(const struct rpc_authops *);
+@@ -134,7 +140,7 @@ struct rpc_auth *	rpcauth_create(rpc_aut
+ void			rpcauth_release(struct rpc_auth *);
+ struct rpc_cred *	rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
+ void			rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
+-struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, int);
++struct rpc_cred *	rpcauth_lookupcred(struct rpc_auth *, struct rpc_groups *, int);
+ struct rpc_cred *	rpcauth_generic_bind_cred(struct rpc_task *, struct rpc_cred *, int);
+ void			put_rpccred(struct rpc_cred *);
+ __be32 *		rpcauth_marshcred(struct rpc_task *, __be32 *);
+diff -Nurp a/include/linux/sunrpc/msg_prot.h b/include/linux/sunrpc/msg_prot.h
+--- a/include/linux/sunrpc/msg_prot.h	2010-01-05 16:42:12.000000000 +0100
++++ b/include/linux/sunrpc/msg_prot.h	2010-11-08 16:36:46.725870833 +0100
+@@ -79,6 +79,7 @@ enum rpc_auth_stat {
+ };
+ 
+ #define RPC_MAXNETNAMELEN	256
++#define RPC_MAXGROUPS		16
+ 
+ /*
+  * From RFC 1831:
+diff -Nurp a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h
+--- a/include/linux/sunrpc/svcauth.h	2008-07-21 17:49:50.000000000 +0200
++++ b/include/linux/sunrpc/svcauth.h	2010-11-08 16:36:46.750870480 +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 -Nurp a/net/sunrpc/auth.c b/net/sunrpc/auth.c
+--- a/net/sunrpc/auth.c	2010-11-02 17:39:31.826196995 +0100
++++ b/net/sunrpc/auth.c	2010-11-08 16:52:16.244721767 +0100
+@@ -17,8 +17,11 @@
+ 
+ #ifdef RPC_DEBUG
+ # define RPCDBG_FACILITY	RPCDBG_AUTH
++# define RG(rg,i)		((i) < (rg).ngroups ? (int)(rg).groups[i] : -1)
+ #endif
+ 
++static void rpcauth_add_groups(struct auth_cred *acred, struct rpc_groups *rg);
++
+ #define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
+ struct rpc_cred_cache {
+ 	struct hlist_head	*hashtable;
+@@ -413,24 +416,24 @@ out:
+ EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
+ 
+ struct rpc_cred *
+-rpcauth_lookupcred(struct rpc_auth *auth, int flags)
++rpcauth_lookupcred(struct rpc_auth *auth, struct rpc_groups *rg, int flags)
+ {
+ 	struct auth_cred acred;
+ 	struct rpc_cred *ret;
+ 	const struct cred *cred = current_cred();
+ 
+-	dprintk("RPC:       looking up %s cred\n",
+-		auth->au_ops->au_name);
++	dprintk("RPC:      looking up %s cred\n", auth->au_ops->au_name);
+ 
+ 	memset(&acred, 0, sizeof(acred));
+ 	acred.uid = cred->fsuid;
+-	acred.gid = cred->fsgid;
+-	acred.group_info = get_group_info(((struct cred *)cred)->group_info);
++	rpcauth_add_groups(&acred, rg);
+ 
+ 	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
+-	put_group_info(acred.group_info);
++
++	dprintk("RPC:      cred %p\n", ret);
+ 	return ret;
+ }
++EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
+ 
+ void
+ rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
+@@ -479,7 +482,7 @@ rpcauth_bind_new_cred(struct rpc_task *t
+ 
+ 	dprintk("RPC: %5u looking up %s cred\n",
+ 		task->tk_pid, auth->au_ops->au_name);
+-	return rpcauth_lookupcred(auth, lookupflags);
++	return rpcauth_lookupcred(auth, NULL, lookupflags);
+ }
+ 
+ static int
+@@ -505,6 +508,44 @@ rpcauth_bindcred(struct rpc_task *task,
+ 	return 0;
+ }
+ 
++/*
++ * Generic function for adding 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 rpcauth_add_groups(struct auth_cred *acred, struct rpc_groups *rg)
++{
++	int i, n, ngroups;
++	gid_t gid;
++	const struct cred *cred = current_cred();
++
++	acred->gid = cred->fsgid;
++	get_group_info(cred->group_info);
++	ngroups = cred->group_info->ngroups;
++	n = 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:      %s(): rg=%d:%d,%d,%d -> %d:%d,%d,%d\n", __func__,
++			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:      %s(): ngroups=%d\n", __func__, ngroups);
++		for (i = 0; i < n; ++i)
++			acred->rg.groups[i] = GROUP_AT(cred->group_info, i);
++		acred->rg.ngroups = n;
++	}
++	put_group_info(cred->group_info);
++}
++
+ void
+ put_rpccred(struct rpc_cred *cred)
+ {
+diff -Nurp a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c
+--- a/net/sunrpc/auth_generic.c	2010-11-02 17:39:31.846196714 +0100
++++ b/net/sunrpc/auth_generic.c	2010-11-08 16:36:46.786869972 +0100
+@@ -32,9 +32,9 @@ static const struct rpc_credops generic_
+ /*
+  * Public call interface
+  */
+-struct rpc_cred *rpc_lookup_cred(void)
++struct rpc_cred *rpc_lookup_cred(struct rpc_groups *rg)
+ {
+-	return rpcauth_lookupcred(&generic_auth, 0);
++	return rpcauth_lookupcred(&generic_auth, rg, 0);
+ }
+ EXPORT_SYMBOL_GPL(rpc_lookup_cred);
+ 
+@@ -84,12 +84,7 @@ generic_create_cred(struct rpc_auth *aut
+ 	rpcauth_init_cred(&gcred->gc_base, acred, &generic_auth, &generic_credops);
+ 	gcred->gc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
+ 
+-	gcred->acred.uid = acred->uid;
+-	gcred->acred.gid = acred->gid;
+-	gcred->acred.group_info = acred->group_info;
+-	if (gcred->acred.group_info != NULL)
+-		get_group_info(gcred->acred.group_info);
+-	gcred->acred.machine_cred = acred->machine_cred;
++	gcred->acred = *acred;
+ 
+ 	dprintk("RPC:       allocated %s cred %p for uid %d gid %d\n",
+ 			gcred->acred.machine_cred ? "machine" : "generic",
+@@ -103,8 +98,6 @@ generic_free_cred(struct rpc_cred *cred)
+ 	struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
+ 
+ 	dprintk("RPC:       generic_free_cred %p\n", gcred);
+-	if (gcred->acred.group_info != NULL)
+-		put_group_info(gcred->acred.group_info);
+ 	kfree(gcred);
+ }
+ 
+@@ -128,29 +121,14 @@ static int
+ generic_match(struct auth_cred *acred, struct rpc_cred *cred, int flags)
+ {
+ 	struct generic_cred *gcred = container_of(cred, struct generic_cred, gc_base);
+-	int i;
+ 
+ 	if (gcred->acred.uid != acred->uid ||
+ 	    gcred->acred.gid != acred->gid ||
++	    gcred->acred.rg.ngroups != acred->rg.ngroups ||
++	    memcmp(gcred->acred.rg.groups, acred->rg.groups, acred->rg.ngroups * sizeof (gid_t)) ||
+ 	    gcred->acred.machine_cred != acred->machine_cred)
+-		goto out_nomatch;
+-
+-	/* Optimisation in the case where pointers are identical... */
+-	if (gcred->acred.group_info == acred->group_info)
+-		goto out_match;
+-
+-	/* Slow path... */
+-	if (gcred->acred.group_info->ngroups != acred->group_info->ngroups)
+-		goto out_nomatch;
+-	for (i = 0; i < gcred->acred.group_info->ngroups; i++) {
+-		if (GROUP_AT(gcred->acred.group_info, i) !=
+-				GROUP_AT(acred->group_info, i))
+-			goto out_nomatch;
+-	}
+-out_match:
++		return 0;
+ 	return 1;
+-out_nomatch:
+-	return 0;
+ }
+ 
+ int __init rpc_init_generic_auth(void)
+diff -Nurp a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
+--- a/net/sunrpc/auth_unix.c	2010-11-02 17:39:31.905195883 +0100
++++ b/net/sunrpc/auth_unix.c	2010-11-08 16:36:46.795869845 +0100
+@@ -13,12 +13,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;
+-	gid_t			uc_gids[NFS_NGROUPS];
++	gid_t			uc_gids[RPC_MAXGROUPS];
+ };
+ #define uc_uid			uc_base.cr_uid
+ 
+@@ -60,8 +58,7 @@ static struct rpc_cred *
+ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
+ {
+ 	struct unx_cred	*cred;
+-	unsigned int groups = 0;
+-	unsigned int i;
++	int n;
+ 
+ 	dprintk("RPC:       allocating UNIX cred for uid %d gid %d\n",
+ 			acred->uid, acred->gid);
+@@ -72,16 +69,11 @@ unx_create_cred(struct rpc_auth *auth, s
+ 	rpcauth_init_cred(&cred->uc_base, acred, auth, &unix_credops);
+ 	cred->uc_base.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE;
+ 
+-	if (acred->group_info != NULL)
+-		groups = acred->group_info->ngroups;
+-	if (groups > NFS_NGROUPS)
+-		groups = NFS_NGROUPS;
+-
++	n = acred->rg.ngroups;
+ 	cred->uc_gid = acred->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;
+ 
+ 	return &cred->uc_base;
+ }
+@@ -115,21 +107,12 @@ static int
+ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags)
+ {
+ 	struct unx_cred	*cred = container_of(rcred, struct unx_cred, uc_base);
+-	unsigned int groups = 0;
+-	unsigned int i;
+-
++	int n = acred->rg.ngroups;
+ 
+ 	if (cred->uc_uid != acred->uid || cred->uc_gid != acred->gid)
+ 		return 0;
+ 
+-	if (acred->group_info != NULL)
+-		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;
++	return !memcmp(cred->uc_gids, acred->rg.groups, n * sizeof (gid_t));
+ }
+ 
+ /*
+@@ -156,7 +139,7 @@ unx_marshal(struct rpc_task *task, __be3
+ 	*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 */
+diff -Nurp a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
+--- a/net/sunrpc/svcauth_unix.c	2010-06-15 11:33:44.000000000 +0200
++++ b/net/sunrpc/svcauth_unix.c	2010-11-08 16:36:46.815869562 +0100
+@@ -836,7 +836,7 @@ svcauth_unix_accept(struct svc_rqst *rqs
+ 	cred->cr_uid = svc_getnl(argv);		/* uid */
+ 	cred->cr_gid = svc_getnl(argv);		/* gid */
+ 	slen = svc_getnl(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)

patches/2.6.37.1-nfs-ngroups-4.58.patch

+diff -Nurp a/fs/nfs/dir.c b/fs/nfs/dir.c
+--- a/fs/nfs/dir.c	2011-02-18 09:24:48.000000000 +0100
++++ b/fs/nfs/dir.c	2011-02-18 09:55:12.000000000 +0100
+@@ -1023,6 +1023,7 @@ static int nfs_lookup_revalidate(struct 
+ 	struct dentry *parent;
+ 	struct nfs_fh *fhandle = NULL;
+ 	struct nfs_fattr *fattr = NULL;
++	struct rpc_groups fsg;
+ 	int error;
+ 
+ 	parent = dget_parent(dentry);
+@@ -1062,7 +1063,9 @@ static int nfs_lookup_revalidate(struct 
+ 	if (fhandle == NULL || fattr == NULL)
+ 		goto out_error;
+ 
+-	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))
+@@ -1176,6 +1179,7 @@ static struct dentry *nfs_lookup(struct 
+ 	struct inode *inode = NULL;
+ 	struct nfs_fh *fhandle = NULL;
+ 	struct nfs_fattr *fattr = NULL;
++	struct rpc_groups fsg;
+ 	int error;
+ 
+ 	dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+@@ -1207,7 +1211,9 @@ static struct dentry *nfs_lookup(struct 
+ 	parent = dentry->d_parent;
+ 	/* Protect against concurrent sillydeletes */
+ 	nfs_block_sillyrename(parent);
+-	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) {
+@@ -1272,7 +1278,7 @@ static struct nfs_open_context *nameidat
+ 	struct rpc_cred *cred;
+ 	fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
+ 
+-	cred = rpc_lookup_cred();
++	cred = rpc_lookup_cred(NULL);
+ 	if (IS_ERR(cred))
+ 		return ERR_CAST(cred);
+ 	ctx = alloc_nfs_open_context(&path, cred, fmode);
+@@ -1504,7 +1510,7 @@ static int nfs_open_create(struct inode 
+ 			goto out_err_drop;
+ 	}
+ 
+-	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx);
++	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, ctx, NULL);
+ 	if (error != 0)
+ 		goto out_put_ctx;
+ 	if (ctx != NULL) {
+@@ -1528,7 +1534,7 @@ out_err:
+  * Code common to create, mkdir, and mknod.
+  */
+ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
+-				struct nfs_fattr *fattr)
++				struct nfs_fattr *fattr, struct rpc_groups *fsg)
+ {
+ 	struct dentry *parent = dget_parent(dentry);
+ 	struct inode *dir = parent->d_inode;
+@@ -1541,7 +1547,7 @@ int nfs_instantiate(struct dentry *dentr
+ 	if (dentry->d_inode)
+ 		goto out;
+ 	if (fhandle->size == 0) {
+-		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_error;
+ 	}
+@@ -1578,6 +1584,7 @@ static int nfs_create(struct inode *dir,
+ 	struct iattr attr;
+ 	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);
+@@ -1588,7 +1595,7 @@ static int nfs_create(struct inode *dir,
+ 	if ((nd->flags & LOOKUP_CREATE) != 0)
+ 		open_flags = nd->intent.open.flags;
+ 
+-	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, NULL);
++	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, NULL, &fsg);
+ 	if (error != 0)
+ 		goto out_err;
+ 	return 0;
+@@ -1605,6 +1612,7 @@ nfs_mknod(struct inode *dir, struct dent
+ {
+ 	struct iattr attr;
+ 	int status;
++	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);
+@@ -1615,7 +1623,7 @@ nfs_mknod(struct inode *dir, struct dent
+ 	attr.ia_mode = mode;
+ 	attr.ia_valid = ATTR_MODE;
+ 
+-	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
++	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev, &fsg);
+ 	if (status != 0)
+ 		goto out_err;
+ 	return 0;
+@@ -1631,6 +1639,7 @@ static int nfs_mkdir(struct inode *dir, 
+ {
+ 	struct iattr attr;
+ 	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);
+@@ -1638,7 +1647,7 @@ static int nfs_mkdir(struct inode *dir, 
+ 	attr.ia_valid = ATTR_MODE;
+ 	attr.ia_mode = mode | S_IFDIR;
+ 
+-	error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);