Merge branch 'for-3.5' of git://linux-nfs.org/~bfields/linux
Pull the rest of the nfsd commits from Bruce Fields: "... and then I cherry-picked the remainder of the patches from the head of my previous branch" This is the rest of the original nfsd branch, rebased without the delegation stuff that I thought really needed to be redone. I don't like rebasing things like this in general, but in this situation this was the lesser of two evils. * 'for-3.5' of git://linux-nfs.org/~bfields/linux: (50 commits) nfsd4: fix, consolidate client_has_state nfsd4: don't remove rebooted client record until confirmation nfsd4: remove some dprintk's and a comment nfsd4: return "real" sequence id in confirmed case nfsd4: fix exchange_id to return confirm flag nfsd4: clarify that renewing expired client is a bug nfsd4: simpler ordering of setclientid_confirm checks nfsd4: setclientid: remove pointless assignment nfsd4: fix error return in non-matching-creds case nfsd4: fix setclientid_confirm same_cred check nfsd4: merge 3 setclientid cases to 2 nfsd4: pull out common code from setclientid cases nfsd4: merge last two setclientid cases nfsd4: setclientid/confirm comment cleanup nfsd4: setclientid remove unnecessary terms from a logical expression nfsd4: move rq_flavor into svc_cred nfsd4: stricter cred comparison for setclientid/exchange_id nfsd4: move principal name into svc_cred nfsd4: allow removing clients not holding state nfsd4: rearrange exchange_id logic to simplify ...
This commit is contained in:
Коммит
419f431949
149
fs/lockd/svc.c
149
fs/lockd/svc.c
|
@ -251,39 +251,40 @@ out_err:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int lockd_up_net(struct net *net)
|
||||
static int lockd_up_net(struct svc_serv *serv, struct net *net)
|
||||
{
|
||||
struct lockd_net *ln = net_generic(net, lockd_net_id);
|
||||
struct svc_serv *serv = nlmsvc_rqst->rq_server;
|
||||
int error;
|
||||
|
||||
if (ln->nlmsvc_users)
|
||||
if (ln->nlmsvc_users++)
|
||||
return 0;
|
||||
|
||||
error = svc_rpcb_setup(serv, net);
|
||||
error = svc_bind(serv, net);
|
||||
if (error)
|
||||
goto err_rpcb;
|
||||
goto err_bind;
|
||||
|
||||
error = make_socks(serv, net);
|
||||
if (error < 0)
|
||||
goto err_socks;
|
||||
dprintk("lockd_up_net: per-net data created; net=%p\n", net);
|
||||
return 0;
|
||||
|
||||
err_socks:
|
||||
svc_rpcb_cleanup(serv, net);
|
||||
err_rpcb:
|
||||
err_bind:
|
||||
ln->nlmsvc_users--;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void lockd_down_net(struct net *net)
|
||||
static void lockd_down_net(struct svc_serv *serv, struct net *net)
|
||||
{
|
||||
struct lockd_net *ln = net_generic(net, lockd_net_id);
|
||||
struct svc_serv *serv = nlmsvc_rqst->rq_server;
|
||||
|
||||
if (ln->nlmsvc_users) {
|
||||
if (--ln->nlmsvc_users == 0) {
|
||||
nlm_shutdown_hosts_net(net);
|
||||
svc_shutdown_net(serv, net);
|
||||
dprintk("lockd_down_net: per-net data destroyed; net=%p\n", net);
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR "lockd_down_net: no users! task=%p, net=%p\n",
|
||||
|
@ -292,21 +293,60 @@ static void lockd_down_net(struct net *net)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Bring up the lockd process if it's not already up.
|
||||
*/
|
||||
int lockd_up(struct net *net)
|
||||
static int lockd_start_svc(struct svc_serv *serv)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (nlmsvc_rqst)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Create the kernel thread and wait for it to start.
|
||||
*/
|
||||
nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
|
||||
if (IS_ERR(nlmsvc_rqst)) {
|
||||
error = PTR_ERR(nlmsvc_rqst);
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: svc_rqst allocation failed, error=%d\n",
|
||||
error);
|
||||
goto out_rqst;
|
||||
}
|
||||
|
||||
svc_sock_update_bufs(serv);
|
||||
serv->sv_maxconn = nlm_max_connections;
|
||||
|
||||
nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name);
|
||||
if (IS_ERR(nlmsvc_task)) {
|
||||
error = PTR_ERR(nlmsvc_task);
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: kthread_run failed, error=%d\n", error);
|
||||
goto out_task;
|
||||
}
|
||||
dprintk("lockd_up: service started\n");
|
||||
return 0;
|
||||
|
||||
out_task:
|
||||
svc_exit_thread(nlmsvc_rqst);
|
||||
nlmsvc_task = NULL;
|
||||
out_rqst:
|
||||
nlmsvc_rqst = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
static struct svc_serv *lockd_create_svc(void)
|
||||
{
|
||||
struct svc_serv *serv;
|
||||
int error = 0;
|
||||
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
/*
|
||||
* Check whether we're already up and running.
|
||||
*/
|
||||
if (nlmsvc_rqst) {
|
||||
error = lockd_up_net(net);
|
||||
goto out;
|
||||
/*
|
||||
* Note: increase service usage, because later in case of error
|
||||
* svc_destroy() will be called.
|
||||
*/
|
||||
svc_get(nlmsvc_rqst->rq_server);
|
||||
return nlmsvc_rqst->rq_server;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -317,59 +357,53 @@ int lockd_up(struct net *net)
|
|||
printk(KERN_WARNING
|
||||
"lockd_up: no pid, %d users??\n", nlmsvc_users);
|
||||
|
||||
error = -ENOMEM;
|
||||
serv = svc_create(&nlmsvc_program, LOCKD_BUFSIZE, NULL);
|
||||
if (!serv) {
|
||||
printk(KERN_WARNING "lockd_up: create service failed\n");
|
||||
goto out;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
dprintk("lockd_up: service created\n");
|
||||
return serv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bring up the lockd process if it's not already up.
|
||||
*/
|
||||
int lockd_up(struct net *net)
|
||||
{
|
||||
struct svc_serv *serv;
|
||||
int error;
|
||||
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
|
||||
serv = lockd_create_svc();
|
||||
if (IS_ERR(serv)) {
|
||||
error = PTR_ERR(serv);
|
||||
goto err_create;
|
||||
}
|
||||
|
||||
error = make_socks(serv, net);
|
||||
error = lockd_up_net(serv, net);
|
||||
if (error < 0)
|
||||
goto destroy_and_out;
|
||||
goto err_net;
|
||||
|
||||
/*
|
||||
* Create the kernel thread and wait for it to start.
|
||||
*/
|
||||
nlmsvc_rqst = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
|
||||
if (IS_ERR(nlmsvc_rqst)) {
|
||||
error = PTR_ERR(nlmsvc_rqst);
|
||||
nlmsvc_rqst = NULL;
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: svc_rqst allocation failed, error=%d\n",
|
||||
error);
|
||||
goto destroy_and_out;
|
||||
}
|
||||
|
||||
svc_sock_update_bufs(serv);
|
||||
serv->sv_maxconn = nlm_max_connections;
|
||||
|
||||
nlmsvc_task = kthread_run(lockd, nlmsvc_rqst, serv->sv_name);
|
||||
if (IS_ERR(nlmsvc_task)) {
|
||||
error = PTR_ERR(nlmsvc_task);
|
||||
svc_exit_thread(nlmsvc_rqst);
|
||||
nlmsvc_task = NULL;
|
||||
nlmsvc_rqst = NULL;
|
||||
printk(KERN_WARNING
|
||||
"lockd_up: kthread_run failed, error=%d\n", error);
|
||||
goto destroy_and_out;
|
||||
}
|
||||
error = lockd_start_svc(serv);
|
||||
if (error < 0)
|
||||
goto err_start;
|
||||
|
||||
nlmsvc_users++;
|
||||
/*
|
||||
* Note: svc_serv structures have an initial use count of 1,
|
||||
* so we exit through here on both success and failure.
|
||||
*/
|
||||
destroy_and_out:
|
||||
err_net:
|
||||
svc_destroy(serv);
|
||||
out:
|
||||
if (!error) {
|
||||
struct lockd_net *ln = net_generic(net, lockd_net_id);
|
||||
|
||||
ln->nlmsvc_users++;
|
||||
nlmsvc_users++;
|
||||
}
|
||||
err_create:
|
||||
mutex_unlock(&nlmsvc_mutex);
|
||||
return error;
|
||||
|
||||
err_start:
|
||||
lockd_down_net(serv, net);
|
||||
goto err_net;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lockd_up);
|
||||
|
||||
|
@ -380,11 +414,10 @@ void
|
|||
lockd_down(struct net *net)
|
||||
{
|
||||
mutex_lock(&nlmsvc_mutex);
|
||||
lockd_down_net(nlmsvc_rqst->rq_server, net);
|
||||
if (nlmsvc_users) {
|
||||
if (--nlmsvc_users) {
|
||||
lockd_down_net(net);
|
||||
if (--nlmsvc_users)
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
printk(KERN_ERR "lockd_down: no users! task=%p\n",
|
||||
nlmsvc_task);
|
||||
|
@ -396,7 +429,9 @@ lockd_down(struct net *net)
|
|||
BUG();
|
||||
}
|
||||
kthread_stop(nlmsvc_task);
|
||||
dprintk("lockd_down: service stopped\n");
|
||||
svc_exit_thread(nlmsvc_rqst);
|
||||
dprintk("lockd_down: service destroyed\n");
|
||||
nlmsvc_task = NULL;
|
||||
nlmsvc_rqst = NULL;
|
||||
out:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/bc_xprt.h>
|
||||
#include <linux/nsproxy.h>
|
||||
|
||||
#include <net/inet_sock.h>
|
||||
|
||||
|
@ -253,6 +254,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
|
|||
char svc_name[12];
|
||||
int ret = 0;
|
||||
int minorversion_setup;
|
||||
struct net *net = current->nsproxy->net_ns;
|
||||
|
||||
mutex_lock(&nfs_callback_mutex);
|
||||
if (cb_info->users++ || cb_info->task != NULL) {
|
||||
|
@ -265,6 +267,12 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
ret = svc_bind(serv, net);
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "NFS: bind callback service failed\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion,
|
||||
serv, xprt, &rqstp, &callback_svc);
|
||||
if (!minorversion_setup) {
|
||||
|
@ -306,6 +314,8 @@ out_err:
|
|||
dprintk("NFS: Couldn't create callback socket or server thread; "
|
||||
"err = %d\n", ret);
|
||||
cb_info->users--;
|
||||
if (serv)
|
||||
svc_shutdown_net(serv, net);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -320,6 +330,7 @@ void nfs_callback_down(int minorversion)
|
|||
cb_info->users--;
|
||||
if (cb_info->users == 0 && cb_info->task != NULL) {
|
||||
kthread_stop(cb_info->task);
|
||||
svc_shutdown_net(cb_info->serv, current->nsproxy->net_ns);
|
||||
svc_exit_thread(cb_info->rqst);
|
||||
cb_info->serv = NULL;
|
||||
cb_info->rqst = NULL;
|
||||
|
@ -332,7 +343,7 @@ void nfs_callback_down(int minorversion)
|
|||
int
|
||||
check_gss_callback_principal(struct nfs_client *clp, struct svc_rqst *rqstp)
|
||||
{
|
||||
char *p = svc_gss_principal(rqstp);
|
||||
char *p = rqstp->rq_cred.cr_principal;
|
||||
|
||||
if (rqstp->rq_authop->flavour != RPC_AUTH_GSS)
|
||||
return 1;
|
||||
|
|
|
@ -11,7 +11,7 @@ int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp)
|
|||
struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors;
|
||||
|
||||
for (f = exp->ex_flavors; f < end; f++) {
|
||||
if (f->pseudoflavor == rqstp->rq_flavor)
|
||||
if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
|
||||
return f->flags;
|
||||
}
|
||||
return exp->ex_flags;
|
||||
|
|
|
@ -706,7 +706,7 @@ static struct cache_head *svc_export_alloc(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct cache_detail svc_export_cache_template = {
|
||||
static struct cache_detail svc_export_cache_template = {
|
||||
.owner = THIS_MODULE,
|
||||
.hash_size = EXPORT_HASHMAX,
|
||||
.name = "nfsd.export",
|
||||
|
@ -904,13 +904,13 @@ __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
|
|||
return 0;
|
||||
/* ip-address based client; check sec= export option: */
|
||||
for (f = exp->ex_flavors; f < end; f++) {
|
||||
if (f->pseudoflavor == rqstp->rq_flavor)
|
||||
if (f->pseudoflavor == rqstp->rq_cred.cr_flavor)
|
||||
return 0;
|
||||
}
|
||||
/* defaults in absence of sec= options: */
|
||||
if (exp->ex_nflavors == 0) {
|
||||
if (rqstp->rq_flavor == RPC_AUTH_NULL ||
|
||||
rqstp->rq_flavor == RPC_AUTH_UNIX)
|
||||
if (rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
|
||||
rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)
|
||||
return 0;
|
||||
}
|
||||
return nfserr_wrongsec;
|
||||
|
|
|
@ -58,6 +58,7 @@ static int nfsd_inject_set(void *op_ptr, u64 val)
|
|||
|
||||
static int nfsd_inject_get(void *data, u64 *val)
|
||||
{
|
||||
*val = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -650,9 +650,10 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
|
|||
struct rpc_clnt *client;
|
||||
|
||||
if (clp->cl_minorversion == 0) {
|
||||
if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
|
||||
if (!clp->cl_cred.cr_principal &&
|
||||
(clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
|
||||
return -EINVAL;
|
||||
args.client_name = clp->cl_principal;
|
||||
args.client_name = clp->cl_cred.cr_principal;
|
||||
args.prognumber = conn->cb_prog,
|
||||
args.protocol = XPRT_TRANSPORT_TCP;
|
||||
args.authflavor = clp->cl_flavor;
|
||||
|
|
|
@ -605,7 +605,7 @@ numeric_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namel
|
|||
static __be32
|
||||
do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, uid_t *id)
|
||||
{
|
||||
if (nfs4_disable_idmapping && rqstp->rq_flavor < RPC_AUTH_GSS)
|
||||
if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
|
||||
if (numeric_name_to_id(rqstp, type, name, namelen, id))
|
||||
return 0;
|
||||
/*
|
||||
|
@ -618,7 +618,7 @@ do_name_to_id(struct svc_rqst *rqstp, int type, const char *name, u32 namelen, u
|
|||
static int
|
||||
do_id_to_name(struct svc_rqst *rqstp, int type, uid_t id, char *name)
|
||||
{
|
||||
if (nfs4_disable_idmapping && rqstp->rq_flavor < RPC_AUTH_GSS)
|
||||
if (nfs4_disable_idmapping && rqstp->rq_cred.cr_flavor < RPC_AUTH_GSS)
|
||||
return sprintf(name, "%u", id);
|
||||
return idmap_id_to_name(rqstp, type, id, name);
|
||||
}
|
||||
|
|
|
@ -570,7 +570,7 @@ static ssize_t
|
|||
cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
|
||||
{
|
||||
struct cld_upcall *tmp, *cup;
|
||||
struct cld_msg *cmsg = (struct cld_msg *)src;
|
||||
struct cld_msg __user *cmsg = (struct cld_msg __user *)src;
|
||||
uint32_t xid;
|
||||
struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
|
||||
nfsd_net_id);
|
||||
|
@ -1029,7 +1029,7 @@ rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr)
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct notifier_block nfsd4_cld_block = {
|
||||
static struct notifier_block nfsd4_cld_block = {
|
||||
.notifier_call = rpc_pipefs_event,
|
||||
};
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <linux/sunrpc/clnt.h>
|
||||
#include "xdr4.h"
|
||||
#include "vfs.h"
|
||||
#include "current_stateid.h"
|
||||
|
||||
#define NFSDDBG_FACILITY NFSDDBG_PROC
|
||||
|
||||
|
@ -447,37 +448,69 @@ static struct list_head close_lru;
|
|||
*
|
||||
* which we should reject.
|
||||
*/
|
||||
static void
|
||||
set_access(unsigned int *access, unsigned long bmap) {
|
||||
static unsigned int
|
||||
bmap_to_share_mode(unsigned long bmap) {
|
||||
int i;
|
||||
unsigned int access = 0;
|
||||
|
||||
*access = 0;
|
||||
for (i = 1; i < 4; i++) {
|
||||
if (test_bit(i, &bmap))
|
||||
*access |= i;
|
||||
access |= i;
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
static void
|
||||
set_deny(unsigned int *deny, unsigned long bmap) {
|
||||
int i;
|
||||
|
||||
*deny = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (test_bit(i, &bmap))
|
||||
*deny |= i ;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
static bool
|
||||
test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) {
|
||||
unsigned int access, deny;
|
||||
|
||||
set_access(&access, stp->st_access_bmap);
|
||||
set_deny(&deny, stp->st_deny_bmap);
|
||||
access = bmap_to_share_mode(stp->st_access_bmap);
|
||||
deny = bmap_to_share_mode(stp->st_deny_bmap);
|
||||
if ((access & open->op_share_deny) || (deny & open->op_share_access))
|
||||
return 0;
|
||||
return 1;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* set share access for a given stateid */
|
||||
static inline void
|
||||
set_access(u32 access, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
__set_bit(access, &stp->st_access_bmap);
|
||||
}
|
||||
|
||||
/* clear share access for a given stateid */
|
||||
static inline void
|
||||
clear_access(u32 access, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
__clear_bit(access, &stp->st_access_bmap);
|
||||
}
|
||||
|
||||
/* test whether a given stateid has access */
|
||||
static inline bool
|
||||
test_access(u32 access, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
return test_bit(access, &stp->st_access_bmap);
|
||||
}
|
||||
|
||||
/* set share deny for a given stateid */
|
||||
static inline void
|
||||
set_deny(u32 access, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
__set_bit(access, &stp->st_deny_bmap);
|
||||
}
|
||||
|
||||
/* clear share deny for a given stateid */
|
||||
static inline void
|
||||
clear_deny(u32 access, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
__clear_bit(access, &stp->st_deny_bmap);
|
||||
}
|
||||
|
||||
/* test whether a given stateid is denying specific access */
|
||||
static inline bool
|
||||
test_deny(u32 access, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
return test_bit(access, &stp->st_deny_bmap);
|
||||
}
|
||||
|
||||
static int nfs4_access_to_omode(u32 access)
|
||||
|
@ -493,6 +526,20 @@ static int nfs4_access_to_omode(u32 access)
|
|||
BUG();
|
||||
}
|
||||
|
||||
/* release all access and file references for a given stateid */
|
||||
static void
|
||||
release_all_access(struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < 4; i++) {
|
||||
if (test_access(i, stp))
|
||||
nfs4_file_put_access(stp->st_file,
|
||||
nfs4_access_to_omode(i));
|
||||
clear_access(i, stp);
|
||||
}
|
||||
}
|
||||
|
||||
static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
list_del(&stp->st_perfile);
|
||||
|
@ -501,16 +548,7 @@ static void unhash_generic_stateid(struct nfs4_ol_stateid *stp)
|
|||
|
||||
static void close_generic_stateid(struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (stp->st_access_bmap) {
|
||||
for (i = 1; i < 4; i++) {
|
||||
if (test_bit(i, &stp->st_access_bmap))
|
||||
nfs4_file_put_access(stp->st_file,
|
||||
nfs4_access_to_omode(i));
|
||||
__clear_bit(i, &stp->st_access_bmap);
|
||||
}
|
||||
}
|
||||
release_all_access(stp);
|
||||
put_nfs4_file(stp->st_file);
|
||||
stp->st_file = NULL;
|
||||
}
|
||||
|
@ -885,7 +923,7 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n
|
|||
struct nfsd4_session *new;
|
||||
struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
|
||||
int numslots, slotsize;
|
||||
int status;
|
||||
__be32 status;
|
||||
int idx;
|
||||
|
||||
/*
|
||||
|
@ -984,7 +1022,8 @@ static inline void
|
|||
renew_client_locked(struct nfs4_client *clp)
|
||||
{
|
||||
if (is_client_expired(clp)) {
|
||||
dprintk("%s: client (clientid %08x/%08x) already expired\n",
|
||||
WARN_ON(1);
|
||||
printk("%s: client (clientid %08x/%08x) already expired\n",
|
||||
__func__,
|
||||
clp->cl_clientid.cl_boot,
|
||||
clp->cl_clientid.cl_id);
|
||||
|
@ -1049,9 +1088,7 @@ free_client(struct nfs4_client *clp)
|
|||
list_del(&ses->se_perclnt);
|
||||
nfsd4_put_session_locked(ses);
|
||||
}
|
||||
if (clp->cl_cred.cr_group_info)
|
||||
put_group_info(clp->cl_cred.cr_group_info);
|
||||
kfree(clp->cl_principal);
|
||||
free_svc_cred(&clp->cl_cred);
|
||||
kfree(clp->cl_name.data);
|
||||
kfree(clp);
|
||||
}
|
||||
|
@ -1132,12 +1169,21 @@ static void copy_clid(struct nfs4_client *target, struct nfs4_client *source)
|
|||
target->cl_clientid.cl_id = source->cl_clientid.cl_id;
|
||||
}
|
||||
|
||||
static void copy_cred(struct svc_cred *target, struct svc_cred *source)
|
||||
static int copy_cred(struct svc_cred *target, struct svc_cred *source)
|
||||
{
|
||||
if (source->cr_principal) {
|
||||
target->cr_principal =
|
||||
kstrdup(source->cr_principal, GFP_KERNEL);
|
||||
if (target->cr_principal == NULL)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
target->cr_principal = NULL;
|
||||
target->cr_flavor = source->cr_flavor;
|
||||
target->cr_uid = source->cr_uid;
|
||||
target->cr_gid = source->cr_gid;
|
||||
target->cr_group_info = source->cr_group_info;
|
||||
get_group_info(target->cr_group_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int same_name(const char *n1, const char *n2)
|
||||
|
@ -1157,11 +1203,31 @@ same_clid(clientid_t *cl1, clientid_t *cl2)
|
|||
return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id);
|
||||
}
|
||||
|
||||
/* XXX what about NGROUP */
|
||||
static bool groups_equal(struct group_info *g1, struct group_info *g2)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (g1->ngroups != g2->ngroups)
|
||||
return false;
|
||||
for (i=0; i<g1->ngroups; i++)
|
||||
if (GROUP_AT(g1, i) != GROUP_AT(g2, i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int
|
||||
same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
|
||||
{
|
||||
return cr1->cr_uid == cr2->cr_uid;
|
||||
if ((cr1->cr_flavor != cr2->cr_flavor)
|
||||
|| (cr1->cr_uid != cr2->cr_uid)
|
||||
|| (cr1->cr_gid != cr2->cr_gid)
|
||||
|| !groups_equal(cr1->cr_group_info, cr2->cr_group_info))
|
||||
return false;
|
||||
if (cr1->cr_principal == cr2->cr_principal)
|
||||
return true;
|
||||
if (!cr1->cr_principal || !cr2->cr_principal)
|
||||
return false;
|
||||
return 0 == strcmp(cr1->cr_principal, cr1->cr_principal);
|
||||
}
|
||||
|
||||
static void gen_clid(struct nfs4_client *clp)
|
||||
|
@ -1204,25 +1270,20 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
|
|||
{
|
||||
struct nfs4_client *clp;
|
||||
struct sockaddr *sa = svc_addr(rqstp);
|
||||
char *princ;
|
||||
int ret;
|
||||
|
||||
clp = alloc_client(name);
|
||||
if (clp == NULL)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&clp->cl_sessions);
|
||||
|
||||
princ = svc_gss_principal(rqstp);
|
||||
if (princ) {
|
||||
clp->cl_principal = kstrdup(princ, GFP_KERNEL);
|
||||
if (clp->cl_principal == NULL) {
|
||||
spin_lock(&client_lock);
|
||||
free_client(clp);
|
||||
spin_unlock(&client_lock);
|
||||
return NULL;
|
||||
}
|
||||
ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred);
|
||||
if (ret) {
|
||||
spin_lock(&client_lock);
|
||||
free_client(clp);
|
||||
spin_unlock(&client_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idr_init(&clp->cl_stateids);
|
||||
memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
|
||||
atomic_set(&clp->cl_refcount, 0);
|
||||
|
@ -1240,8 +1301,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
|
|||
rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
|
||||
copy_verf(clp, verf);
|
||||
rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa);
|
||||
clp->cl_flavor = rqstp->rq_flavor;
|
||||
copy_cred(&clp->cl_cred, &rqstp->rq_cred);
|
||||
gen_confirm(clp);
|
||||
clp->cl_cb_session = NULL;
|
||||
return clp;
|
||||
|
@ -1470,18 +1529,32 @@ nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid)
|
|||
clid->flags = new->cl_exchange_flags;
|
||||
}
|
||||
|
||||
static bool client_has_state(struct nfs4_client *clp)
|
||||
{
|
||||
/*
|
||||
* Note clp->cl_openowners check isn't quite right: there's no
|
||||
* need to count owners without stateid's.
|
||||
*
|
||||
* Also note we should probably be using this in 4.0 case too.
|
||||
*/
|
||||
return !list_empty(&clp->cl_openowners)
|
||||
|| !list_empty(&clp->cl_delegations)
|
||||
|| !list_empty(&clp->cl_sessions);
|
||||
}
|
||||
|
||||
__be32
|
||||
nfsd4_exchange_id(struct svc_rqst *rqstp,
|
||||
struct nfsd4_compound_state *cstate,
|
||||
struct nfsd4_exchange_id *exid)
|
||||
{
|
||||
struct nfs4_client *unconf, *conf, *new;
|
||||
int status;
|
||||
__be32 status;
|
||||
unsigned int strhashval;
|
||||
char dname[HEXDIR_LEN];
|
||||
char addr_str[INET6_ADDRSTRLEN];
|
||||
nfs4_verifier verf = exid->verifier;
|
||||
struct sockaddr *sa = svc_addr(rqstp);
|
||||
bool update = exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A;
|
||||
|
||||
rpc_ntop(sa, addr_str, sizeof(addr_str));
|
||||
dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p "
|
||||
|
@ -1507,71 +1580,63 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
|
|||
status = nfs4_make_rec_clidname(dname, &exid->clname);
|
||||
|
||||
if (status)
|
||||
goto error;
|
||||
return status;
|
||||
|
||||
strhashval = clientstr_hashval(dname);
|
||||
|
||||
/* Cases below refer to rfc 5661 section 18.35.4: */
|
||||
nfs4_lock_state();
|
||||
status = nfs_ok;
|
||||
|
||||
conf = find_confirmed_client_by_str(dname, strhashval);
|
||||
if (conf) {
|
||||
if (!clp_used_exchangeid(conf)) {
|
||||
status = nfserr_clid_inuse; /* XXX: ? */
|
||||
goto out;
|
||||
}
|
||||
if (!same_verf(&verf, &conf->cl_verifier)) {
|
||||
/* 18.35.4 case 8 */
|
||||
if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
|
||||
status = nfserr_not_same;
|
||||
bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
|
||||
bool verfs_match = same_verf(&verf, &conf->cl_verifier);
|
||||
|
||||
if (update) {
|
||||
if (!clp_used_exchangeid(conf)) { /* buggy client */
|
||||
status = nfserr_inval;
|
||||
goto out;
|
||||
}
|
||||
/* Client reboot: destroy old state */
|
||||
expire_client(conf);
|
||||
goto out_new;
|
||||
}
|
||||
if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) {
|
||||
/* 18.35.4 case 9 */
|
||||
if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
|
||||
if (!creds_match) { /* case 9 */
|
||||
status = nfserr_perm;
|
||||
goto out;
|
||||
}
|
||||
if (!verfs_match) { /* case 8 */
|
||||
status = nfserr_not_same;
|
||||
goto out;
|
||||
}
|
||||
/* case 6 */
|
||||
exid->flags |= EXCHGID4_FLAG_CONFIRMED_R;
|
||||
new = conf;
|
||||
goto out_copy;
|
||||
}
|
||||
if (!creds_match) { /* case 3 */
|
||||
if (client_has_state(conf)) {
|
||||
status = nfserr_clid_inuse;
|
||||
goto out;
|
||||
}
|
||||
expire_client(conf);
|
||||
goto out_new;
|
||||
}
|
||||
/*
|
||||
* Set bit when the owner id and verifier map to an already
|
||||
* confirmed client id (18.35.3).
|
||||
*/
|
||||
exid->flags |= EXCHGID4_FLAG_CONFIRMED_R;
|
||||
|
||||
/*
|
||||
* Falling into 18.35.4 case 2, possible router replay.
|
||||
* Leave confirmed record intact and return same result.
|
||||
*/
|
||||
copy_verf(conf, &verf);
|
||||
new = conf;
|
||||
goto out_copy;
|
||||
if (verfs_match) { /* case 2 */
|
||||
conf->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R;
|
||||
new = conf;
|
||||
goto out_copy;
|
||||
}
|
||||
/* case 5, client reboot */
|
||||
goto out_new;
|
||||
}
|
||||
|
||||
/* 18.35.4 case 7 */
|
||||
if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) {
|
||||
if (update) { /* case 7 */
|
||||
status = nfserr_noent;
|
||||
goto out;
|
||||
}
|
||||
|
||||
unconf = find_unconfirmed_client_by_str(dname, strhashval);
|
||||
if (unconf) {
|
||||
/*
|
||||
* Possible retry or client restart. Per 18.35.4 case 4,
|
||||
* a new unconfirmed record should be generated regardless
|
||||
* of whether any properties have changed.
|
||||
*/
|
||||
if (unconf) /* case 4, possible retry or client restart */
|
||||
expire_client(unconf);
|
||||
}
|
||||
|
||||
/* case 1 (normal case) */
|
||||
out_new:
|
||||
/* Normal case */
|
||||
new = create_client(exid->clname, dname, rqstp, &verf);
|
||||
if (new == NULL) {
|
||||
status = nfserr_jukebox;
|
||||
|
@ -1584,7 +1649,7 @@ out_copy:
|
|||
exid->clientid.cl_boot = new->cl_clientid.cl_boot;
|
||||
exid->clientid.cl_id = new->cl_clientid.cl_id;
|
||||
|
||||
exid->seqid = 1;
|
||||
exid->seqid = new->cl_cs_slot.sl_seqid + 1;
|
||||
nfsd4_set_ex_flags(new, exid);
|
||||
|
||||
dprintk("nfsd4_exchange_id seqid %d flags %x\n",
|
||||
|
@ -1593,12 +1658,10 @@ out_copy:
|
|||
|
||||
out:
|
||||
nfs4_unlock_state();
|
||||
error:
|
||||
dprintk("nfsd4_exchange_id returns %d\n", ntohl(status));
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
static __be32
|
||||
check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse)
|
||||
{
|
||||
dprintk("%s enter. seqid %d slot_seqid %d\n", __func__, seqid,
|
||||
|
@ -1626,7 +1689,7 @@ check_slot_seqid(u32 seqid, u32 slot_seqid, int slot_inuse)
|
|||
*/
|
||||
static void
|
||||
nfsd4_cache_create_session(struct nfsd4_create_session *cr_ses,
|
||||
struct nfsd4_clid_slot *slot, int nfserr)
|
||||
struct nfsd4_clid_slot *slot, __be32 nfserr)
|
||||
{
|
||||
slot->sl_status = nfserr;
|
||||
memcpy(&slot->sl_cr_ses, cr_ses, sizeof(*cr_ses));
|
||||
|
@ -1657,7 +1720,7 @@ nfsd4_replay_create_session(struct nfsd4_create_session *cr_ses,
|
|||
/* seqid, slotID, slotID, slotID, status */ \
|
||||
5 ) * sizeof(__be32))
|
||||
|
||||
static __be32 check_forechannel_attrs(struct nfsd4_channel_attrs fchannel)
|
||||
static bool check_forechannel_attrs(struct nfsd4_channel_attrs fchannel)
|
||||
{
|
||||
return fchannel.maxreq_sz < NFSD_MIN_REQ_HDR_SEQ_SZ
|
||||
|| fchannel.maxresp_sz < NFSD_MIN_RESP_HDR_SEQ_SZ;
|
||||
|
@ -1673,7 +1736,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
struct nfsd4_session *new;
|
||||
struct nfsd4_clid_slot *cs_slot = NULL;
|
||||
bool confirm_me = false;
|
||||
int status = 0;
|
||||
__be32 status = 0;
|
||||
|
||||
if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
|
||||
return nfserr_inval;
|
||||
|
@ -1686,16 +1749,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
cs_slot = &conf->cl_cs_slot;
|
||||
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
|
||||
if (status == nfserr_replay_cache) {
|
||||
dprintk("Got a create_session replay! seqid= %d\n",
|
||||
cs_slot->sl_seqid);
|
||||
/* Return the cached reply status */
|
||||
status = nfsd4_replay_create_session(cr_ses, cs_slot);
|
||||
goto out;
|
||||
} else if (cr_ses->seqid != cs_slot->sl_seqid + 1) {
|
||||
status = nfserr_seq_misordered;
|
||||
dprintk("Sequence misordered!\n");
|
||||
dprintk("Expected seqid= %d but got seqid= %d\n",
|
||||
cs_slot->sl_seqid, cr_ses->seqid);
|
||||
goto out;
|
||||
}
|
||||
} else if (unconf) {
|
||||
|
@ -1704,7 +1761,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
status = nfserr_clid_inuse;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cs_slot = &unconf->cl_cs_slot;
|
||||
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
|
||||
if (status) {
|
||||
|
@ -1712,7 +1768,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
status = nfserr_seq_misordered;
|
||||
goto out;
|
||||
}
|
||||
|
||||
confirm_me = true;
|
||||
conf = unconf;
|
||||
} else {
|
||||
|
@ -1749,8 +1804,14 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
|||
|
||||
/* cache solo and embedded create sessions under the state lock */
|
||||
nfsd4_cache_create_session(cr_ses, cs_slot, status);
|
||||
if (confirm_me)
|
||||
if (confirm_me) {
|
||||
unsigned int hash = clientstr_hashval(unconf->cl_recdir);
|
||||
struct nfs4_client *old =
|
||||
find_confirmed_client_by_str(conf->cl_recdir, hash);
|
||||
if (old)
|
||||
expire_client(old);
|
||||
move_to_confirmed(conf);
|
||||
}
|
||||
out:
|
||||
nfs4_unlock_state();
|
||||
dprintk("%s returns %d\n", __func__, ntohl(status));
|
||||
|
@ -1818,7 +1879,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
|
|||
struct nfsd4_destroy_session *sessionid)
|
||||
{
|
||||
struct nfsd4_session *ses;
|
||||
u32 status = nfserr_badsession;
|
||||
__be32 status = nfserr_badsession;
|
||||
|
||||
/* Notes:
|
||||
* - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
|
||||
|
@ -1914,7 +1975,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
|
|||
struct nfsd4_session *session;
|
||||
struct nfsd4_slot *slot;
|
||||
struct nfsd4_conn *conn;
|
||||
int status;
|
||||
__be32 status;
|
||||
|
||||
if (resp->opcnt != 1)
|
||||
return nfserr_sequence_pos;
|
||||
|
@ -2008,18 +2069,11 @@ out:
|
|||
return status;
|
||||
}
|
||||
|
||||
static inline bool has_resources(struct nfs4_client *clp)
|
||||
{
|
||||
return !list_empty(&clp->cl_openowners)
|
||||
|| !list_empty(&clp->cl_delegations)
|
||||
|| !list_empty(&clp->cl_sessions);
|
||||
}
|
||||
|
||||
__be32
|
||||
nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_destroy_clientid *dc)
|
||||
{
|
||||
struct nfs4_client *conf, *unconf, *clp;
|
||||
int status = 0;
|
||||
__be32 status = 0;
|
||||
|
||||
nfs4_lock_state();
|
||||
unconf = find_unconfirmed_client(&dc->clientid);
|
||||
|
@ -2028,7 +2082,7 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
|
|||
if (conf) {
|
||||
clp = conf;
|
||||
|
||||
if (!is_client_expired(conf) && has_resources(conf)) {
|
||||
if (!is_client_expired(conf) && client_has_state(conf)) {
|
||||
status = nfserr_clientid_busy;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2055,7 +2109,7 @@ out:
|
|||
__be32
|
||||
nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_reclaim_complete *rc)
|
||||
{
|
||||
int status = 0;
|
||||
__be32 status = 0;
|
||||
|
||||
if (rc->rca_one_fs) {
|
||||
if (!cstate->current_fh.fh_dentry)
|
||||
|
@ -2106,17 +2160,13 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
if (status)
|
||||
return status;
|
||||
|
||||
/*
|
||||
* XXX The Duplicate Request Cache (DRC) has been checked (??)
|
||||
* We get here on a DRC miss.
|
||||
*/
|
||||
|
||||
strhashval = clientstr_hashval(dname);
|
||||
|
||||
/* Cases below refer to rfc 3530 section 14.2.33: */
|
||||
nfs4_lock_state();
|
||||
conf = find_confirmed_client_by_str(dname, strhashval);
|
||||
if (conf) {
|
||||
/* RFC 3530 14.2.33 CASE 0: */
|
||||
/* case 0: */
|
||||
status = nfserr_clid_inuse;
|
||||
if (clp_used_exchangeid(conf))
|
||||
goto out;
|
||||
|
@ -2129,63 +2179,18 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
|||
goto out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* section 14.2.33 of RFC 3530 (under the heading "IMPLEMENTATION")
|
||||
* has a description of SETCLIENTID request processing consisting
|
||||
* of 5 bullet points, labeled as CASE0 - CASE4 below.
|
||||
*/
|
||||
unconf = find_unconfirmed_client_by_str(dname, strhashval);
|
||||
status = nfserr_jukebox;
|
||||
if (!conf) {
|
||||
/*
|
||||
* RFC 3530 14.2.33 CASE 4:
|
||||
* placed first, because it is the normal case
|
||||
*/
|
||||
if (unconf)
|
||||
expire_client(unconf);
|
||||
new = create_client(clname, dname, rqstp, &clverifier);
|
||||
if (new == NULL)
|
||||
goto out;
|
||||
gen_clid(new);
|
||||
} else if (same_verf(&conf->cl_verifier, &clverifier)) {
|
||||
/*
|
||||
* RFC 3530 14.2.33 CASE 1:
|
||||
* probable callback update
|
||||
*/
|
||||
if (unconf) {
|
||||
/* Note this is removing unconfirmed {*x***},
|
||||
* which is stronger than RFC recommended {vxc**}.
|
||||
* This has the advantage that there is at most
|
||||
* one {*x***} in either list at any time.
|
||||
*/
|
||||
expire_client(unconf);
|
||||
}
|
||||
new = create_client(clname, dname, rqstp, &clverifier);
|
||||
if (new == NULL)
|
||||
goto out;
|
||||
copy_clid(new, conf);
|
||||
} else if (!unconf) {
|
||||
/*
|
||||
* RFC 3530 14.2.33 CASE 2:
|
||||
* probable client reboot; state will be removed if
|
||||
* confirmed.
|
||||
*/
|
||||
new = create_client(clname, dname, rqstp, &clverifier);
|
||||
if (new == NULL)
|
||||
goto out;
|
||||
gen_clid(new);
|
||||
} else {
|
||||
/*
|
||||
* RFC 3530 14.2.33 CASE 3:
|
||||
* probable client reboot; state will be removed if
|
||||
* confirmed.
|
||||
*/
|
||||
if (unconf)
|
||||
expire_client(unconf);
|
||||
new = create_client(clname, dname, rqstp, &clverifier);
|
||||
if (new == NULL)
|
||||
goto out;
|
||||
status = nfserr_jukebox;
|
||||
new = create_client(clname, dname, rqstp, &clverifier);
|
||||
if (new == NULL)
|
||||
goto out;
|
||||
if (conf && same_verf(&conf->cl_verifier, &clverifier))
|
||||
/* case 1: probable callback update */
|
||||
copy_clid(new, conf);
|
||||
else /* case 4 (new client) or cases 2, 3 (client reboot): */
|
||||
gen_clid(new);
|
||||
}
|
||||
/*
|
||||
* XXX: we should probably set this at creation time, and check
|
||||
* for consistent minorversion use throughout:
|
||||
|
@ -2203,17 +2208,11 @@ out:
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Section 14.2.34 of RFC 3530 (under the heading "IMPLEMENTATION") has
|
||||
* a description of SETCLIENTID_CONFIRM request processing consisting of 4
|
||||
* bullets, labeled as CASE1 - CASE4 below.
|
||||
*/
|
||||
__be32
|
||||
nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
||||
struct nfsd4_compound_state *cstate,
|
||||
struct nfsd4_setclientid_confirm *setclientid_confirm)
|
||||
{
|
||||
struct sockaddr *sa = svc_addr(rqstp);
|
||||
struct nfs4_client *conf, *unconf;
|
||||
nfs4_verifier confirm = setclientid_confirm->sc_confirm;
|
||||
clientid_t * clid = &setclientid_confirm->sc_clientid;
|
||||
|
@ -2221,84 +2220,44 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
|
|||
|
||||
if (STALE_CLIENTID(clid))
|
||||
return nfserr_stale_clientid;
|
||||
/*
|
||||
* XXX The Duplicate Request Cache (DRC) has been checked (??)
|
||||
* We get here on a DRC miss.
|
||||
*/
|
||||
|
||||
nfs4_lock_state();
|
||||
|
||||
conf = find_confirmed_client(clid);
|
||||
unconf = find_unconfirmed_client(clid);
|
||||
|
||||
status = nfserr_clid_inuse;
|
||||
if (conf && !rpc_cmp_addr((struct sockaddr *) &conf->cl_addr, sa))
|
||||
goto out;
|
||||
if (unconf && !rpc_cmp_addr((struct sockaddr *) &unconf->cl_addr, sa))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* section 14.2.34 of RFC 3530 has a description of
|
||||
* SETCLIENTID_CONFIRM request processing consisting
|
||||
* of 4 bullet points, labeled as CASE1 - CASE4 below.
|
||||
* We try hard to give out unique clientid's, so if we get an
|
||||
* attempt to confirm the same clientid with a different cred,
|
||||
* there's a bug somewhere. Let's charitably assume it's our
|
||||
* bug.
|
||||
*/
|
||||
if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) {
|
||||
/*
|
||||
* RFC 3530 14.2.34 CASE 1:
|
||||
* callback update
|
||||
*/
|
||||
if (!same_creds(&conf->cl_cred, &unconf->cl_cred))
|
||||
status = nfserr_clid_inuse;
|
||||
else {
|
||||
nfsd4_change_callback(conf, &unconf->cl_cb_conn);
|
||||
nfsd4_probe_callback(conf);
|
||||
expire_client(unconf);
|
||||
status = nfserr_serverfault;
|
||||
if (unconf && !same_creds(&unconf->cl_cred, &rqstp->rq_cred))
|
||||
goto out;
|
||||
if (conf && !same_creds(&conf->cl_cred, &rqstp->rq_cred))
|
||||
goto out;
|
||||
/* cases below refer to rfc 3530 section 14.2.34: */
|
||||
if (!unconf || !same_verf(&confirm, &unconf->cl_confirm)) {
|
||||
if (conf && !unconf) /* case 2: probable retransmit */
|
||||
status = nfs_ok;
|
||||
else /* case 4: client hasn't noticed we rebooted yet? */
|
||||
status = nfserr_stale_clientid;
|
||||
goto out;
|
||||
}
|
||||
status = nfs_ok;
|
||||
if (conf) { /* case 1: callback update */
|
||||
nfsd4_change_callback(conf, &unconf->cl_cb_conn);
|
||||
nfsd4_probe_callback(conf);
|
||||
expire_client(unconf);
|
||||
} else { /* case 3: normal case; new or rebooted client */
|
||||
unsigned int hash = clientstr_hashval(unconf->cl_recdir);
|
||||
|
||||
conf = find_confirmed_client_by_str(unconf->cl_recdir, hash);
|
||||
if (conf) {
|
||||
nfsd4_client_record_remove(conf);
|
||||
expire_client(conf);
|
||||
}
|
||||
} else if (conf && !unconf) {
|
||||
/*
|
||||
* RFC 3530 14.2.34 CASE 2:
|
||||
* probable retransmitted request; play it safe and
|
||||
* do nothing.
|
||||
*/
|
||||
if (!same_creds(&conf->cl_cred, &rqstp->rq_cred))
|
||||
status = nfserr_clid_inuse;
|
||||
else
|
||||
status = nfs_ok;
|
||||
} else if (!conf && unconf
|
||||
&& same_verf(&unconf->cl_confirm, &confirm)) {
|
||||
/*
|
||||
* RFC 3530 14.2.34 CASE 3:
|
||||
* Normal case; new or rebooted client:
|
||||
*/
|
||||
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred)) {
|
||||
status = nfserr_clid_inuse;
|
||||
} else {
|
||||
unsigned int hash =
|
||||
clientstr_hashval(unconf->cl_recdir);
|
||||
conf = find_confirmed_client_by_str(unconf->cl_recdir,
|
||||
hash);
|
||||
if (conf) {
|
||||
nfsd4_client_record_remove(conf);
|
||||
expire_client(conf);
|
||||
}
|
||||
move_to_confirmed(unconf);
|
||||
conf = unconf;
|
||||
nfsd4_probe_callback(conf);
|
||||
status = nfs_ok;
|
||||
}
|
||||
} else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm)))
|
||||
&& (!unconf || (unconf && !same_verf(&unconf->cl_confirm,
|
||||
&confirm)))) {
|
||||
/*
|
||||
* RFC 3530 14.2.34 CASE 4:
|
||||
* Client probably hasn't noticed that we rebooted yet.
|
||||
*/
|
||||
status = nfserr_stale_clientid;
|
||||
} else {
|
||||
/* check that we have hit one of the cases...*/
|
||||
status = nfserr_clid_inuse;
|
||||
move_to_confirmed(unconf);
|
||||
nfsd4_probe_callback(unconf);
|
||||
}
|
||||
out:
|
||||
nfs4_unlock_state();
|
||||
|
@ -2454,8 +2413,8 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
|
|||
stp->st_file = fp;
|
||||
stp->st_access_bmap = 0;
|
||||
stp->st_deny_bmap = 0;
|
||||
__set_bit(open->op_share_access, &stp->st_access_bmap);
|
||||
__set_bit(open->op_share_deny, &stp->st_deny_bmap);
|
||||
set_access(open->op_share_access, stp);
|
||||
set_deny(open->op_share_deny, stp);
|
||||
stp->st_openstp = NULL;
|
||||
}
|
||||
|
||||
|
@ -2534,8 +2493,8 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
|
|||
ret = nfserr_locked;
|
||||
/* Search for conflicting share reservations */
|
||||
list_for_each_entry(stp, &fp->fi_stateids, st_perfile) {
|
||||
if (test_bit(deny_type, &stp->st_deny_bmap) ||
|
||||
test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap))
|
||||
if (test_deny(deny_type, stp) ||
|
||||
test_deny(NFS4_SHARE_DENY_BOTH, stp))
|
||||
goto out;
|
||||
}
|
||||
ret = nfs_ok;
|
||||
|
@ -2791,7 +2750,7 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c
|
|||
bool new_access;
|
||||
__be32 status;
|
||||
|
||||
new_access = !test_bit(op_share_access, &stp->st_access_bmap);
|
||||
new_access = !test_access(op_share_access, stp);
|
||||
if (new_access) {
|
||||
status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open);
|
||||
if (status)
|
||||
|
@ -2806,8 +2765,8 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c
|
|||
return status;
|
||||
}
|
||||
/* remember the open */
|
||||
__set_bit(op_share_access, &stp->st_access_bmap);
|
||||
__set_bit(open->op_share_deny, &stp->st_deny_bmap);
|
||||
set_access(op_share_access, stp);
|
||||
set_deny(open->op_share_deny, stp);
|
||||
|
||||
return nfs_ok;
|
||||
}
|
||||
|
@ -3282,18 +3241,18 @@ STALE_STATEID(stateid_t *stateid)
|
|||
}
|
||||
|
||||
static inline int
|
||||
access_permit_read(unsigned long access_bmap)
|
||||
access_permit_read(struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
return test_bit(NFS4_SHARE_ACCESS_READ, &access_bmap) ||
|
||||
test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap) ||
|
||||
test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap);
|
||||
return test_access(NFS4_SHARE_ACCESS_READ, stp) ||
|
||||
test_access(NFS4_SHARE_ACCESS_BOTH, stp) ||
|
||||
test_access(NFS4_SHARE_ACCESS_WRITE, stp);
|
||||
}
|
||||
|
||||
static inline int
|
||||
access_permit_write(unsigned long access_bmap)
|
||||
access_permit_write(struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
return test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap) ||
|
||||
test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap);
|
||||
return test_access(NFS4_SHARE_ACCESS_WRITE, stp) ||
|
||||
test_access(NFS4_SHARE_ACCESS_BOTH, stp);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -3304,9 +3263,9 @@ __be32 nfs4_check_openmode(struct nfs4_ol_stateid *stp, int flags)
|
|||
/* For lock stateid's, we test the parent open, not the lock: */
|
||||
if (stp->st_openstp)
|
||||
stp = stp->st_openstp;
|
||||
if ((flags & WR_STATE) && (!access_permit_write(stp->st_access_bmap)))
|
||||
if ((flags & WR_STATE) && !access_permit_write(stp))
|
||||
goto out;
|
||||
if ((flags & RD_STATE) && (!access_permit_read(stp->st_access_bmap)))
|
||||
if ((flags & RD_STATE) && !access_permit_read(stp))
|
||||
goto out;
|
||||
status = nfs_ok;
|
||||
out:
|
||||
|
@ -3346,7 +3305,7 @@ static bool stateid_generation_after(stateid_t *a, stateid_t *b)
|
|||
return (s32)a->si_generation - (s32)b->si_generation > 0;
|
||||
}
|
||||
|
||||
static int check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
|
||||
static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
|
||||
{
|
||||
/*
|
||||
* When sessions are used the stateid generation number is ignored
|
||||
|
@ -3655,10 +3614,10 @@ out:
|
|||
|
||||
static inline void nfs4_stateid_downgrade_bit(struct nfs4_ol_stateid *stp, u32 access)
|
||||
{
|
||||
if (!test_bit(access, &stp->st_access_bmap))
|
||||
if (!test_access(access, stp))
|
||||
return;
|
||||
nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(access));
|
||||
__clear_bit(access, &stp->st_access_bmap);
|
||||
clear_access(access, stp);
|
||||
}
|
||||
|
||||
static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_access)
|
||||
|
@ -3680,12 +3639,12 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac
|
|||
}
|
||||
|
||||
static void
|
||||
reset_union_bmap_deny(unsigned long deny, unsigned long *bmap)
|
||||
reset_union_bmap_deny(unsigned long deny, struct nfs4_ol_stateid *stp)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((i & deny) != i)
|
||||
__clear_bit(i, bmap);
|
||||
clear_deny(i, stp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3712,19 +3671,19 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
|
|||
if (status)
|
||||
goto out;
|
||||
status = nfserr_inval;
|
||||
if (!test_bit(od->od_share_access, &stp->st_access_bmap)) {
|
||||
dprintk("NFSD:access not a subset current bitmap: 0x%lx, input access=%08x\n",
|
||||
if (!test_access(od->od_share_access, stp)) {
|
||||
dprintk("NFSD: access not a subset current bitmap: 0x%lx, input access=%08x\n",
|
||||
stp->st_access_bmap, od->od_share_access);
|
||||
goto out;
|
||||
}
|
||||
if (!test_bit(od->od_share_deny, &stp->st_deny_bmap)) {
|
||||
if (!test_deny(od->od_share_deny, stp)) {
|
||||
dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n",
|
||||
stp->st_deny_bmap, od->od_share_deny);
|
||||
goto out;
|
||||
}
|
||||
nfs4_stateid_downgrade(stp, od->od_share_access);
|
||||
|
||||
reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap);
|
||||
reset_union_bmap_deny(od->od_share_deny, stp);
|
||||
|
||||
update_stateid(&stp->st_stid.sc_stateid);
|
||||
memcpy(&od->od_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
|
||||
|
@ -4014,13 +3973,13 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access)
|
|||
struct nfs4_file *fp = lock_stp->st_file;
|
||||
int oflag = nfs4_access_to_omode(access);
|
||||
|
||||
if (test_bit(access, &lock_stp->st_access_bmap))
|
||||
if (test_access(access, lock_stp))
|
||||
return;
|
||||
nfs4_file_get_access(fp, oflag);
|
||||
__set_bit(access, &lock_stp->st_access_bmap);
|
||||
set_access(access, lock_stp);
|
||||
}
|
||||
|
||||
__be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new)
|
||||
static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new)
|
||||
{
|
||||
struct nfs4_file *fi = ost->st_file;
|
||||
struct nfs4_openowner *oo = openowner(ost->st_stateowner);
|
||||
|
|
|
@ -1674,12 +1674,12 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
|
|||
|
||||
static void write32(__be32 **p, u32 n)
|
||||
{
|
||||
*(*p)++ = n;
|
||||
*(*p)++ = htonl(n);
|
||||
}
|
||||
|
||||
static void write64(__be32 **p, u64 n)
|
||||
{
|
||||
write32(p, (u32)(n >> 32));
|
||||
write32(p, (n >> 32));
|
||||
write32(p, (u32)n);
|
||||
}
|
||||
|
||||
|
@ -1744,15 +1744,16 @@ static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, _
|
|||
}
|
||||
|
||||
/* Encode as an array of strings the string given with components
|
||||
* separated @sep.
|
||||
* separated @sep, escaped with esc_enter and esc_exit.
|
||||
*/
|
||||
static __be32 nfsd4_encode_components(char sep, char *components,
|
||||
__be32 **pp, int *buflen)
|
||||
static __be32 nfsd4_encode_components_esc(char sep, char *components,
|
||||
__be32 **pp, int *buflen,
|
||||
char esc_enter, char esc_exit)
|
||||
{
|
||||
__be32 *p = *pp;
|
||||
__be32 *countp = p;
|
||||
int strlen, count=0;
|
||||
char *str, *end;
|
||||
char *str, *end, *next;
|
||||
|
||||
dprintk("nfsd4_encode_components(%s)\n", components);
|
||||
if ((*buflen -= 4) < 0)
|
||||
|
@ -1760,8 +1761,23 @@ static __be32 nfsd4_encode_components(char sep, char *components,
|
|||
WRITE32(0); /* We will fill this in with @count later */
|
||||
end = str = components;
|
||||
while (*end) {
|
||||
for (; *end && (*end != sep); end++)
|
||||
; /* Point to end of component */
|
||||
bool found_esc = false;
|
||||
|
||||
/* try to parse as esc_start, ..., esc_end, sep */
|
||||
if (*str == esc_enter) {
|
||||
for (; *end && (*end != esc_exit); end++)
|
||||
/* find esc_exit or end of string */;
|
||||
next = end + 1;
|
||||
if (*end && (!*next || *next == sep)) {
|
||||
str++;
|
||||
found_esc = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_esc)
|
||||
for (; *end && (*end != sep); end++)
|
||||
/* find sep or end of string */;
|
||||
|
||||
strlen = end - str;
|
||||
if (strlen) {
|
||||
if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
|
||||
|
@ -1780,6 +1796,15 @@ static __be32 nfsd4_encode_components(char sep, char *components,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Encode as an array of strings the string given with components
|
||||
* separated @sep.
|
||||
*/
|
||||
static __be32 nfsd4_encode_components(char sep, char *components,
|
||||
__be32 **pp, int *buflen)
|
||||
{
|
||||
return nfsd4_encode_components_esc(sep, components, pp, buflen, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* encode a location element of a fs_locations structure
|
||||
*/
|
||||
|
@ -1789,7 +1814,8 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
|
|||
__be32 status;
|
||||
__be32 *p = *pp;
|
||||
|
||||
status = nfsd4_encode_components(':', location->hosts, &p, buflen);
|
||||
status = nfsd4_encode_components_esc(':', location->hosts, &p, buflen,
|
||||
'[', ']');
|
||||
if (status)
|
||||
return status;
|
||||
status = nfsd4_encode_components('/', location->path, &p, buflen);
|
||||
|
@ -3251,7 +3277,7 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
|
|||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
|
||||
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_exchange_id *exid)
|
||||
{
|
||||
__be32 *p;
|
||||
|
@ -3306,7 +3332,7 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
|
|||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
|
||||
nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_create_session *sess)
|
||||
{
|
||||
__be32 *p;
|
||||
|
@ -3355,14 +3381,14 @@ nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
|
|||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, int nfserr,
|
||||
nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_destroy_session *destroy_session)
|
||||
{
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
|
||||
nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_free_stateid *free_stateid)
|
||||
{
|
||||
__be32 *p;
|
||||
|
@ -3371,13 +3397,13 @@ nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr,
|
|||
return nfserr;
|
||||
|
||||
RESERVE_SPACE(4);
|
||||
WRITE32(nfserr);
|
||||
*p++ = nfserr;
|
||||
ADJUST_ARGS();
|
||||
return nfserr;
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
|
||||
nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_sequence *seq)
|
||||
{
|
||||
__be32 *p;
|
||||
|
@ -3399,8 +3425,8 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
__be32
|
||||
nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr,
|
||||
static __be32
|
||||
nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
struct nfsd4_test_stateid *test_stateid)
|
||||
{
|
||||
struct nfsd4_test_stateid_id *stateid, *next;
|
||||
|
@ -3503,7 +3529,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
|
|||
* Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
|
||||
* will be at least a page and will therefore hold the xdr_buf head.
|
||||
*/
|
||||
int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
|
||||
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
|
||||
{
|
||||
struct xdr_buf *xb = &resp->rqstp->rq_res;
|
||||
struct nfsd4_session *session = NULL;
|
||||
|
|
|
@ -661,6 +661,7 @@ static ssize_t __write_ports_addfd(char *buf)
|
|||
{
|
||||
char *mesg = buf;
|
||||
int fd, err;
|
||||
struct net *net = &init_net;
|
||||
|
||||
err = get_int(&mesg, &fd);
|
||||
if (err != 0 || fd < 0)
|
||||
|
@ -672,6 +673,8 @@ static ssize_t __write_ports_addfd(char *buf)
|
|||
|
||||
err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
|
||||
if (err < 0) {
|
||||
if (nfsd_serv->sv_nrthreads == 1)
|
||||
svc_shutdown_net(nfsd_serv, net);
|
||||
svc_destroy(nfsd_serv);
|
||||
return err;
|
||||
}
|
||||
|
@ -709,6 +712,7 @@ static ssize_t __write_ports_addxprt(char *buf)
|
|||
char transport[16];
|
||||
struct svc_xprt *xprt;
|
||||
int port, err;
|
||||
struct net *net = &init_net;
|
||||
|
||||
if (sscanf(buf, "%15s %4u", transport, &port) != 2)
|
||||
return -EINVAL;
|
||||
|
@ -720,12 +724,12 @@ static ssize_t __write_ports_addxprt(char *buf)
|
|||
if (err != 0)
|
||||
return err;
|
||||
|
||||
err = svc_create_xprt(nfsd_serv, transport, &init_net,
|
||||
err = svc_create_xprt(nfsd_serv, transport, net,
|
||||
PF_INET, port, SVC_SOCK_ANONYMOUS);
|
||||
if (err < 0)
|
||||
goto out_err;
|
||||
|
||||
err = svc_create_xprt(nfsd_serv, transport, &init_net,
|
||||
err = svc_create_xprt(nfsd_serv, transport, net,
|
||||
PF_INET6, port, SVC_SOCK_ANONYMOUS);
|
||||
if (err < 0 && err != -EAFNOSUPPORT)
|
||||
goto out_close;
|
||||
|
@ -734,12 +738,14 @@ static ssize_t __write_ports_addxprt(char *buf)
|
|||
nfsd_serv->sv_nrthreads--;
|
||||
return 0;
|
||||
out_close:
|
||||
xprt = svc_find_xprt(nfsd_serv, transport, &init_net, PF_INET, port);
|
||||
xprt = svc_find_xprt(nfsd_serv, transport, net, PF_INET, port);
|
||||
if (xprt != NULL) {
|
||||
svc_close_xprt(xprt);
|
||||
svc_xprt_put(xprt);
|
||||
}
|
||||
out_err:
|
||||
if (nfsd_serv->sv_nrthreads == 1)
|
||||
svc_shutdown_net(nfsd_serv, net);
|
||||
svc_destroy(nfsd_serv);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/nsproxy.h>
|
||||
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
|
@ -330,6 +331,8 @@ static int nfsd_get_default_max_blksize(void)
|
|||
|
||||
int nfsd_create_serv(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&nfsd_mutex));
|
||||
if (nfsd_serv) {
|
||||
svc_get(nfsd_serv);
|
||||
|
@ -343,6 +346,12 @@ int nfsd_create_serv(void)
|
|||
if (nfsd_serv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
error = svc_bind(nfsd_serv, current->nsproxy->net_ns);
|
||||
if (error < 0) {
|
||||
svc_destroy(nfsd_serv);
|
||||
return error;
|
||||
}
|
||||
|
||||
set_max_drc();
|
||||
do_gettimeofday(&nfssvc_boot); /* record boot time */
|
||||
return 0;
|
||||
|
@ -373,6 +382,7 @@ int nfsd_set_nrthreads(int n, int *nthreads)
|
|||
int i = 0;
|
||||
int tot = 0;
|
||||
int err = 0;
|
||||
struct net *net = &init_net;
|
||||
|
||||
WARN_ON(!mutex_is_locked(&nfsd_mutex));
|
||||
|
||||
|
@ -417,6 +427,9 @@ int nfsd_set_nrthreads(int n, int *nthreads)
|
|||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (nfsd_serv->sv_nrthreads == 1)
|
||||
svc_shutdown_net(nfsd_serv, net);
|
||||
svc_destroy(nfsd_serv);
|
||||
|
||||
return err;
|
||||
|
@ -432,6 +445,7 @@ nfsd_svc(unsigned short port, int nrservs)
|
|||
{
|
||||
int error;
|
||||
bool nfsd_up_before;
|
||||
struct net *net = &init_net;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
dprintk("nfsd: creating service\n");
|
||||
|
@ -464,6 +478,8 @@ out_shutdown:
|
|||
if (error < 0 && !nfsd_up_before)
|
||||
nfsd_shutdown();
|
||||
out_destroy:
|
||||
if (nfsd_serv->sv_nrthreads == 1)
|
||||
svc_shutdown_net(nfsd_serv, net);
|
||||
svc_destroy(nfsd_serv); /* Release server */
|
||||
out:
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
|
@ -547,6 +563,9 @@ nfsd(void *vrqstp)
|
|||
nfsdstats.th_cnt --;
|
||||
|
||||
out:
|
||||
if (rqstp->rq_server->sv_nrthreads == 1)
|
||||
svc_shutdown_net(rqstp->rq_server, &init_net);
|
||||
|
||||
/* Release the thread */
|
||||
svc_exit_thread(rqstp);
|
||||
|
||||
|
@ -659,8 +678,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
|||
int nfsd_pool_stats_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = seq_release(inode, file);
|
||||
struct net *net = &init_net;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
/* this function really, really should have been called svc_put() */
|
||||
if (nfsd_serv->sv_nrthreads == 1)
|
||||
svc_shutdown_net(nfsd_serv, net);
|
||||
svc_destroy(nfsd_serv);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return ret;
|
||||
|
|
|
@ -232,7 +232,6 @@ struct nfs4_client {
|
|||
time_t cl_time; /* time of last lease renewal */
|
||||
struct sockaddr_storage cl_addr; /* client ipaddress */
|
||||
u32 cl_flavor; /* setclientid pseudoflavor */
|
||||
char *cl_principal; /* setclientid principal name */
|
||||
struct svc_cred cl_cred; /* setclientid principal */
|
||||
clientid_t cl_clientid; /* generated by server */
|
||||
nfs4_verifier cl_confirm; /* generated by server */
|
||||
|
|
|
@ -60,7 +60,7 @@ struct nfsd4_compound_state {
|
|||
__be32 *datap;
|
||||
size_t iovlen;
|
||||
u32 minorversion;
|
||||
u32 status;
|
||||
__be32 status;
|
||||
stateid_t current_stateid;
|
||||
stateid_t save_stateid;
|
||||
/* to indicate current and saved state id presents */
|
||||
|
@ -364,7 +364,7 @@ struct nfsd4_test_stateid_id {
|
|||
};
|
||||
|
||||
struct nfsd4_test_stateid {
|
||||
__be32 ts_num_ids;
|
||||
u32 ts_num_ids;
|
||||
struct list_head ts_stateid_list;
|
||||
};
|
||||
|
||||
|
@ -549,7 +549,7 @@ int nfs4svc_decode_compoundargs(struct svc_rqst *, __be32 *,
|
|||
struct nfsd4_compoundargs *);
|
||||
int nfs4svc_encode_compoundres(struct svc_rqst *, __be32 *,
|
||||
struct nfsd4_compoundres *);
|
||||
int nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
|
||||
__be32 nfsd4_check_resp_size(struct nfsd4_compoundres *, u32);
|
||||
void nfsd4_encode_operation(struct nfsd4_compoundres *, struct nfsd4_op *);
|
||||
void nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op);
|
||||
__be32 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
||||
|
|
|
@ -232,7 +232,6 @@ struct svc_rqst {
|
|||
struct svc_pool * rq_pool; /* thread pool */
|
||||
struct svc_procedure * rq_procinfo; /* procedure info */
|
||||
struct auth_ops * rq_authop; /* authentication flavour */
|
||||
u32 rq_flavor; /* pseudoflavor */
|
||||
struct svc_cred rq_cred; /* auth info */
|
||||
void * rq_xprt_ctxt; /* transport specific context ptr */
|
||||
struct svc_deferred_req*rq_deferred; /* deferred request we are replaying */
|
||||
|
@ -416,6 +415,7 @@ struct svc_procedure {
|
|||
*/
|
||||
int svc_rpcb_setup(struct svc_serv *serv, struct net *net);
|
||||
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);
|
||||
int svc_bind(struct svc_serv *serv, struct net *net);
|
||||
struct svc_serv *svc_create(struct svc_program *, unsigned int,
|
||||
void (*shutdown)(struct svc_serv *, struct net *net));
|
||||
struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
|
||||
|
|
|
@ -15,13 +15,23 @@
|
|||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
struct svc_cred {
|
||||
uid_t cr_uid;
|
||||
gid_t cr_gid;
|
||||
struct group_info *cr_group_info;
|
||||
u32 cr_flavor; /* pseudoflavor */
|
||||
char *cr_principal; /* for gss */
|
||||
};
|
||||
|
||||
static inline void free_svc_cred(struct svc_cred *cred)
|
||||
{
|
||||
if (cred->cr_group_info)
|
||||
put_group_info(cred->cr_group_info);
|
||||
kfree(cred->cr_principal);
|
||||
}
|
||||
|
||||
struct svc_rqst; /* forward decl */
|
||||
struct in6_addr;
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ int gss_svc_init_net(struct net *net);
|
|||
void gss_svc_shutdown_net(struct net *net);
|
||||
int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
|
||||
u32 svcauth_gss_flavor(struct auth_domain *dom);
|
||||
char *svc_gss_principal(struct svc_rqst *);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
|
||||
|
|
|
@ -381,21 +381,53 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
|
|||
}
|
||||
|
||||
/*
|
||||
* We cannot currently handle tokens with rotated data. We need a
|
||||
* generalized routine to rotate the data in place. It is anticipated
|
||||
* that we won't encounter rotated data in the general case.
|
||||
* We can shift data by up to LOCAL_BUF_LEN bytes in a pass. If we need
|
||||
* to do more than that, we shift repeatedly. Kevin Coffman reports
|
||||
* seeing 28 bytes as the value used by Microsoft clients and servers
|
||||
* with AES, so this constant is chosen to allow handling 28 in one pass
|
||||
* without using too much stack space.
|
||||
*
|
||||
* If that proves to a problem perhaps we could use a more clever
|
||||
* algorithm.
|
||||
*/
|
||||
static u32
|
||||
rotate_left(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf, u16 rrc)
|
||||
#define LOCAL_BUF_LEN 32u
|
||||
|
||||
static void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
|
||||
{
|
||||
unsigned int realrrc = rrc % (buf->len - offset - GSS_KRB5_TOK_HDR_LEN);
|
||||
char head[LOCAL_BUF_LEN];
|
||||
char tmp[LOCAL_BUF_LEN];
|
||||
unsigned int this_len, i;
|
||||
|
||||
if (realrrc == 0)
|
||||
return 0;
|
||||
BUG_ON(shift > LOCAL_BUF_LEN);
|
||||
|
||||
dprintk("%s: cannot process token with rotated data: "
|
||||
"rrc %u, realrrc %u\n", __func__, rrc, realrrc);
|
||||
return 1;
|
||||
read_bytes_from_xdr_buf(buf, 0, head, shift);
|
||||
for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
|
||||
this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
|
||||
read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
|
||||
write_bytes_to_xdr_buf(buf, i, tmp, this_len);
|
||||
}
|
||||
write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
|
||||
}
|
||||
|
||||
static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
|
||||
{
|
||||
int shifted = 0;
|
||||
int this_shift;
|
||||
|
||||
shift %= buf->len;
|
||||
while (shifted < shift) {
|
||||
this_shift = min(shift - shifted, LOCAL_BUF_LEN);
|
||||
rotate_buf_a_little(buf, this_shift);
|
||||
shifted += this_shift;
|
||||
}
|
||||
}
|
||||
|
||||
static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
|
||||
{
|
||||
struct xdr_buf subbuf;
|
||||
|
||||
xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
|
||||
_rotate_left(&subbuf, shift);
|
||||
}
|
||||
|
||||
static u32
|
||||
|
@ -495,11 +527,8 @@ gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
|
|||
|
||||
seqnum = be64_to_cpup((__be64 *)(ptr + 8));
|
||||
|
||||
if (rrc != 0) {
|
||||
err = rotate_left(kctx, offset, buf, rrc);
|
||||
if (err)
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
if (rrc != 0)
|
||||
rotate_left(offset + 16, buf, rrc);
|
||||
|
||||
err = (*kctx->gk5e->decrypt_v2)(kctx, offset, buf,
|
||||
&headskip, &tailskip);
|
||||
|
|
|
@ -336,7 +336,6 @@ struct rsc {
|
|||
struct svc_cred cred;
|
||||
struct gss_svc_seq_data seqdata;
|
||||
struct gss_ctx *mechctx;
|
||||
char *client_name;
|
||||
};
|
||||
|
||||
static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
|
||||
|
@ -347,9 +346,7 @@ static void rsc_free(struct rsc *rsci)
|
|||
kfree(rsci->handle.data);
|
||||
if (rsci->mechctx)
|
||||
gss_delete_sec_context(&rsci->mechctx);
|
||||
if (rsci->cred.cr_group_info)
|
||||
put_group_info(rsci->cred.cr_group_info);
|
||||
kfree(rsci->client_name);
|
||||
free_svc_cred(&rsci->cred);
|
||||
}
|
||||
|
||||
static void rsc_put(struct kref *ref)
|
||||
|
@ -387,7 +384,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
|
|||
tmp->handle.data = NULL;
|
||||
new->mechctx = NULL;
|
||||
new->cred.cr_group_info = NULL;
|
||||
new->client_name = NULL;
|
||||
new->cred.cr_principal = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -402,8 +399,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
|
|||
spin_lock_init(&new->seqdata.sd_lock);
|
||||
new->cred = tmp->cred;
|
||||
tmp->cred.cr_group_info = NULL;
|
||||
new->client_name = tmp->client_name;
|
||||
tmp->client_name = NULL;
|
||||
new->cred.cr_principal = tmp->cred.cr_principal;
|
||||
tmp->cred.cr_principal = NULL;
|
||||
}
|
||||
|
||||
static struct cache_head *
|
||||
|
@ -501,8 +498,8 @@ static int rsc_parse(struct cache_detail *cd,
|
|||
/* get client name */
|
||||
len = qword_get(&mesg, buf, mlen);
|
||||
if (len > 0) {
|
||||
rsci.client_name = kstrdup(buf, GFP_KERNEL);
|
||||
if (!rsci.client_name)
|
||||
rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL);
|
||||
if (!rsci.cred.cr_principal)
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -932,16 +929,6 @@ struct gss_svc_data {
|
|||
struct rsc *rsci;
|
||||
};
|
||||
|
||||
char *svc_gss_principal(struct svc_rqst *rqstp)
|
||||
{
|
||||
struct gss_svc_data *gd = (struct gss_svc_data *)rqstp->rq_auth_data;
|
||||
|
||||
if (gd && gd->rsci)
|
||||
return gd->rsci->client_name;
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_gss_principal);
|
||||
|
||||
static int
|
||||
svcauth_gss_set_client(struct svc_rqst *rqstp)
|
||||
{
|
||||
|
@ -1220,7 +1207,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
}
|
||||
svcdata->rsci = rsci;
|
||||
cache_get(&rsci->h);
|
||||
rqstp->rq_flavor = gss_svc_to_pseudoflavor(
|
||||
rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
|
||||
rsci->mechctx->mech_type, gc->gc_svc);
|
||||
ret = SVC_OK;
|
||||
goto out;
|
||||
|
|
|
@ -180,14 +180,16 @@ void rpcb_put_local(struct net *net)
|
|||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
struct rpc_clnt *clnt = sn->rpcb_local_clnt;
|
||||
struct rpc_clnt *clnt4 = sn->rpcb_local_clnt4;
|
||||
int shutdown;
|
||||
int shutdown = 0;
|
||||
|
||||
spin_lock(&sn->rpcb_clnt_lock);
|
||||
if (--sn->rpcb_users == 0) {
|
||||
sn->rpcb_local_clnt = NULL;
|
||||
sn->rpcb_local_clnt4 = NULL;
|
||||
if (sn->rpcb_users) {
|
||||
if (--sn->rpcb_users == 0) {
|
||||
sn->rpcb_local_clnt = NULL;
|
||||
sn->rpcb_local_clnt4 = NULL;
|
||||
}
|
||||
shutdown = !sn->rpcb_users;
|
||||
}
|
||||
shutdown = !sn->rpcb_users;
|
||||
spin_unlock(&sn->rpcb_clnt_lock);
|
||||
|
||||
if (shutdown) {
|
||||
|
|
|
@ -407,6 +407,14 @@ static int svc_uses_rpcbind(struct svc_serv *serv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int svc_bind(struct svc_serv *serv, struct net *net)
|
||||
{
|
||||
if (!svc_uses_rpcbind(serv))
|
||||
return 0;
|
||||
return svc_rpcb_setup(serv, net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_bind);
|
||||
|
||||
/*
|
||||
* Create an RPC service
|
||||
*/
|
||||
|
@ -471,15 +479,8 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
|
|||
spin_lock_init(&pool->sp_lock);
|
||||
}
|
||||
|
||||
if (svc_uses_rpcbind(serv)) {
|
||||
if (svc_rpcb_setup(serv, current->nsproxy->net_ns) < 0) {
|
||||
kfree(serv->sv_pools);
|
||||
kfree(serv);
|
||||
return NULL;
|
||||
}
|
||||
if (!serv->sv_shutdown)
|
||||
serv->sv_shutdown = svc_rpcb_cleanup;
|
||||
}
|
||||
if (svc_uses_rpcbind(serv) && (!serv->sv_shutdown))
|
||||
serv->sv_shutdown = svc_rpcb_cleanup;
|
||||
|
||||
return serv;
|
||||
}
|
||||
|
@ -536,8 +537,6 @@ EXPORT_SYMBOL_GPL(svc_shutdown_net);
|
|||
void
|
||||
svc_destroy(struct svc_serv *serv)
|
||||
{
|
||||
struct net *net = current->nsproxy->net_ns;
|
||||
|
||||
dprintk("svc: svc_destroy(%s, %d)\n",
|
||||
serv->sv_program->pg_name,
|
||||
serv->sv_nrthreads);
|
||||
|
@ -552,8 +551,6 @@ svc_destroy(struct svc_serv *serv)
|
|||
|
||||
del_timer_sync(&serv->sv_temptimer);
|
||||
|
||||
svc_shutdown_net(serv, net);
|
||||
|
||||
/*
|
||||
* The last user is gone and thus all sockets have to be destroyed to
|
||||
* the point. Check this.
|
||||
|
|
|
@ -598,6 +598,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
|
|||
|
||||
/* now allocate needed pages. If we get a failure, sleep briefly */
|
||||
pages = (serv->sv_max_mesg + PAGE_SIZE) / PAGE_SIZE;
|
||||
BUG_ON(pages >= RPCSVC_MAXPAGES);
|
||||
for (i = 0; i < pages ; i++)
|
||||
while (rqstp->rq_pages[i] == NULL) {
|
||||
struct page *p = alloc_page(GFP_KERNEL);
|
||||
|
@ -612,7 +613,6 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
|
|||
rqstp->rq_pages[i] = p;
|
||||
}
|
||||
rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */
|
||||
BUG_ON(pages >= RPCSVC_MAXPAGES);
|
||||
|
||||
/* Make arg->head point to first page and arg->pages point to rest */
|
||||
arg = &rqstp->rq_arg;
|
||||
|
@ -973,7 +973,7 @@ void svc_close_net(struct svc_serv *serv, struct net *net)
|
|||
svc_clear_pools(serv, net);
|
||||
/*
|
||||
* At this point the sp_sockets lists will stay empty, since
|
||||
* svc_enqueue will not add new entries without taking the
|
||||
* svc_xprt_enqueue will not add new entries without taking the
|
||||
* sp_lock and checking XPT_BUSY.
|
||||
*/
|
||||
svc_clear_list(&serv->sv_tempsocks, net);
|
||||
|
|
|
@ -746,6 +746,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
struct svc_cred *cred = &rqstp->rq_cred;
|
||||
|
||||
cred->cr_group_info = NULL;
|
||||
cred->cr_principal = NULL;
|
||||
rqstp->rq_client = NULL;
|
||||
|
||||
if (argv->iov_len < 3*4)
|
||||
|
@ -773,7 +774,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
svc_putnl(resv, RPC_AUTH_NULL);
|
||||
svc_putnl(resv, 0);
|
||||
|
||||
rqstp->rq_flavor = RPC_AUTH_NULL;
|
||||
rqstp->rq_cred.cr_flavor = RPC_AUTH_NULL;
|
||||
return SVC_OK;
|
||||
}
|
||||
|
||||
|
@ -811,6 +812,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
int len = argv->iov_len;
|
||||
|
||||
cred->cr_group_info = NULL;
|
||||
cred->cr_principal = NULL;
|
||||
rqstp->rq_client = NULL;
|
||||
|
||||
if ((len -= 3*4) < 0)
|
||||
|
@ -847,7 +849,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
svc_putnl(resv, RPC_AUTH_NULL);
|
||||
svc_putnl(resv, 0);
|
||||
|
||||
rqstp->rq_flavor = RPC_AUTH_UNIX;
|
||||
rqstp->rq_cred.cr_flavor = RPC_AUTH_UNIX;
|
||||
return SVC_OK;
|
||||
|
||||
badcred:
|
||||
|
|
Загрузка…
Ссылка в новой задаче