--- ./fs/nfs/dir.c.orig 2004-12-26 13:41:47.000000000 +0100 +++ ./fs/nfs/dir.c 2005-01-30 16:54:06.000000000 +0100 @@ -1593,7 +1593,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); res = nfs_do_access(inode, cred, mask); put_rpccred(cred); unlock_kernel(); --- ./fs/nfs/inode.c.orig 2005-01-30 16:39:01.000000000 +0100 +++ ./fs/nfs/inode.c 2005-01-30 16:54:06.000000000 +0100 @@ -910,7 +910,7 @@ struct nfs_open_context *ctx; struct rpc_cred *cred; - if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL) + if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, NULL, 0)) == NULL) return -ENOMEM; ctx = alloc_nfs_open_context(filp->f_dentry, cred); if (ctx == NULL) @@ -1615,7 +1615,7 @@ } clnt->cl_chatty = 1; clp->cl_rpcclient = clnt; - clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, 0); + clp->cl_cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0); memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); nfs_idmap_new(clp); } --- ./fs/nfs/nfs4proc.c.orig 2005-01-30 16:39:01.000000000 +0100 +++ ./fs/nfs/nfs4proc.c 2005-01-30 16:54:06.000000000 +0100 @@ -784,7 +784,7 @@ BUG_ON(nd->intent.open.flags & O_CREAT); } - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); state = nfs4_do_open(dir, &dentry->d_name, nd->intent.open.flags, &attr, cred); put_rpccred(cred); if (IS_ERR(state)) @@ -799,7 +799,7 @@ struct nfs4_state *state; struct inode *inode; - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); state = nfs4_open_delegated(dentry->d_inode, openflags, cred); if (IS_ERR(state)) state = nfs4_do_open(dir, &dentry->d_name, openflags, NULL, cred); @@ -1019,7 +1019,7 @@ fattr->valid = 0; if (size_change) { - struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); + struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, NULL, 0); state = nfs4_find_state(inode, cred, FMODE_WRITE); if (state == NULL) { state = nfs4_open_delegated(dentry->d_inode, @@ -1334,7 +1334,7 @@ struct nfs4_state *state = NULL; struct rpc_cred *cred; - cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, 0); + cred = rpcauth_lookupcred(NFS_SERVER(dir)->client->cl_auth, NULL, 0); state = nfs4_do_open(dir, name, flags, sattr, cred); put_rpccred(cred); if (!IS_ERR(state)) { @@ -2011,7 +2011,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 (unlikely(cred == NULL)) return -ENOMEM; ctx = alloc_nfs_open_context(dentry, cred); --- ./fs/nfs/unlink.c.orig 2004-07-12 23:03:04.000000000 +0200 +++ ./fs/nfs/unlink.c 2005-01-30 16:54:06.000000000 +0100 @@ -183,7 +183,7 @@ spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); - data->cred = rpcauth_lookupcred(clnt->cl_auth, 0); + data->cred = rpcauth_lookupcred(clnt->cl_auth, NULL, 0); rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL); status = 0; --- ./include/linux/sunrpc/auth.h.orig 2005-01-30 16:39:23.000000000 +0100 +++ ./include/linux/sunrpc/auth.h 2005-01-30 16:54:06.000000000 +0100 @@ -24,11 +24,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; }; /* @@ -93,6 +98,7 @@ void (*destroy)(struct rpc_auth *); struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int); + void (*cr_add_groups)(struct auth_cred *, struct rpc_groups *); }; struct rpc_credops { @@ -121,7 +127,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-01-30 16:39:23.000000000 +0100 +++ ./net/sunrpc/auth.c 2005-01-30 16:54:06.000000000 +0100 @@ -253,20 +253,18 @@ } struct rpc_cred * -rpcauth_lookupcred(struct rpc_auth *auth, int taskflags) +rpcauth_lookupcred(struct rpc_auth *auth, struct rpc_groups *rg, int taskflags) { struct auth_cred acred; struct rpc_cred *ret; - get_group_info(current->group_info); acred.uid = current->fsuid; - acred.gid = current->fsgid; - acred.group_info = current->group_info; + if (auth->au_ops->cr_add_groups) + auth->au_ops->cr_add_groups(&acred, rg); dprintk("RPC: looking up %s cred\n", auth->au_ops->au_name); ret = rpcauth_lookup_credcache(auth, &acred, taskflags); - put_group_info(current->group_info); return ret; } --- ./net/sunrpc/clnt.c.orig 2005-01-30 16:39:23.000000000 +0100 +++ ./net/sunrpc/clnt.c 2005-01-30 16:57:23.000000000 +0100 @@ -430,7 +430,7 @@ /* we copied msg->rpc_cred, hold it */ get_rpccred(task->tk_msg.rpc_cred); } else { - task->tk_msg.rpc_cred = rpcauth_lookupcred(task->tk_auth, task->tk_flags); + task->tk_msg.rpc_cred = rpcauth_lookupcred(task->tk_auth, NULL, task->tk_flags); if (task->tk_msg.rpc_cred == NULL) task->tk_status = -ENOMEM; } --- ./net/sunrpc/auth_unix.c.orig 2005-01-30 16:38:58.000000000 +0100 +++ ./net/sunrpc/auth_unix.c 2005-01-30 16:54:06.000000000 +0100 @@ -63,7 +63,6 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) { struct unx_cred *cred; - int i; dprintk("RPC: allocating UNIX cred for uid %d gid %d\n", acred->uid, acred->gid); @@ -78,22 +77,50 @@ 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, struct rpc_groups *rg) +{ + int i, n; + gid_t gid; + + acred->gid = current->fsgid; + get_group_info(current->group_info); + n = current->group_info->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; + } + } else { + for (i = 0; i < n; ++i) + acred->rg.groups[i] = GROUP_AT(current->group_info, i); + } + put_group_info(current->group_info); + acred->rg.ngroups = n; +} + static void unx_destroy_cred(struct rpc_cred *cred) { @@ -109,22 +136,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 @@ -210,6 +234,7 @@ .create = unx_create, .destroy = unx_destroy, .crcreate = unx_create_cred, + .cr_add_groups = unx_add_groups, }; static