diff -Nurp a/fs/nfs/dir.c b/fs/nfs/dir.c --- a/fs/nfs/dir.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/dir.c 2017-04-08 15:45:05.742381685 +0200 @@ -109,7 +109,12 @@ nfs_opendir(struct inode *inode, struct nfs_inc_stats(inode, NFSIOS_VFSOPEN); - 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_dir_context(inode, cred); @@ -1134,6 +1139,7 @@ static int nfs_lookup_revalidate(struct struct nfs_fh *fhandle = NULL; struct nfs_fattr *fattr = NULL; struct nfs4_label *label = NULL; + struct rpc_groups fsg; int error; if (flags & LOOKUP_RCU) { @@ -1197,8 +1203,10 @@ static int nfs_lookup_revalidate(struct if (IS_ERR(label)) goto out_error; + fsg.ngroups = 1; + fsg.groups[0] = dir->i_gid; trace_nfs_lookup_revalidate_enter(dir, dentry, flags); - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label, &fsg); trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error); if (error) goto out_bad; @@ -1381,6 +1389,7 @@ struct dentry *nfs_lookup(struct inode * struct nfs_fh *fhandle = NULL; struct nfs_fattr *fattr = NULL; struct nfs4_label *label = NULL; + struct rpc_groups fsg; int error; dfprintk(VFS, "NFS: lookup(%pd2)\n", dentry); @@ -1407,7 +1416,9 @@ struct dentry *nfs_lookup(struct inode * goto out; trace_nfs_lookup_enter(dir, dentry, flags); - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label); + fsg.ngroups = 1; + fsg.groups[0] = dir->i_gid; + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label, &fsg); if (error == -ENOENT) goto no_entry; if (error < 0) { @@ -1675,7 +1686,8 @@ no_open: */ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, struct nfs_fattr *fattr, - struct nfs4_label *label) + struct nfs4_label *label, + struct rpc_groups *fsg) { struct dentry *parent = dget_parent(dentry); struct inode *dir = d_inode(parent); @@ -1688,7 +1700,7 @@ int nfs_instantiate(struct dentry *dentr if (d_really_is_positive(dentry)) goto out; if (fhandle->size == 0) { - error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL); + error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL, fsg); if (error) goto out_error; } @@ -1725,6 +1737,7 @@ int nfs_create(struct inode *dir, struct { struct iattr attr; int open_flags = excl ? O_CREAT | O_EXCL : O_CREAT; + struct rpc_groups fsg = { 1, { dir->i_gid } }; int error; dfprintk(VFS, "NFS: create(%s/%lu), %pd\n", @@ -1734,7 +1747,7 @@ int nfs_create(struct inode *dir, struct attr.ia_valid = ATTR_MODE; trace_nfs_create_enter(dir, dentry, open_flags); - error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags); + error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, &fsg); trace_nfs_create_exit(dir, dentry, open_flags, error); if (error != 0) goto out_err; @@ -1753,6 +1766,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/%lu), %pd\n", dir->i_sb->s_id, dir->i_ino, dentry); @@ -1761,7 +1775,7 @@ nfs_mknod(struct inode *dir, struct dent attr.ia_valid = ATTR_MODE; trace_nfs_mknod_enter(dir, dentry); - status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); + status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev, &fsg); trace_nfs_mknod_exit(dir, dentry, status); if (status != 0) goto out_err; @@ -1779,6 +1793,7 @@ int nfs_mkdir(struct inode *dir, struct { struct iattr attr; int error; + struct rpc_groups fsg = { 1, { dir->i_gid } }; dfprintk(VFS, "NFS: mkdir(%s/%lu), %pd\n", dir->i_sb->s_id, dir->i_ino, dentry); @@ -1787,7 +1802,7 @@ int nfs_mkdir(struct inode *dir, struct attr.ia_mode = mode | S_IFDIR; trace_nfs_mkdir_enter(dir, dentry); - error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); + error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr, &fsg); trace_nfs_mkdir_exit(dir, dentry, error); if (error != 0) goto out_err; @@ -1807,6 +1822,7 @@ static void nfs_dentry_handle_enoent(str int nfs_rmdir(struct inode *dir, struct dentry *dentry) { int error; + struct rpc_groups fsg = { 1, { dir->i_gid } }; dfprintk(VFS, "NFS: rmdir(%s/%lu), %pd\n", dir->i_sb->s_id, dir->i_ino, dentry); @@ -1814,7 +1830,7 @@ int nfs_rmdir(struct inode *dir, struct trace_nfs_rmdir_enter(dir, dentry); if (d_really_is_positive(dentry)) { down_write(&NFS_I(d_inode(dentry))->rmdir_sem); - 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 */ switch (error) { case 0: @@ -1825,7 +1841,7 @@ int nfs_rmdir(struct inode *dir, struct } up_write(&NFS_I(d_inode(dentry))->rmdir_sem); } else - error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); + error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name, &fsg); trace_nfs_rmdir_exit(dir, dentry, error); return error; @@ -1843,6 +1859,7 @@ static int nfs_safe_remove(struct dentry { struct inode *dir = d_inode(dentry->d_parent); struct inode *inode = d_inode(dentry); + struct rpc_groups fsg = { 1, { dir->i_gid } }; int error = -EBUSY; dfprintk(VFS, "NFS: safe_remove(%pd2)\n", dentry); @@ -1856,11 +1873,11 @@ static int nfs_safe_remove(struct dentry trace_nfs_remove_enter(dir, dentry); if (inode != NULL) { NFS_PROTO(inode)->return_delegation(inode); - error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); + error = NFS_PROTO(dir)->remove(dir, &dentry->d_name, &fsg); if (error == 0) nfs_drop_nlink(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); trace_nfs_remove_exit(dir, dentry, error); @@ -1928,6 +1945,7 @@ int nfs_symlink(struct inode *dir, struc struct iattr attr; unsigned int pathlen = strlen(symname); int error; + struct rpc_groups fsg = { 1, { dir->i_gid } }; dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s)\n", dir->i_sb->s_id, dir->i_ino, dentry, symname); @@ -1948,7 +1966,7 @@ int nfs_symlink(struct inode *dir, struc memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen); trace_nfs_symlink_enter(dir, dentry); - error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr); + error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr, &fsg); trace_nfs_symlink_exit(dir, dentry, error); if (error != 0) { dfprintk(VFS, "NFS: symlink(%s/%lu, %pd, %s) error %d\n", @@ -1983,6 +2001,7 @@ int nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *inode = d_inode(old_dentry); + struct rpc_groups fsg = { 1, { dir->i_gid } }; int error; dfprintk(VFS, "NFS: link(%pd2 -> %pd2)\n", @@ -1992,7 +2011,7 @@ nfs_link(struct dentry *old_dentry, stru NFS_PROTO(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) { ihold(inode); d_add(dentry, inode); @@ -2448,6 +2467,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); @@ -2506,6 +2527,7 @@ static int nfs_execute_ok(struct inode * int nfs_permission(struct inode *inode, int mask) { struct rpc_cred *cred; + struct rpc_groups fsg, *pfsg; int res = 0; nfs_inc_stats(inode, NFSIOS_VFSACCESS); @@ -2537,9 +2559,17 @@ force_lookup: if (!NFS_PROTO(inode)->access) goto out_notsup; + if (NFS_PROTO(inode)->version > 3) { + pfsg = NULL; + } else { + fsg.ngroups = 1; + fsg.groups[0] = inode->i_gid; + pfsg = &fsg; + } + /* Always try fast lookups first */ rcu_read_lock(); - cred = rpc_lookup_cred_nonblock(); + cred = rpc_lookup_cred_nonblock(pfsg); if (!IS_ERR(cred)) res = nfs_do_access(inode, cred, mask|MAY_NOT_BLOCK); else @@ -2547,7 +2577,7 @@ force_lookup: rcu_read_unlock(); if (res == -ECHILD && !(mask & MAY_NOT_BLOCK)) { /* Fast lookup failed, try the slow way */ - cred = rpc_lookup_cred(); + cred = rpc_lookup_cred(pfsg); if (!IS_ERR(cred)) { res = nfs_do_access(inode, cred, mask); put_rpccred(cred); diff -Nurp a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c --- a/fs/nfs/flexfilelayout/flexfilelayout.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/flexfilelayout/flexfilelayout.c 2017-04-08 15:46:30.540214749 +0200 @@ -28,8 +28,6 @@ #define FF_LAYOUTRETURN_MAXERR 20 -static struct group_info *ff_zero_group; - static void ff_layout_read_record_layoutstats_done(struct rpc_task *task, struct nfs_pgio_header *hdr); static int ff_layout_mirror_prepare_stats(struct pnfs_layout_hdr *lo, @@ -413,7 +411,7 @@ ff_layout_alloc_lseg(struct pnfs_layout_ for (i = 0; i < fls->mirror_array_cnt; i++) { struct nfs4_ff_layout_mirror *mirror; - struct auth_cred acred = { .group_info = ff_zero_group }; + struct auth_cred acred = { {0} }; struct rpc_cred __rcu *cred; u32 ds_count, fh_count, id; int j; @@ -2403,11 +2401,6 @@ static int __init nfs4flexfilelayout_ini { printk(KERN_INFO "%s: NFSv4 Flexfile Layout Driver Registering...\n", __func__); - if (!ff_zero_group) { - ff_zero_group = groups_alloc(0); - if (!ff_zero_group) - return -ENOMEM; - } return pnfs_register_layoutdriver(&flexfilelayout_type); } @@ -2416,10 +2409,6 @@ static void __exit nfs4flexfilelayout_ex printk(KERN_INFO "%s: NFSv4 Flexfile Layout Driver Unregistering...\n", __func__); pnfs_unregister_layoutdriver(&flexfilelayout_type); - if (ff_zero_group) { - put_group_info(ff_zero_group); - ff_zero_group = NULL; - } } MODULE_ALIAS("nfs-layouttype4-4"); diff -Nurp a/fs/nfs/inode.c b/fs/nfs/inode.c --- a/fs/nfs/inode.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/inode.c 2017-04-08 15:45:05.746381677 +0200 @@ -549,6 +549,7 @@ nfs_setattr(struct dentry *dentry, struc { struct inode *inode = d_inode(dentry); struct nfs_fattr *fattr; + struct rpc_groups fsg; int error = 0; nfs_inc_stats(inode, NFSIOS_VFSSETATTR); @@ -590,7 +591,11 @@ nfs_setattr(struct dentry *dentry, struc */ if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) NFS_PROTO(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) error = nfs_refresh_inode(inode, fattr); nfs_free_fattr(fattr); @@ -853,7 +858,14 @@ struct nfs_open_context *alloc_nfs_open_ struct file *filp) { struct nfs_open_context *ctx; - struct rpc_cred *cred = rpc_lookup_cred(); + struct rpc_cred *cred; + + if (NFS_SB(dentry->d_sb)->nfs_client->rpc_ops->version > 3) + cred = rpc_lookup_cred(NULL); + else { + struct rpc_groups fsg = { 1, { d_inode(dentry)->i_gid } }; + cred = rpc_lookup_cred(&fsg); + } if (IS_ERR(cred)) return ERR_CAST(cred); diff -Nurp a/fs/nfs/namespace.c b/fs/nfs/namespace.c --- a/fs/nfs/namespace.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/namespace.c 2017-04-08 15:45:05.746381677 +0200 @@ -277,9 +277,17 @@ struct vfsmount *nfs_submount(struct nfs { int err; struct dentry *parent = dget_parent(dentry); + struct rpc_groups fsg, *pfsg; + if (NFS_PROTO(d_inode(parent))->version > 3) { + pfsg = NULL; + } else { + pfsg = &fsg; + pfsg->ngroups = 1; + pfsg->groups[0] = d_inode(parent)->i_gid; + } /* Look it up again to get its attributes */ - err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name, fh, fattr, NULL); + err = server->nfs_client->rpc_ops->lookup(d_inode(parent), &dentry->d_name, fh, fattr, NULL, pfsg); dput(parent); if (err != 0) return ERR_PTR(err); diff -Nurp a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c --- a/fs/nfs/nfs3proc.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/nfs3proc.c 2017-04-08 15:45:05.748381673 +0200 @@ -28,11 +28,19 @@ /* A wrapper to handle the EJUKEBOX 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) break; freezable_schedule_timeout_killable_unsafe(NFS_JUKEBOX_RETRY_TIME); @@ -41,7 +49,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) @@ -60,21 +81,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 (status == 0 && !(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; @@ -102,41 +116,41 @@ static int nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label) { - 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 = d_inode(dentry); 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, fattr); dprintk("NFS reply setattr: %d\n", status); @@ -146,7 +160,7 @@ nfs3_proc_setattr(struct dentry *dentry, static int nfs3_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr, - struct nfs4_label *label) + struct nfs4_label *label, struct rpc_groups *fsg) { struct nfs3_diropargs arg = { .fh = NFS_FH(dir), @@ -157,11 +171,6 @@ nfs3_proc_lookup(struct inode *dir, cons .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); @@ -170,13 +179,11 @@ nfs3_proc_lookup(struct inode *dir, cons 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); @@ -189,12 +196,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; @@ -218,7 +219,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; @@ -245,19 +247,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: @@ -266,7 +263,8 @@ out: } struct nfs3_createdata { - struct rpc_message msg; + u32 proc; + struct rpc_groups *fsg; union { struct nfs3_createargs create; struct nfs3_mkdirargs mkdir; @@ -285,8 +283,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; @@ -300,10 +296,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, NULL); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL, data->fsg); return status; } @@ -317,7 +313,7 @@ static void nfs3_free_createdata(struct */ static int nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - int flags) + int flags, struct rpc_groups *fsg) { struct posix_acl *default_acl, *acl; struct nfs3_createdata *data; @@ -329,7 +325,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; @@ -385,7 +382,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(d_inode(dentry), data->res.fattr); dprintk("NFS reply setattr (post-create): %d\n", status); if (status != 0) @@ -404,18 +401,13 @@ out: } static int -nfs3_proc_remove(struct inode *dir, const struct qstr *name) +nfs3_proc_remove(struct inode *dir, const struct qstr *name, struct rpc_groups *fsg) { struct nfs_removeargs arg = { .fh = NFS_FH(dir), .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); @@ -423,7 +415,7 @@ nfs3_proc_remove(struct inode *dir, cons 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: @@ -480,7 +472,8 @@ nfs3_proc_rename_done(struct rpc_task *t } static int -nfs3_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name) +nfs3_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name, + struct rpc_groups *fsg) { struct nfs3_linkargs arg = { .fromfh = NFS_FH(inode), @@ -489,11 +482,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); @@ -502,7 +490,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: @@ -514,7 +502,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; @@ -527,7 +515,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; @@ -544,7 +533,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 posix_acl *default_acl, *acl; struct nfs3_createdata *data; @@ -560,7 +550,8 @@ nfs3_proc_mkdir(struct inode *dir, struc if (status) 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; @@ -582,7 +573,7 @@ out: } static int -nfs3_proc_rmdir(struct inode *dir, const struct qstr *name) +nfs3_proc_rmdir(struct inode *dir, const struct qstr *name, struct rpc_groups *fsg) { struct nfs_fattr *dir_attr; struct nfs3_diropargs arg = { @@ -590,10 +581,6 @@ nfs3_proc_rmdir(struct inode *dir, const .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); @@ -601,8 +588,7 @@ nfs3_proc_rmdir(struct inode *dir, const 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: @@ -637,16 +623,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); @@ -655,7 +636,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); @@ -669,7 +650,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 posix_acl *default_acl, *acl; struct nfs3_createdata *data; @@ -686,7 +667,8 @@ nfs3_proc_mknod(struct inode *dir, struc if (status) 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; @@ -730,16 +712,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 fsstat: %d\n", status); return status; } @@ -748,16 +725,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; } @@ -782,16 +754,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/nfs4namespace.c b/fs/nfs/nfs4namespace.c --- a/fs/nfs/nfs4namespace.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/nfs4namespace.c 2017-04-08 15:45:05.748381673 +0200 @@ -182,7 +182,7 @@ static struct rpc_clnt *nfs_find_best_se * flavor. This is mostly for RPC_AUTH_GSS * where cr_init obtains a gss context */ - cred = rpcauth_lookupcred(new->cl_auth, 0); + cred = rpcauth_lookupcred(new->cl_auth, NULL, 0); if (IS_ERR(cred)) { rpc_shutdown_client(new); continue; diff -Nurp a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c --- a/fs/nfs/nfs4proc.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/nfs4proc.c 2017-04-08 15:47:53.749050942 +0200 @@ -3750,7 +3750,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 = d_inode(dentry); struct rpc_cred *cred = NULL; @@ -3796,7 +3796,8 @@ nfs4_proc_setattr(struct dentry *dentry, static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, - struct nfs_fattr *fattr, struct nfs4_label *label) + struct nfs_fattr *fattr, struct nfs4_label *label, + struct rpc_groups *fsg) { struct nfs_server *server = NFS_SERVER(dir); int status; @@ -3843,7 +3844,7 @@ static int nfs4_proc_lookup_common(struc struct rpc_clnt *client = *clnt; int err; do { - err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label); + err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label, NULL); trace_nfs4_lookup(dir, name, err); switch (err) { case -NFS4ERR_BADNAME: @@ -3880,7 +3881,7 @@ out: static int nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr, - struct nfs4_label *label) + struct nfs4_label *label, struct rpc_groups *fsg) { int status; struct rpc_clnt *client = NFS_CLIENT(dir); @@ -4028,7 +4029,7 @@ static int nfs4_proc_readlink(struct ino */ static int nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - int flags) + int flags, struct rpc_groups *fsg) { struct nfs_server *server = NFS_SERVER(dir); struct nfs4_label l, *ilabel = NULL; @@ -4079,7 +4080,8 @@ static int _nfs4_proc_remove(struct inod return status; } -static int nfs4_proc_remove(struct inode *dir, const struct qstr *name) +static int nfs4_proc_remove(struct inode *dir, const struct qstr *name, + struct rpc_groups *fsg) { struct nfs4_exception exception = { }; int err; @@ -4213,7 +4215,8 @@ out: return status; } -static int nfs4_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name) +static int nfs4_proc_link(struct inode *inode, struct inode *dir, + const struct qstr *name, struct rpc_groups *fsg) { struct nfs4_exception exception = { }; int err; @@ -4276,7 +4279,7 @@ static int nfs4_do_create(struct inode * if (status == 0) { update_changeattr(dir, &data->res.dir_cinfo, data->res.fattr->time_start); - status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label, NULL); } return status; } @@ -4315,7 +4318,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 = { }; struct nfs4_label l, *label = NULL; @@ -4353,7 +4357,7 @@ out: } static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, - struct iattr *sattr) + struct iattr *sattr, struct rpc_groups *fsg) { struct nfs_server *server = NFS_SERVER(dir); struct nfs4_exception exception = { }; @@ -4464,7 +4468,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 nfs_server *server = NFS_SERVER(dir); struct nfs4_exception exception = { }; @@ -5351,7 +5355,7 @@ nfs4_set_security_label(struct inode *in ilabel.label = (char *)buf; ilabel.len = buflen; - cred = rpc_lookup_cred(); + cred = rpc_lookup_cred(NULL); if (IS_ERR(cred)) return PTR_ERR(cred); diff -Nurp a/fs/nfs/proc.c b/fs/nfs/proc.c --- a/fs/nfs/proc.c 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/proc.c 2017-04-08 15:45:05.755381660 +0200 @@ -6,7 +6,7 @@ * OS-independent nfs remote procedure call functions * * Tuned by Alan Cox 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. @@ -46,6 +46,37 @@ #define NFSDBG_FACILITY NFSDBG_PROC +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; + + res = rpc_call_sync(clnt, &msg, flags); + return res; +} + +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; +} + /* * Bare-bones access to getattr: this is for nfs_read_super. */ @@ -55,29 +86,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; @@ -100,44 +124,44 @@ static int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label) { - 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 = d_inode(dentry); - 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, fattr); dprintk("NFS reply setattr: %d\n", status); @@ -147,7 +171,7 @@ nfs_proc_setattr(struct dentry *dentry, static int nfs_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr, - struct nfs4_label *label) + struct nfs4_label *label, struct rpc_groups *fsg) { struct nfs_diropargs arg = { .fh = NFS_FH(dir), @@ -158,16 +182,11 @@ nfs_proc_lookup(struct inode *dir, const .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; } @@ -181,14 +200,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; } @@ -227,24 +242,19 @@ static void nfs_free_createdata(const st static int nfs_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, - int flags) + int flags, 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 %pd\n", dentry); 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, NULL); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL, fsg); nfs_free_createdata(data); out: dprintk("NFS reply create: %d\n", status); @@ -256,12 +266,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; @@ -279,19 +286,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, NULL); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL, fsg); nfs_free_createdata(data); out: dprintk("NFS reply mknod: %d\n", status); @@ -299,20 +304,16 @@ out: } static int -nfs_proc_remove(struct inode *dir, const struct qstr *name) +nfs_proc_remove(struct inode *dir, const struct qstr *name, struct rpc_groups *fsg) { struct nfs_removeargs arg = { .fh = NFS_FH(dir), .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); @@ -357,7 +358,8 @@ nfs_proc_rename_done(struct rpc_task *ta } static int -nfs_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name) +nfs_proc_link(struct inode *inode, struct inode *dir, const struct qstr *name, + struct rpc_groups *fsg) { struct nfs_linkargs arg = { .fromfh = NFS_FH(inode), @@ -365,14 +367,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); @@ -381,7 +379,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; @@ -393,10 +391,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 %pd\n", dentry); @@ -410,7 +404,7 @@ nfs_proc_symlink(struct inode *dir, stru if (fh == NULL || fattr == NULL) goto out_free; - 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); /* @@ -419,7 +413,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, NULL); + status = nfs_instantiate(dentry, fh, fattr, NULL, fsg); out_free: nfs_free_fattr(fattr); @@ -430,25 +424,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 %pd\n", dentry); 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, NULL); + status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL, fsg); nfs_free_createdata(data); out: dprintk("NFS reply mkdir: %d\n", status); @@ -456,21 +446,17 @@ out: } static int -nfs_proc_rmdir(struct inode *dir, const struct qstr *name) +nfs_proc_rmdir(struct inode *dir, const 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; @@ -494,15 +480,10 @@ nfs_proc_readdir(struct dentry *dentry, .count = count, .pages = pages, }; - 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); @@ -515,16 +496,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; @@ -543,16 +519,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 2017-04-08 09:35:38.000000000 +0200 +++ b/fs/nfs/unlink.c 2017-04-08 15:45:05.755381660 +0200 @@ -164,6 +164,7 @@ static int nfs_call_unlink(struct dentry static int nfs_async_unlink(struct dentry *dentry, const struct qstr *name) { + struct inode *dir = dentry->d_parent->d_inode; struct nfs_unlinkdata *data; int status = -ENOMEM; void *devname_garbage = NULL; @@ -176,7 +177,12 @@ nfs_async_unlink(struct dentry *dentry, goto out_free; data->args.name.len = name->len; - 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_name; @@ -338,7 +344,12 @@ nfs_async_rename(struct inode *old_dir, return ERR_PTR(-ENOMEM); task_setup_data.callback_data = data; - data->cred = rpc_lookup_cred(); + if (NFS_PROTO(old_dir)->version > 3) + data->cred = rpc_lookup_cred(NULL); + else { + struct rpc_groups fsg = { 2, { old_dir->i_gid, new_dir->i_gid } }; + data->cred = rpc_lookup_cred(&fsg); + } if (IS_ERR(data->cred)) { struct rpc_task *task = ERR_CAST(data->cred); kfree(data); diff -Nurp a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h --- a/include/linux/nfs_fs.h 2017-04-08 09:35:38.000000000 +0200 +++ b/include/linux/nfs_fs.h 2017-04-08 15:45:05.756381658 +0200 @@ -451,7 +451,8 @@ extern const struct dentry_operations nf extern void nfs_force_lookup_revalidate(struct inode *dir); extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, - struct nfs_fattr *fattr, struct nfs4_label *label); + struct nfs_fattr *fattr, struct nfs4_label *label, + 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 2017-04-08 09:35:38.000000000 +0200 +++ b/include/linux/nfs_xdr.h 2017-04-08 15:45:05.758381654 +0200 @@ -1536,13 +1536,14 @@ struct nfs_renamedata { struct nfs_access_entry; struct nfs_client; struct rpc_timeout; +struct rpc_groups; struct nfs_subversion; struct nfs_mount_info; struct nfs_client_initdata; struct nfs_pageio_descriptor; /* - * RPC procedure vector for NFSv2/NFSv3 demuxing + * RPC procedure vector for NFSv2/NFSv3/NFSv4 demuxing */ struct nfs_rpc_ops { u32 version; /* Protocol version */ @@ -1560,31 +1561,33 @@ struct nfs_rpc_ops { int (*getattr) (struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *); int (*setattr) (struct dentry *, struct nfs_fattr *, - struct iattr *); + struct iattr *, struct rpc_groups *); int (*lookup) (struct inode *, const struct qstr *, struct nfs_fh *, struct nfs_fattr *, - struct nfs4_label *); + struct nfs4_label *, 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); - int (*remove) (struct inode *, const struct qstr *); + struct iattr *, int, struct rpc_groups *); + int (*remove) (struct inode *, const struct qstr *, struct rpc_groups *); void (*unlink_setup) (struct rpc_message *, struct inode *dir); void (*unlink_rpc_prepare) (struct rpc_task *, struct nfs_unlinkdata *); int (*unlink_done) (struct rpc_task *, struct inode *); void (*rename_setup) (struct rpc_message *msg, struct inode *dir); void (*rename_rpc_prepare)(struct rpc_task *task, struct nfs_renamedata *); int (*rename_done) (struct rpc_task *task, struct inode *old_dir, struct inode *new_dir); - int (*link) (struct inode *, struct inode *, const struct qstr *); + int (*link) (struct inode *, struct inode *, const 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 *, const struct qstr *); + unsigned int, struct iattr *, struct rpc_groups *); + int (*mkdir) (struct inode *, struct dentry *, struct iattr *, + struct rpc_groups *); + int (*rmdir) (struct inode *, const 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 2017-04-08 09:35:38.000000000 +0200 +++ b/include/linux/sunrpc/auth.h 2017-04-08 15:45:05.759381652 +0200 @@ -42,11 +42,16 @@ enum { key will expire soon */ }; +struct rpc_groups { + int ngroups; + kgid_t groups[RPC_MAXGROUPS]; +}; + /* Work around the lack of a VFS credential */ struct auth_cred { kuid_t uid; kgid_t gid; - struct group_info *group_info; + struct rpc_groups rg; const char *principal; unsigned long ac_flags; unsigned char machine_cred : 1; @@ -171,8 +176,8 @@ void rpcauth_remove_module(void); void rpc_destroy_generic_auth(void); void rpc_destroy_authunix(void); -struct rpc_cred * rpc_lookup_cred(void); -struct rpc_cred * rpc_lookup_cred_nonblock(void); +struct rpc_cred * rpc_lookup_cred(struct rpc_groups *); +struct rpc_cred * rpc_lookup_cred_nonblock(struct rpc_groups *); struct rpc_cred * rpc_lookup_generic_cred(struct auth_cred *, int, gfp_t); struct rpc_cred * rpc_lookup_machine_cred(const char *service_name); int rpcauth_register(const struct rpc_authops *); @@ -187,7 +192,7 @@ int rpcauth_get_gssinfo(rpc_authflavor int rpcauth_list_flavors(rpc_authflavor_t *, int); struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int, gfp_t); 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 2017-04-08 09:35:38.000000000 +0200 +++ b/include/linux/sunrpc/msg_prot.h 2017-04-08 15:45:05.760381650 +0200 @@ -79,6 +79,7 @@ enum rpc_auth_stat { }; #define RPC_MAXNETNAMELEN 256 +#define RPC_MAXGROUPS 16 /* * From RFC 1831: diff -Nurp a/net/sunrpc/auth.c b/net/sunrpc/auth.c --- a/net/sunrpc/auth.c 2017-04-08 09:35:38.000000000 +0200 +++ b/net/sunrpc/auth.c 2017-04-08 15:45:05.760381650 +0200 @@ -15,11 +15,15 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) # define RPCDBG_FACILITY RPCDBG_AUTH +# define RG(rg,i) ((i) < (rg).ngroups ? (int)from_kgid(&init_user_ns, (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; @@ -618,20 +622,20 @@ 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 = cred->group_info; + rpcauth_add_groups(&acred, rg); ret = auth->au_ops->lookup_cred(auth, &acred, flags); + + dprintk("RPC: cred %p\n", ret); return ret; } EXPORT_SYMBOL_GPL(rpcauth_lookupcred); @@ -683,7 +687,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 @@ -708,6 +712,42 @@ 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; + kgid_t gid; + const struct cred *cred = current_cred(); + + acred->gid = cred->fsgid; + 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] = cred->group_info->gid[i]; + acred->rg.ngroups = n; + } +} + 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 2017-04-08 09:35:38.000000000 +0200 +++ b/net/sunrpc/auth_generic.c 2017-04-08 15:45:05.761381648 +0200 @@ -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); @@ -45,9 +45,9 @@ rpc_lookup_generic_cred(struct auth_cred } EXPORT_SYMBOL_GPL(rpc_lookup_generic_cred); -struct rpc_cred *rpc_lookup_cred_nonblock(void) +struct rpc_cred *rpc_lookup_cred_nonblock(struct rpc_groups *rg) { - return rpcauth_lookupcred(&generic_auth, RPCAUTH_LOOKUP_RCU); + return rpcauth_lookupcred(&generic_auth, rg, RPCAUTH_LOOKUP_RCU); } EXPORT_SYMBOL_GPL(rpc_lookup_cred_nonblock); @@ -107,14 +107,8 @@ 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; + gcred->acred = *acred; gcred->acred.ac_flags = 0; - if (gcred->acred.group_info != NULL) - get_group_info(gcred->acred.group_info); - gcred->acred.machine_cred = acred->machine_cred; - gcred->acred.principal = acred->principal; dprintk("RPC: allocated %s cred %p for uid %d gid %d\n", gcred->acred.machine_cred ? "machine" : "generic", @@ -130,8 +124,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); } @@ -176,19 +168,12 @@ generic_match(struct auth_cred *acred, s gcred->acred.machine_cred != 0) 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) + if (gcred->acred.rg.ngroups != acred->rg.ngroups) goto out_nomatch; - for (i = 0; i < gcred->acred.group_info->ngroups; i++) { - if (!gid_eq(gcred->acred.group_info->gid[i], - acred->group_info->gid[i])) + for (i = 0; i < gcred->acred.rg.ngroups; i++) { + if (!gid_eq(gcred->acred.rg.groups[i], acred->rg.groups[i])) goto out_nomatch; } -out_match: return 1; out_nomatch: return 0; diff -Nurp a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c --- a/net/sunrpc/auth_unix.c 2017-04-08 09:35:38.000000000 +0200 +++ b/net/sunrpc/auth_unix.c 2017-04-08 15:45:05.761381648 +0200 @@ -12,14 +12,11 @@ #include #include #include -#include - -#define NFS_NGROUPS 16 struct unx_cred { struct rpc_cred uc_base; kgid_t uc_gid; - kgid_t uc_gids[NFS_NGROUPS]; + kgid_t uc_gids[RPC_MAXGROUPS]; }; #define uc_uid uc_base.cr_uid @@ -67,7 +64,7 @@ static struct rpc_cred * unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags, gfp_t gfp) { struct unx_cred *cred; - unsigned int groups = 0; + unsigned int groups = acred->rg.ngroups; unsigned int i; dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", @@ -80,15 +77,10 @@ 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; - cred->uc_gid = acred->gid; for (i = 0; i < groups; i++) - cred->uc_gids[i] = acred->group_info->gid[i]; - if (i < NFS_NGROUPS) + cred->uc_gids[i] = acred->rg.groups[i]; + if (i < RPC_MAXGROUPS) cred->uc_gids[i] = INVALID_GID; return &cred->uc_base; @@ -123,21 +115,17 @@ 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 groups = acred->rg.ngroups; unsigned int i; if (!uid_eq(cred->uc_uid, acred->uid) || !gid_eq(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 (!gid_eq(cred->uc_gids[i], acred->group_info->gid[i])) + if (!gid_eq(cred->uc_gids[i], acred->rg.groups[i])) return 0; - if (groups < NFS_NGROUPS && gid_valid(cred->uc_gids[groups])) + if (groups < RPC_MAXGROUPS && gid_valid(cred->uc_gids[groups])) return 0; return 1; } @@ -166,7 +154,7 @@ unx_marshal(struct rpc_task *task, __be3 *p++ = htonl((u32) from_kuid(&init_user_ns, cred->uc_uid)); *p++ = htonl((u32) from_kgid(&init_user_ns, cred->uc_gid)); hold = p++; - for (i = 0; i < 16 && gid_valid(cred->uc_gids[i]); i++) + for (i = 0; i < RPC_MAXGROUPS && gid_valid(cred->uc_gids[i]); i++) *p++ = htonl((u32) from_kgid(&init_user_ns, 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 2017-04-08 09:35:38.000000000 +0200 +++ b/net/sunrpc/svcauth_unix.c 2017-04-08 15:45:05.762381646 +0200 @@ -810,7 +810,7 @@ svcauth_unix_accept(struct svc_rqst *rqs cred->cr_uid = make_kuid(&init_user_ns, svc_getnl(argv)); /* uid */ cred->cr_gid = make_kgid(&init_user_ns, 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)