--- ./fs/nfs/dir.c.orig 2005-06-12 14:02:31.000000000 +0200 +++ ./fs/nfs/dir.c 2005-06-13 22:35:24.000000000 +0200 @@ -1537,7 +1537,7 @@ if (!NFS_PROTO(inode)->access) goto out_notsup; - cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0); if (!IS_ERR(cred)) { res = nfs_do_access(inode, cred, mask); put_rpccred(cred); --- ./fs/nfs/inode.c.orig 2005-06-12 14:02:31.000000000 +0200 +++ ./fs/nfs/inode.c 2005-06-13 22:35:24.000000000 +0200 @@ -934,7 +934,7 @@ struct nfs_open_context *ctx; struct rpc_cred *cred; - cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0); if (IS_ERR(cred)) return PTR_ERR(cred); ctx = alloc_nfs_open_context(filp->f_dentry, cred); @@ -1631,7 +1631,7 @@ clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clp->cl_rpcclient = clnt; - clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); + clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0); if (IS_ERR(clp->cl_cred)) { up_write(&clp->cl_sem); err = PTR_ERR(clp->cl_cred); --- ./fs/nfs/nfs4proc.c.orig 2005-06-12 14:02:31.000000000 +0200 +++ ./fs/nfs/nfs4proc.c 2005-06-13 22:35:24.000000000 +0200 @@ -886,7 +886,7 @@ BUG_ON(nd->intent.open.flags & O_CREAT); } - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); if (IS_ERR(cred)) return (struct inode *)cred; state = nfs4_do_open(dir, dentry, nd->intent.open.flags, &attr, cred); @@ -903,7 +903,7 @@ struct nfs4_state *state; struct inode *inode; - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); if (IS_ERR(cred)) return PTR_ERR(cred); state = nfs4_open_delegated(dentry->d_inode, openflags, cred); @@ -1125,7 +1125,7 @@ fattr->valid = 0; if (size_change) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0); if (IS_ERR(cred)) return PTR_ERR(cred); state = nfs4_find_state(inode, cred, FMODE_WRITE); @@ -1442,7 +1442,7 @@ struct rpc_cred *cred; int status = 0; - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); if (IS_ERR(cred)) { status = PTR_ERR(cred); goto out; @@ -2126,7 +2126,7 @@ /* Find our open stateid */ - cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0); if (IS_ERR(cred)) return PTR_ERR(cred); ctx = alloc_nfs_open_context(dentry, cred); --- ./fs/nfs/unlink.c.orig 2005-06-12 14:02:31.000000000 +0200 +++ ./fs/nfs/unlink.c 2005-06-13 22:35:24.000000000 +0200 @@ -167,7 +167,7 @@ goto out; memset(data, 0, sizeof(*data)); - data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + data->cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0); if (IS_ERR(data->cred)) { status = PTR_ERR(data->cred); goto out_free; --- ./fs/nfsd/nfs4callback.c.orig 2005-06-12 14:02:31.000000000 +0200 +++ ./fs/nfsd/nfs4callback.c 2005-06-13 22:35:24.000000000 +0200 @@ -353,17 +353,18 @@ { struct auth_cred acred; struct rpc_clnt *clnt = clp->cl_callback.cb_client; + struct svc_cred *cr = &clp->cl_cred; struct rpc_cred *ret; - get_group_info(clp->cl_cred.cr_group_info); - acred.uid = clp->cl_cred.cr_uid; - acred.gid = clp->cl_cred.cr_gid; - acred.group_info = clp->cl_cred.cr_group_info; - dprintk("NFSD: looking up %s cred\n", clnt->cl_auth->au_ops->au_name); + + acred.uid = cr->cr_uid; + if (clnt->cl_auth->au_ops->cr_add_groups) + clnt->cl_auth->au_ops->cr_add_groups(&acred, cr->cr_gid, cr->cr_group_info, NULL); ret = rpcauth_lookup_credcache(clnt->cl_auth, &acred, taskflags); - put_group_info(clp->cl_cred.cr_group_info); + + dprintk("NFSD: cred %p\n", ret); return ret; } --- ./include/linux/sunrpc/auth.h.orig 2005-06-13 22:34:57.000000000 +0200 +++ ./include/linux/sunrpc/auth.h 2005-06-13 22:35:24.000000000 +0200 @@ -12,6 +12,7 @@ #ifdef __KERNEL__ #include +#include #include #include #include @@ -24,11 +25,16 @@ /* Maximum size (in bytes) of an rpc credential or verifier */ #define RPC_MAX_AUTH_SIZE (400) +struct rpc_groups { + int ngroups; + gid_t groups[RPC_MAXGROUPS]; +}; + /* Work around the lack of a VFS credential */ struct auth_cred { - uid_t uid; - gid_t gid; - struct group_info *group_info; + uid_t uid; + gid_t gid; + struct rpc_groups rg; }; /* @@ -96,6 +102,7 @@ struct rpc_cred * (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int); struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int); + void (*cr_add_groups)(struct auth_cred *, gid_t, struct group_info *, struct rpc_groups *); }; struct rpc_credops { @@ -123,7 +130,7 @@ struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); void rpcauth_destroy(struct rpc_auth *); struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); -struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); +struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, struct rpc_groups *, int); void put_rpccred(struct rpc_cred *); u32 * rpcauth_marshcred(struct rpc_task *, u32 *); u32 * rpcauth_checkverf(struct rpc_task *, u32 *); --- ./net/sunrpc/auth.c.orig 2005-06-13 22:34:57.000000000 +0200 +++ ./net/sunrpc/auth.c 2005-06-13 22:35:24.000000000 +0200 @@ -239,20 +239,20 @@ } struct rpc_cred * -rpcauth_lookupcred(struct rpc_auth *auth, int taskflags) +rpcauth_lookupcred(struct rpc_auth *auth, struct rpc_groups *rg, int taskflags) { struct auth_cred acred = { .uid = current->fsuid, - .gid = current->fsgid, - .group_info = current->group_info, }; struct rpc_cred *ret; - dprintk("RPC: looking up %s cred\n", - auth->au_ops->au_name); - get_group_info(acred.group_info); + dprintk("RPC: looking up %s cred\n", auth->au_ops->au_name); + + if (auth->au_ops->cr_add_groups) + auth->au_ops->cr_add_groups(&acred, current->fsgid, current->group_info, rg); ret = auth->au_ops->lookup_cred(auth, &acred, taskflags); - put_group_info(acred.group_info); + + dprintk("RPC: cred %p\n", ret); return ret; } --- ./net/sunrpc/auth_unix.c.orig 2005-06-13 22:34:57.000000000 +0200 +++ ./net/sunrpc/auth_unix.c 2005-06-13 22:35:41.000000000 +0200 @@ -30,6 +30,7 @@ #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH +# define RG(rg,i) ((i) < (rg).ngroups ? (int)(rg).groups[i] : -1) #endif static struct rpc_auth unix_auth; @@ -65,7 +66,6 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) { struct unx_cred *cred; - int i; dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", acred->uid, acred->gid); @@ -80,22 +80,54 @@ cred->uc_gid = 0; cred->uc_gids[0] = NOGROUP; } else { - int groups = acred->group_info->ngroups; - if (groups > RPC_MAXGROUPS) - groups = RPC_MAXGROUPS; - + int n = acred->rg.ngroups; cred->uc_uid = acred->uid; cred->uc_gid = acred->gid; - for (i = 0; i < groups; i++) - cred->uc_gids[i] = GROUP_AT(acred->group_info, i); - if (i < RPC_MAXGROUPS) - cred->uc_gids[i] = NOGROUP; + memcpy(cred->uc_gids, acred->rg.groups, n * sizeof (gid_t)); + if (n < RPC_MAXGROUPS) + cred->uc_gids[n] = NOGROUP; } cred->uc_base.cr_ops = &unix_credops; return (struct rpc_cred *) cred; } +/* + * Add groups to acred. When there are too many then try to be smart by + * picking only the relevant ones from our secondary group list. + */ +static void +unx_add_groups(struct auth_cred *acred, gid_t gid, struct group_info *gi, struct rpc_groups *rg) +{ + int i, n; + + acred->gid = gid; + get_group_info(gi); + n = gi->ngroups; + if (n <= RPC_MAXGROUPS) + rg = NULL; + else + n = RPC_MAXGROUPS; /* too many groups for AUTH_UNIX */ + if (rg) { + n = 0; /* pick the few relevant groups we're a member of */ + for (i = 0; i < rg->ngroups; ++i) { + gid = rg->groups[i]; + if (in_group_p(gid)) + acred->rg.groups[n++] = gid; + } + acred->rg.ngroups = n; + dprintk("RPC: unx_add_groups(): rg=%d:%d,%d,%d -> %d:%d,%d,%d\n", + rg->ngroups, RG(*rg, 0), RG(*rg, 1), RG(*rg, 2), + n, RG(acred->rg, 0), RG(acred->rg, 1), RG(acred->rg, 2)); + } else { + dprintk("RPC: unx_add_groups(): ngroups=%d\n", gi->ngroups); + for (i = 0; i < n; ++i) + acred->rg.groups[i] = GROUP_AT(gi, i); + acred->rg.ngroups = n; + } + put_group_info(gi); +} + static void unx_destroy_cred(struct rpc_cred *cred) { @@ -111,22 +143,19 @@ unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int taskflags) { struct unx_cred *cred = (struct unx_cred *) rcred; - int i; if (!(taskflags & RPC_TASK_ROOTCREDS)) { - int groups; + int n = acred->rg.ngroups; if (cred->uc_uid != acred->uid || cred->uc_gid != acred->gid) return 0; - groups = acred->group_info->ngroups; - if (groups > RPC_MAXGROUPS) - groups = RPC_MAXGROUPS; - for (i = 0; i < groups ; i++) - if (cred->uc_gids[i] != GROUP_AT(acred->group_info, i)) - return 0; - return 1; + if (n < RPC_MAXGROUPS && cred->uc_gids[n] != NOGROUP) + return 0; + + return !memcmp(cred->uc_gids, acred->rg.groups, + n * sizeof (gid_t)); } return (cred->uc_uid == 0 && cred->uc_gid == 0 @@ -213,6 +242,7 @@ .destroy = unx_destroy, .lookup_cred = unx_lookup_cred, .crcreate = unx_create_cred, + .cr_add_groups = unx_add_groups, }; static --- ./net/sunrpc/clnt.c.orig 2005-06-13 22:34:57.000000000 +0200 +++ ./net/sunrpc/clnt.c 2005-06-13 22:35:24.000000000 +0200 @@ -432,7 +432,7 @@ /* we copied msg->rpc_cred, hold it */ get_rpccred(task->tk_msg.rpc_cred); } else { - cred = rpcauth_lookupcred(task->tk_auth, task->tk_flags); + cred = rpcauth_lookupcred(task->tk_auth, NULL, task->tk_flags); if (!IS_ERR(cred)) task->tk_msg.rpc_cred = cred; else