From bfa4b36525577dfe2b52c7e52e03525aba1b74d3 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 25 Apr 2012 16:49:18 -0400 Subject: nfsd: state.c should include current_stateid.h OK, admittedly I'm mainly just trying to shut sparse up. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 277c989e7f7b..cf26658e4e31 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -42,6 +42,7 @@ #include #include "xdr4.h" #include "vfs.h" +#include "current_stateid.h" #define NFSDDBG_FACILITY NFSDDBG_PROC -- cgit v1.2.3 From 2355c59644def5950f982fc1509dd45037e79ded Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 25 Apr 2012 16:56:22 -0400 Subject: nfsd4: fix missing "static" Signed-off-by: J. Bruce Fields --- fs/nfsd/export.c | 2 +- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/nfs4state.c | 2 +- fs/nfsd/nfs4xdr.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index dcb52b884519..ec16364f782e 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -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", diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 4767429264a2..2b8e9a93fdd5 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -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, }; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cf26658e4e31..3ccebdc4812c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4021,7 +4021,7 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access) __set_bit(access, &lock_stp->st_access_bmap); } -__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); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index bcd8904ab1e3..59c5184e6f0d 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3399,7 +3399,7 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr, return 0; } -__be32 +static __be32 nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_test_stateid *test_stateid) { -- cgit v1.2.3 From bc1b542be9aba019ea89ef04da1f3592018a2a60 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 25 Apr 2012 16:58:05 -0400 Subject: nfsd4: preserve __user annotation on cld downcall msg Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4recover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 2b8e9a93fdd5..260d8c8f691b 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c @@ -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); -- cgit v1.2.3 From 57b7b43b403136dc18d067909050e8677f97aeed Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 25 Apr 2012 17:58:50 -0400 Subject: nfsd4: int/__be32 fixes In each of these cases there's a simple unambiguous correct choice, and no actual bug. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 22 +++++++++++----------- fs/nfsd/nfs4xdr.c | 12 ++++++------ fs/nfsd/xdr4.h | 6 +++--- 3 files changed, 20 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3ccebdc4812c..064f61acfe00 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -886,7 +886,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; /* @@ -1477,7 +1477,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, 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]; @@ -1599,7 +1599,7 @@ error: 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, @@ -1627,7 +1627,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)); @@ -1658,7 +1658,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; @@ -1674,7 +1674,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; @@ -1819,7 +1819,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 @@ -1915,7 +1915,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; @@ -2020,7 +2020,7 @@ __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); @@ -2056,7 +2056,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) @@ -3347,7 +3347,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 diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 59c5184e6f0d..f7cc41144dc1 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3251,7 +3251,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 +3306,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,7 +3355,7 @@ 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; @@ -3377,7 +3377,7 @@ nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int 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; @@ -3400,7 +3400,7 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr, } static __be32 -nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, int nfserr, +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 +3503,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; diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index 1b3501598ab5..acd127d4ee82 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h @@ -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, -- cgit v1.2.3 From d1829b38241394c0c66d407a165fbd6d9897c241 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 25 Apr 2012 18:04:54 -0400 Subject: nfsd4: fix free_stateid return endianness Cc: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f7cc41144dc1..f839be1a202c 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -3362,7 +3362,7 @@ nfsd4_encode_destroy_session(struct nfsd4_compoundres *resp, __be32 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,7 +3371,7 @@ nfsd4_encode_free_stateid(struct nfsd4_compoundres *resp, int nfserr, return nfserr; RESERVE_SPACE(4); - WRITE32(nfserr); + *p++ = nfserr; ADJUST_ARGS(); return nfserr; } -- cgit v1.2.3 From 45eaa1c1a16122a98bf995c004c23806759d2e5f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 25 Apr 2012 18:11:04 -0400 Subject: nfsd4: fix change attribute endianness Though actually this doesn't matter much, as NFSv4.0 clients are required to treat the change attribute as opaque. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f839be1a202c..61555e756410 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -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); } -- cgit v1.2.3 From e7a0444aef4a1649bc155fd5c6d6ab3f8bdc88ab Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Tue, 24 Apr 2012 11:07:59 -0400 Subject: nfsd: add IPv6 addr escaping to fs_location hosts The fs_location->hosts list is split on colons, but this doesn't work when IPv6 addresses are used (they contain colons). This patch adds the function nfsd4_encode_components_esc() to allow the caller to specify escape characters when splitting on 'sep'. In order to fix referrals, this patch must be used with the mountd patch that similarly fixes IPv6 [] escaping. Signed-off-by: Weston Andros Adamson Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4xdr.c | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 61555e756410..30817e9f1bb7 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -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); -- cgit v1.2.3 From 9793f7c88937e7ac07305ab1af1a519225836823 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 2 May 2012 16:08:38 +0400 Subject: SUNRPC: new svc_bind() routine introduced This new routine is responsible for service registration in a specified network context. The idea is to separate service creation from per-net operations. Note also: since registering service with svc_bind() can fail, the service will be destroyed and during destruction it will try to unregister itself from rpcbind. In this case unregistration has to be skipped. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 6 ++++++ fs/nfs/callback.c | 8 ++++++++ fs/nfsd/nfssvc.c | 9 +++++++++ include/linux/sunrpc/svc.h | 1 + net/sunrpc/rpcb_clnt.c | 12 +++++++----- net/sunrpc/svc.c | 19 ++++++++++--------- 6 files changed, 41 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 1ead0750cdbb..b7e92ed56885 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -324,6 +324,12 @@ int lockd_up(struct net *net) goto out; } + error = svc_bind(serv, net); + if (error < 0) { + printk(KERN_WARNING "lockd_up: bind service failed\n"); + goto destroy_and_out; + } + error = make_socks(serv, net); if (error < 0) goto destroy_and_out; diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index eb95f5091c1a..26b38fb8102e 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -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) { diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index cb4d51d8cbdb..0762f3c9e0fb 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 51b29ac45a8e..2b43e0214261 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -416,6 +416,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, diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 78ac39fd9fe7..4c38b33ab8a8 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -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) { diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 4153846984ac..e6d542cee0f3 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -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; } -- cgit v1.2.3 From 786185b5f8abefa6a8a16695bb4a59c164d5a071 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Fri, 4 May 2012 12:49:41 +0400 Subject: SUNRPC: move per-net operations from svc_destroy() The idea is to separate service destruction and per-net operations, because these are two different things and the mix looks ugly. Notes: 1) For NFS server this patch looks ugly (sorry for that). But these place will be rewritten soon during NFSd containerization. 2) LockD per-net counter increase int lockd_up() was moved prior to make_socks() to make lockd_down_net() call safe in case of error. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 27 +++++++++++++++------------ fs/nfs/callback.c | 3 +++ fs/nfsd/nfsctl.c | 12 +++++++++--- fs/nfsd/nfssvc.c | 14 ++++++++++++++ net/sunrpc/svc.c | 4 ---- 5 files changed, 41 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index b7e92ed56885..3250f280a171 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -257,7 +257,7 @@ static int lockd_up_net(struct net *net) 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); @@ -272,6 +272,7 @@ static int lockd_up_net(struct net *net) err_socks: svc_rpcb_cleanup(serv, net); err_rpcb: + ln->nlmsvc_users--; return error; } @@ -299,6 +300,7 @@ int lockd_up(struct net *net) { struct svc_serv *serv; int error = 0; + struct lockd_net *ln = net_generic(net, lockd_net_id); mutex_lock(&nlmsvc_mutex); /* @@ -330,9 +332,11 @@ int lockd_up(struct net *net) goto destroy_and_out; } + ln->nlmsvc_users++; + error = make_socks(serv, net); if (error < 0) - goto destroy_and_out; + goto err_start; /* * Create the kernel thread and wait for it to start. @@ -344,7 +348,7 @@ int lockd_up(struct net *net) printk(KERN_WARNING "lockd_up: svc_rqst allocation failed, error=%d\n", error); - goto destroy_and_out; + goto err_start; } svc_sock_update_bufs(serv); @@ -358,7 +362,7 @@ int lockd_up(struct net *net) nlmsvc_rqst = NULL; printk(KERN_WARNING "lockd_up: kthread_run failed, error=%d\n", error); - goto destroy_and_out; + goto err_start; } /* @@ -368,14 +372,14 @@ int lockd_up(struct net *net) destroy_and_out: svc_destroy(serv); out: - if (!error) { - struct lockd_net *ln = net_generic(net, lockd_net_id); - - ln->nlmsvc_users++; + if (!error) nlmsvc_users++; - } mutex_unlock(&nlmsvc_mutex); return error; + +err_start: + lockd_down_net(net); + goto destroy_and_out; } EXPORT_SYMBOL_GPL(lockd_up); @@ -386,11 +390,10 @@ void lockd_down(struct net *net) { mutex_lock(&nlmsvc_mutex); + lockd_down_net(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); diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 26b38fb8102e..cff39406f965 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -314,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; } @@ -328,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; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 72699885ac48..c55298ed5772 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -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; } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 0762f3c9e0fb..ee709fc8f58b 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -382,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)); @@ -426,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; @@ -441,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"); @@ -473,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); @@ -556,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); @@ -668,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; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index e6d542cee0f3..b7210f5cc893 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -537,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); @@ -553,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. -- cgit v1.2.3 From 4db77695bf5738bdafa83d1b58b64cbecc6f55e7 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Apr 2012 18:22:40 +0400 Subject: LockD: pass service to per-net up and down functions Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 3250f280a171..58ddc38cfccd 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -251,10 +251,9 @@ 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++) @@ -276,10 +275,9 @@ err_rpcb: 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) { @@ -307,7 +305,7 @@ int lockd_up(struct net *net) * Check whether we're already up and running. */ if (nlmsvc_rqst) { - error = lockd_up_net(net); + error = lockd_up_net(nlmsvc_rqst->rq_server, net); goto out; } @@ -378,7 +376,7 @@ out: return error; err_start: - lockd_down_net(net); + lockd_down_net(serv, net); goto destroy_and_out; } EXPORT_SYMBOL_GPL(lockd_up); @@ -390,7 +388,7 @@ void lockd_down(struct net *net) { mutex_lock(&nlmsvc_mutex); - lockd_down_net(net); + lockd_down_net(nlmsvc_rqst->rq_server, net); if (nlmsvc_users) { if (--nlmsvc_users) goto out; -- cgit v1.2.3 From dbf9b5d74ceae787607e3d7db626fffa8be3c03d Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Apr 2012 18:22:47 +0400 Subject: LockD: use existing per-net data function on service creation This patch also replaces svc_rpcb_setup() with svc_bind(). Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 58ddc38cfccd..71c6c3122d35 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -259,9 +259,9 @@ static int lockd_up_net(struct svc_serv *serv, struct net *net) 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) @@ -270,7 +270,7 @@ static int lockd_up_net(struct svc_serv *serv, struct net *net) err_socks: svc_rpcb_cleanup(serv, net); -err_rpcb: +err_bind: ln->nlmsvc_users--; return error; } @@ -298,7 +298,6 @@ int lockd_up(struct net *net) { struct svc_serv *serv; int error = 0; - struct lockd_net *ln = net_generic(net, lockd_net_id); mutex_lock(&nlmsvc_mutex); /* @@ -324,17 +323,9 @@ int lockd_up(struct net *net) goto out; } - error = svc_bind(serv, net); - if (error < 0) { - printk(KERN_WARNING "lockd_up: bind service failed\n"); - goto destroy_and_out; - } - - ln->nlmsvc_users++; - - error = make_socks(serv, net); + error = lockd_up_net(serv, net); if (error < 0) - goto err_start; + goto err_net; /* * Create the kernel thread and wait for it to start. @@ -367,7 +358,7 @@ int lockd_up(struct net *net) * 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) @@ -377,7 +368,7 @@ out: err_start: lockd_down_net(serv, net); - goto destroy_and_out; + goto err_net; } EXPORT_SYMBOL_GPL(lockd_up); -- cgit v1.2.3 From 24452239094a8b52f54fd4403f6e177837cecf67 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Apr 2012 18:22:54 +0400 Subject: LockD: service creation function introduced This function creates service if it doesn't exist, or increases usage counter if it does, and returns a pointer to it. The usage counter will be droppepd by svc_destroy() later in lockd_up(). Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 71c6c3122d35..ad11ea777581 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -291,21 +291,20 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net) } } -/* - * Bring up the lockd process if it's not already up. - */ -int lockd_up(struct net *net) +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(nlmsvc_rqst->rq_server, 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; } /* @@ -316,11 +315,28 @@ 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); + } + 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 = 0; + + mutex_lock(&nlmsvc_mutex); + + serv = lockd_create_svc(); + if (IS_ERR(serv)) { + error = PTR_ERR(serv); + goto err_create; } error = lockd_up_net(serv, net); @@ -360,9 +376,9 @@ int lockd_up(struct net *net) */ err_net: svc_destroy(serv); -out: if (!error) nlmsvc_users++; +err_create: mutex_unlock(&nlmsvc_mutex); return error; -- cgit v1.2.3 From 7d13ec761ae45d5e401965c73d855840ff8cdd23 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Apr 2012 18:23:02 +0400 Subject: LockD: move global usage counter manipulation from error path Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index ad11ea777581..53cd69ef2add 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -329,7 +329,7 @@ static struct svc_serv *lockd_create_svc(void) int lockd_up(struct net *net) { struct svc_serv *serv; - int error = 0; + int error; mutex_lock(&nlmsvc_mutex); @@ -370,14 +370,13 @@ int lockd_up(struct net *net) 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. */ err_net: svc_destroy(serv); - if (!error) - nlmsvc_users++; err_create: mutex_unlock(&nlmsvc_mutex); return error; -- cgit v1.2.3 From 3d1221dfa95ca876535c5947626f3834343e8fbc Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Apr 2012 18:23:09 +0400 Subject: LockD: service start function introduced This is just a code move, which from my POV makes the code look better. I.e. now on start we have 3 different stages: 1) Service creation. 2) Service per-net data allocation. 3) Service start. Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 67 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index 53cd69ef2add..b47bf7755008 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -291,6 +291,46 @@ static void lockd_down_net(struct svc_serv *serv, 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; @@ -343,32 +383,9 @@ int lockd_up(struct net *net) if (error < 0) 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 err_start; - } - - 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); + error = lockd_start_svc(serv); + if (error < 0) goto err_start; - } nlmsvc_users++; /* -- cgit v1.2.3 From 8dbf28e495337b0c66657e3e278ae0ff175b7140 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsbursky Date: Wed, 25 Apr 2012 18:23:16 +0400 Subject: LockD: add debug message to start and stop functions Signed-off-by: Stanislav Kinsbursky Signed-off-by: J. Bruce Fields --- fs/lockd/svc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index b47bf7755008..80938fda67e0 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -266,6 +266,7 @@ static int lockd_up_net(struct svc_serv *serv, struct net *net) 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: @@ -283,6 +284,7 @@ static void lockd_down_net(struct svc_serv *serv, struct net *net) 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", @@ -360,6 +362,7 @@ static struct svc_serv *lockd_create_svc(void) printk(KERN_WARNING "lockd_up: create service failed\n"); return ERR_PTR(-ENOMEM); } + dprintk("lockd_up: service created\n"); return serv; } @@ -426,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: -- cgit v1.2.3 From f07ea10dc8f20dfc215b15b012741fcf9f5aa17b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Tue, 15 May 2012 17:42:08 -0400 Subject: NFSD: SETCLIENTID_CONFIRM returns NFS4ERR_CLID_INUSE too often According to RFC 3530bis, the only items SETCLIENTID_CONFIRM processing should be concerned with is the clientid, clientid verifier, and principal. The client's IP address is not supposed to be interesting. And, NFS4ERR_CLID_INUSE is meant only for principal mismatches. I triggered this logic with a prototype UCS client -- one that uses the same nfs_client_id4 string for all servers. The client mounted our server via its IPv4, then via its IPv6 address. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 064f61acfe00..91aec738e8fd 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2214,7 +2214,6 @@ 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; @@ -2232,17 +2231,12 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, 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. */ + status = nfserr_clid_inuse; if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) { /* * RFC 3530 14.2.34 CASE 1: @@ -2255,7 +2249,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfsd4_probe_callback(conf); expire_client(unconf); status = nfs_ok; - } } else if (conf && !unconf) { /* @@ -2297,11 +2290,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, * 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; } -out: + nfs4_unlock_state(); return status; } -- cgit v1.2.3 From 5ae037e599de5a97eb6ae1054db2fdfbfbac65b5 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 11 May 2012 09:45:11 -0400 Subject: nfsd: consolidate set_access and set_deny These functions are identical. Also, rename them to bmap_to_share_mode to better reflect what they do, and have them just return the result instead of passing in a pointer to the storage location. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 91aec738e8fd..8034c2b67a2a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -448,34 +448,24 @@ 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; - } -} - -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 ; + access |= i; } + return access; } static int 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; -- cgit v1.2.3 From 3a3286147f557f05e002be3ec7512b582373ae65 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 11 May 2012 09:45:12 -0400 Subject: nfsd: make test_share a bool return All of the callers treat the return that way already. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8034c2b67a2a..88a897a484ec 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -460,15 +460,15 @@ bmap_to_share_mode(unsigned long bmap) { return access; } -static int +static bool test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { unsigned int access, deny; 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; } static int nfs4_access_to_omode(u32 access) -- cgit v1.2.3 From 82c5ff1b1409f43ccb282cbdfcb06991714e4c5f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 11 May 2012 09:45:13 -0400 Subject: nfsd: wrap accesses to st_access_bmap Currently, we do this for the most part with "bare" bitops, but eventually we'll need to expand the share mode code to handle access and deny modes on other nodes. In order to facilitate that code in the future, move to some generic accessor functions. For now, these are mostly static inlines, but eventually we'll want to move these to "real" functions that are able to handle multi-node configurations or have a way to "swap in" new operations to be done in lieu of or in conjunction with these atomic bitops. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 82 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 88a897a484ec..83066abc66bb 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -471,6 +471,27 @@ test_share(struct nfs4_ol_stateid *stp, struct nfsd4_open *open) { 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); +} + static int nfs4_access_to_omode(u32 access) { switch (access & NFS4_SHARE_ACCESS_BOTH) { @@ -484,6 +505,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); @@ -492,16 +527,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; } @@ -2435,7 +2461,7 @@ 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_access(open->op_share_access, stp); __set_bit(open->op_share_deny, &stp->st_deny_bmap); stp->st_openstp = NULL; } @@ -2772,7 +2798,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) @@ -2787,7 +2813,7 @@ 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_access(op_share_access, stp); __set_bit(open->op_share_deny, &stp->st_deny_bmap); return nfs_ok; @@ -3263,18 +3289,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 @@ -3285,9 +3311,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: @@ -3636,10 +3662,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) @@ -3693,8 +3719,8 @@ 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; } @@ -3995,10 +4021,10 @@ 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); } 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) -- cgit v1.2.3 From ce0fc43c5a09d50ff5ffe208cbb0d3a9c82f038a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 11 May 2012 09:45:14 -0400 Subject: nfsd: wrap all accesses to st_deny_bmap Handle the st_deny_bmap in a similar fashion to the st_access_bmap. Add accessor functions and use those instead of bare bitops. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 83066abc66bb..c474161fd4b6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -492,6 +492,27 @@ 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) { switch (access & NFS4_SHARE_ACCESS_BOTH) { @@ -2462,7 +2483,7 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, stp->st_access_bmap = 0; stp->st_deny_bmap = 0; set_access(open->op_share_access, stp); - __set_bit(open->op_share_deny, &stp->st_deny_bmap); + set_deny(open->op_share_deny, stp); stp->st_openstp = NULL; } @@ -2541,8 +2562,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; @@ -2814,7 +2835,7 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c } /* remember the open */ set_access(op_share_access, stp); - __set_bit(open->op_share_deny, &stp->st_deny_bmap); + set_deny(open->op_share_deny, stp); return nfs_ok; } @@ -3687,12 +3708,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); } } @@ -3724,14 +3745,14 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, 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)); -- cgit v1.2.3 From 5fb35a3a9bc482110b2c6afd1a5004909fa12501 Mon Sep 17 00:00:00 2001 From: Weston Andros Adamson Date: Thu, 10 May 2012 15:31:10 -0400 Subject: nfsd: return 0 on reads of fault injection files debugfs read operations were returning the contents of an uninitialized u64. Signed-off-by: Weston Andros Adamson Signed-off-by: J. Bruce Fields --- fs/nfsd/fault_inject.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c index 9559ce468732..e6c38159622f 100644 --- a/fs/nfsd/fault_inject.c +++ b/fs/nfsd/fault_inject.c @@ -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; } -- cgit v1.2.3 From 11ae681052022a60c1b5a697f00052a6c8c61f7e Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 4 May 2012 15:16:06 -0400 Subject: nfsd4: exchange_id has a pointless copy We just verified above that these two verifiers are already the same. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c474161fd4b6..8a06cb07896a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1587,7 +1587,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, * 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; } -- cgit v1.2.3 From 2786cc3a056e642809a348d9b1e6a857b9d23d25 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 12 May 2012 20:53:20 -0400 Subject: nfsd4: exchange_id error cleanup There's no point to the dprintk here as the main proc_compound loop already does this. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8a06cb07896a..aca84c942595 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1545,13 +1545,11 @@ 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); nfs4_lock_state(); - status = nfs_ok; - conf = find_confirmed_client_by_str(dname, strhashval); if (conf) { if (!clp_used_exchangeid(conf)) { @@ -1630,8 +1628,6 @@ out_copy: out: nfs4_unlock_state(); -error: - dprintk("nfsd4_exchange_id returns %d\n", ntohl(status)); return status; } -- cgit v1.2.3 From ea236d0704b9b9e7742b107313bbdddbba62724a Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 12 May 2012 21:08:41 -0400 Subject: nfsd4: exchange_id: check creds before killing confirmed client We mustn't allow a client to destroy another client with established state unless it has the right credential. And some minor cleanup. (Note: our comparison of credentials is actually pretty bogus currently; that will need to be fixed in another patch.) Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index aca84c942595..f372a6edef34 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1556,6 +1556,14 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, status = nfserr_clid_inuse; /* XXX: ? */ goto out; } + if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { + /* 18.35.4 case 9 */ + if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) + status = nfserr_perm; + else /* case 3 */ + status = nfserr_clid_inuse; + goto out; + } if (!same_verf(&verf, &conf->cl_verifier)) { /* 18.35.4 case 8 */ if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { @@ -1566,15 +1574,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, 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) { - status = nfserr_perm; - 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). -- cgit v1.2.3 From 1a308118c238c5aa0af3d303b42fa6bbee279394 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 12 May 2012 21:32:30 -0400 Subject: nfsd4: allow an EXCHANGE_ID to kill a 4.0 client Following rfc 5661 section 2.4.1, we can permit a 4.1 client to remove an established 4.0 client's state. (But we don't allow updates.) Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f372a6edef34..c88f5c103e87 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1553,8 +1553,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, conf = find_confirmed_client_by_str(dname, strhashval); if (conf) { if (!clp_used_exchangeid(conf)) { - status = nfserr_clid_inuse; /* XXX: ? */ - goto out; + if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { + status = nfserr_inval; /* buggy client */ + goto out; + } } if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { /* 18.35.4 case 9 */ @@ -1574,6 +1576,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, expire_client(conf); goto out_new; } + if (!clp_used_exchangeid(conf)) { + status = nfserr_inval; + goto out; + } /* * Set bit when the owner id and verifier map to an already * confirmed client id (18.35.3). -- cgit v1.2.3 From 83e08fd46cea3b59dce9285461e3854b594042af Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 May 2012 09:08:10 -0400 Subject: nfsd4: exchange_id cleanup: local shorthands for repeated tests Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c88f5c103e87..3b6d61008673 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1520,6 +1520,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, 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 " @@ -1552,23 +1553,26 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, nfs4_lock_state(); conf = find_confirmed_client_by_str(dname, strhashval); if (conf) { + bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); + bool verfs_match = same_verf(&verf, &conf->cl_verifier); + if (!clp_used_exchangeid(conf)) { - if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { + if (update) { status = nfserr_inval; /* buggy client */ goto out; } } - if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) { + if (!creds_match) { /* 18.35.4 case 9 */ - if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) + if (update) status = nfserr_perm; else /* case 3 */ status = nfserr_clid_inuse; goto out; } - if (!same_verf(&verf, &conf->cl_verifier)) { + if (!verfs_match) { /* 18.35.4 case 8 */ - if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { + if (update) { status = nfserr_not_same; goto out; } @@ -1595,7 +1599,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, } /* 18.35.4 case 7 */ - if (exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) { + if (update) { status = nfserr_noent; goto out; } -- cgit v1.2.3 From 2dbb269dfed9e8653be81d49e8f109a243bea25c Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 May 2012 09:47:11 -0400 Subject: nfsd4: exchange_id cleanup: comments Make these comments a bit more concise and uniform. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3b6d61008673..93fcf53daee1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1550,6 +1550,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, strhashval = clientstr_hashval(dname); + /* Cases below refer to rfc 5661 section 18.35.4: */ nfs4_lock_state(); conf = find_confirmed_client_by_str(dname, strhashval); if (conf) { @@ -1557,26 +1558,24 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, bool verfs_match = same_verf(&verf, &conf->cl_verifier); if (!clp_used_exchangeid(conf)) { - if (update) { - status = nfserr_inval; /* buggy client */ + if (update) { /* buggy client */ + status = nfserr_inval; goto out; } } - if (!creds_match) { - /* 18.35.4 case 9 */ + if (!creds_match) { /* case 9 */ if (update) status = nfserr_perm; else /* case 3 */ status = nfserr_clid_inuse; goto out; } - if (!verfs_match) { - /* 18.35.4 case 8 */ + if (!verfs_match) { /* case 8 */ if (update) { status = nfserr_not_same; goto out; } - /* Client reboot: destroy old state */ + /* case 5, client reboot */ expire_client(conf); goto out_new; } @@ -1584,38 +1583,23 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, status = nfserr_inval; goto out; } - /* - * Set bit when the owner id and verifier map to an already - * confirmed client id (18.35.3). - */ + /* case 2 */ exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; - - /* - * Falling into 18.35.4 case 2, possible router replay. - * Leave confirmed record intact and return same result. - */ new = conf; goto out_copy; } - /* 18.35.4 case 7 */ - if (update) { + 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; -- cgit v1.2.3 From 136e658d621f71b67982dda51a2327830146ef9d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 12 May 2012 20:37:23 -0400 Subject: nfsd4: rearrange exchange_id logic to simplify Minor cleanup: it's simpler to have separate code paths for the update and non-update cases. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 93fcf53daee1..491f13a70db1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1557,36 +1557,36 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); bool verfs_match = same_verf(&verf, &conf->cl_verifier); - if (!clp_used_exchangeid(conf)) { - if (update) { /* buggy client */ + if (update) { + if (!clp_used_exchangeid(conf)) { /* buggy client */ status = nfserr_inval; goto out; } - } - if (!creds_match) { /* case 9 */ - if (update) + if (!creds_match) { /* case 9 */ status = nfserr_perm; - else /* case 3 */ - status = nfserr_clid_inuse; - goto out; - } - if (!verfs_match) { /* case 8 */ - if (update) { + goto out; + } + if (!verfs_match) { /* case 8 */ status = nfserr_not_same; goto out; } - /* case 5, client reboot */ - expire_client(conf); - goto out_new; + /* case 6 */ + exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; + new = conf; + goto out_copy; } - if (!clp_used_exchangeid(conf)) { - status = nfserr_inval; + if (!creds_match) { /* case 3 */ + status = nfserr_clid_inuse; goto out; } - /* case 2 */ - exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; - new = conf; - goto out_copy; + if (verfs_match) { /* case 2 */ + exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; + new = conf; + goto out_copy; + } + /* case 5, client reboot */ + expire_client(conf); + goto out_new; } if (update) { /* case 7 */ -- cgit v1.2.3 From 631fc9ea05c97e5d1d14ea58a7347be4857d09da Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 May 2012 15:57:23 -0400 Subject: nfsd4: allow removing clients not holding state RFC 5661 actually says we should allow an exchange_id to remove a matching client, even if the exchange_id comes from a different principal, *if* the victim client lacks any state. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 491f13a70db1..5415550a63a9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1508,6 +1508,19 @@ 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, @@ -1576,8 +1589,11 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out_copy; } if (!creds_match) { /* case 3 */ - status = nfserr_clid_inuse; - goto out; + if (client_has_state(conf)) { + status = nfserr_clid_inuse; + goto out; + } + goto expire_client; } if (verfs_match) { /* case 2 */ exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; @@ -1585,6 +1601,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out_copy; } /* case 5, client reboot */ +expire_client: expire_client(conf); goto out_new; } -- cgit v1.2.3 From 03a4e1f6ddf25f48848e1bddcffc0ad489648331 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 May 2012 19:55:22 -0400 Subject: nfsd4: move principal name into svc_cred Instead of keeping the principal name associated with a request in a structure that's private to auth_gss and using an accessor function, move it to svc_cred. Signed-off-by: J. Bruce Fields --- fs/nfs/callback.c | 2 +- fs/nfsd/nfs4callback.c | 5 +++-- fs/nfsd/nfs4state.c | 34 +++++++++++++++++----------------- fs/nfsd/state.h | 1 - include/linux/sunrpc/svcauth.h | 9 +++++++++ include/linux/sunrpc/svcauth_gss.h | 1 - net/sunrpc/auth_gss/svcauth_gss.c | 25 ++++++------------------- net/sunrpc/svcauth_unix.c | 2 ++ 8 files changed, 38 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index cff39406f965..970659daa323 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -343,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; diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index c8e9f637153a..a5fd6b982f27 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -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; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 5415550a63a9..37bafb290c11 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1087,9 +1087,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); } @@ -1170,12 +1168,20 @@ 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_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) @@ -1242,25 +1248,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); @@ -1279,7 +1280,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, 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; diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 89ab137d379a..849091e16ea6 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -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 */ diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 2c54683b91de..16fe477a96e0 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -15,13 +15,22 @@ #include #include #include +#include struct svc_cred { uid_t cr_uid; gid_t cr_gid; struct group_info *cr_group_info; + 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; diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h index 7c32daa025eb..726aff1a5201 100644 --- a/include/linux/sunrpc/svcauth_gss.h +++ b/include/linux/sunrpc/svcauth_gss.h @@ -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 */ diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index f0a0cd4470b7..d091d7d09bea 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -335,7 +335,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); @@ -346,9 +345,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) @@ -386,7 +383,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 @@ -401,8 +398,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 * @@ -496,8 +493,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; } @@ -927,16 +924,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) { diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 9c3b9f014468..12e4897d0bf3 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -740,6 +740,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) @@ -805,6 +806,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) -- cgit v1.2.3 From 8fbba96e5b327665265ad02b7f331b68536828bf Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 May 2012 21:20:54 -0400 Subject: nfsd4: stricter cred comparison for setclientid/exchange_id The typical setclientid or exchange_id will probably be performed with a credential that maps to either root or nobody, so comparing just uid's is unlikely to be useful. So, use everything else we can get our hands on. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 37bafb290c11..6dc0cfb37541 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1201,11 +1201,31 @@ same_clid(clientid_t *cl1, clientid_t *cl2) return (cl1->cl_boot == cl2->cl_boot) && (cl1->cl_id == cl2->cl_id); } +static bool groups_equal(struct group_info *g1, struct group_info *g2) +{ + int i; + + if (g1->ngroups != g2->ngroups) + return false; + for (i=0; ingroups; i++) + if (GROUP_AT(g1, i) != GROUP_AT(g2, i)) + return false; + return true; +} + /* XXX what about NGROUP */ static int same_creds(struct svc_cred *cr1, struct svc_cred *cr2) { - return cr1->cr_uid == cr2->cr_uid; + if ((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) -- cgit v1.2.3 From d5497fc693a446ce9100fcf4117c3f795ddfd0d2 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Mon, 14 May 2012 22:06:49 -0400 Subject: nfsd4: move rq_flavor into svc_cred Move the rq_flavor into struct svc_cred, and use it in setclientid and exchange_id comparisons as well. Signed-off-by: J. Bruce Fields --- fs/nfsd/auth.c | 2 +- fs/nfsd/export.c | 6 +++--- fs/nfsd/nfs4idmap.c | 4 ++-- fs/nfsd/nfs4state.c | 6 +++--- include/linux/sunrpc/svc.h | 1 - include/linux/sunrpc/svcauth.h | 1 + net/sunrpc/auth_gss/svcauth_gss.c | 2 +- net/sunrpc/svcauth_unix.c | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index 79717a40daba..b42eaf3aac16 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -10,7 +10,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; diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index ec16364f782e..ba233499b9a5 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -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; diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 286a7f8f2024..dae36f1dee95 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -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); } diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6dc0cfb37541..c743cdf51ebc 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1177,6 +1177,7 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) 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; @@ -1213,11 +1214,11 @@ static bool groups_equal(struct group_info *g1, struct group_info *g2) return true; } -/* XXX what about NGROUP */ static int same_creds(struct svc_cred *cr1, struct svc_cred *cr2) { - if ((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; @@ -1299,7 +1300,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; gen_confirm(clp); clp->cl_cb_session = NULL; return clp; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 2b43e0214261..40e0a273faea 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -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 */ diff --git a/include/linux/sunrpc/svcauth.h b/include/linux/sunrpc/svcauth.h index 16fe477a96e0..dd74084a9799 100644 --- a/include/linux/sunrpc/svcauth.h +++ b/include/linux/sunrpc/svcauth.h @@ -21,6 +21,7 @@ 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 */ }; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index d091d7d09bea..bcb773781ec0 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -1202,7 +1202,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; diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 12e4897d0bf3..88962cf34377 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -768,7 +768,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; } @@ -839,7 +839,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: -- cgit v1.2.3 From e98479b8d6a451bfae1a29e9b2dd503e435f7a48 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 18 May 2012 21:34:55 -0400 Subject: nfsd4: setclientid remove unnecessary terms from a logical expression Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c743cdf51ebc..7bb4e88f33c7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2344,9 +2344,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, 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)))) { + } else if ((!conf || !same_verf(&conf->cl_confirm, &confirm)) + && (!unconf || !same_verf(&unconf->cl_confirm, &confirm))) { /* * RFC 3530 14.2.34 CASE 4: * Client probably hasn't noticed that we rebooted yet. -- cgit v1.2.3 From 63db46328a9854d0adcde69871a28d94e0fc7746 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 18 May 2012 21:54:19 -0400 Subject: nfsd4: setclientid/confirm comment cleanup Be a little more concise. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 67 +++++++++-------------------------------------------- 1 file changed, 11 insertions(+), 56 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 7bb4e88f33c7..dd4c6d36534b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2169,17 +2169,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; @@ -2192,18 +2188,10 @@ 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 - */ + /* case 4: placed first, because it's the normal case */ if (unconf) expire_client(unconf); new = create_client(clname, dname, rqstp, &clverifier); @@ -2211,10 +2199,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; gen_clid(new); } else if (same_verf(&conf->cl_verifier, &clverifier)) { - /* - * RFC 3530 14.2.33 CASE 1: - * probable callback update - */ + /* case 1: probable callback update */ if (unconf) { /* Note this is removing unconfirmed {*x***}, * which is stronger than RFC recommended {vxc**}. @@ -2228,21 +2213,13 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, 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. - */ + /* case 2: probable client reboot: */ 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. - */ + /* case 3: probable client reboot: */ expire_client(unconf); new = create_client(clname, dname, rqstp, &clverifier); if (new == NULL) @@ -2266,11 +2243,6 @@ 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, @@ -2293,17 +2265,10 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, conf = find_confirmed_client(clid); unconf = find_unconfirmed_client(clid); - /* - * 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. - */ + /* cases below refer to rfc 3530 section 14.2.34: */ status = nfserr_clid_inuse; if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) { - /* - * RFC 3530 14.2.34 CASE 1: - * callback update - */ + /* case 1: callback update */ if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) status = nfserr_clid_inuse; else { @@ -2313,21 +2278,14 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, status = nfs_ok; } } else if (conf && !unconf) { - /* - * RFC 3530 14.2.34 CASE 2: - * probable retransmitted request; play it safe and - * do nothing. - */ + /* case 2: probable retransmit: */ 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: - */ + /* case 3: normal case; new or rebooted client */ if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred)) { status = nfserr_clid_inuse; } else { @@ -2346,10 +2304,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, } } else if ((!conf || !same_verf(&conf->cl_confirm, &confirm)) && (!unconf || !same_verf(&unconf->cl_confirm, &confirm))) { - /* - * RFC 3530 14.2.34 CASE 4: - * Client probably hasn't noticed that we rebooted yet. - */ + /* case 4: client hasn't noticed we rebooted yet? */ status = nfserr_stale_clientid; } -- cgit v1.2.3 From ad72aae5ada1970f423ae7ee0027d0d7eb477597 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 18 May 2012 22:00:38 -0400 Subject: nfsd4: merge last two setclientid cases The code here is mostly the same. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index dd4c6d36534b..f62ab7624b1f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2212,15 +2212,10 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (new == NULL) goto out; copy_clid(new, conf); - } else if (!unconf) { - /* case 2: probable client reboot: */ - new = create_client(clname, dname, rqstp, &clverifier); - if (new == NULL) - goto out; - gen_clid(new); - } else { - /* case 3: probable client reboot: */ - expire_client(unconf); + } else { /* conf && !same_verf(): */ + /* cases 2, 3: probable client reboot: */ + if (unconf) + expire_client(unconf); new = create_client(clname, dname, rqstp, &clverifier); if (new == NULL) goto out; -- cgit v1.2.3 From 8f9307119d7fb2f2c04fce27aa492d30e7ac4518 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 18 May 2012 22:06:41 -0400 Subject: nfsd4: pull out common code from setclientid cases Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f62ab7624b1f..1fe06ebf192f 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2189,36 +2189,20 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } } unconf = find_unconfirmed_client_by_str(dname, strhashval); + if (unconf) + expire_client(unconf); status = nfserr_jukebox; + new = create_client(clname, dname, rqstp, &clverifier); + if (new == NULL) + goto out; if (!conf) { /* case 4: placed first, because it's 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)) { /* 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 { /* conf && !same_verf(): */ /* cases 2, 3: probable client reboot: */ - if (unconf) - expire_client(unconf); - new = create_client(clname, dname, rqstp, &clverifier); - if (new == NULL) - goto out; gen_clid(new); } /* -- cgit v1.2.3 From 34b232bb3742dc88eef7352da2ca018f51ac4561 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 18 May 2012 22:23:42 -0400 Subject: nfsd4: merge 3 setclientid cases to 2 Boy, is this simpler. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 1fe06ebf192f..9d7e3eb0f5db 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2195,16 +2195,11 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, new = create_client(clname, dname, rqstp, &clverifier); if (new == NULL) goto out; - if (!conf) { - /* case 4: placed first, because it's the normal case */ - gen_clid(new); - } else if (same_verf(&conf->cl_verifier, &clverifier)) { + if (conf && same_verf(&conf->cl_verifier, &clverifier)) /* case 1: probable callback update */ copy_clid(new, conf); - } else { /* conf && !same_verf(): */ - /* cases 2, 3: probable client reboot: */ + 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: -- cgit v1.2.3 From 788c1eba50133777df5ca2972cda3c9a8f78f52f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 18 May 2012 22:42:16 -0400 Subject: nfsd4: fix setclientid_confirm same_cred check New clients are created only by nfsd4_setclientid(), which always gives any new client a unique clientid. The only exception is in the "callback update" case, in which case it may create an unconfirmed client with the same clientid as a confirmed client. In that case it also checks that the confirmed client has the same credential. Therefore, it is pointless for setclientid_confirm to check whether a confirmed and unconfirmed client with the same clientid have matching credentials--they're guaranteed to. Instead, it should be checking whether the credential on the setclientid_confirm matches either of those. Otherwise, it could be anyone sending the setclientid_confirm. Granted, I can't see why anyone would, but still it's probalby safer to check. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9d7e3eb0f5db..0f108f68da77 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2243,7 +2243,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, status = nfserr_clid_inuse; if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) { /* case 1: callback update */ - if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) + if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) status = nfserr_clid_inuse; else { nfsd4_change_callback(conf, &unconf->cl_cb_conn); -- cgit v1.2.3 From 8695b90ac3c37278010c76ab3680f35ab358caf9 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 19 May 2012 10:05:58 -0400 Subject: nfsd4: fix error return in non-matching-creds case Note CLID_INUSE is for the case where two clients are trying to use the same client-provided long-form client identifiers. But what we're looking at here is the server-returned shorthand client id--if those clash there's a bug somewhere. Fix the error return, pull the check out into common code, and do the check unconditionally in all cases. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 62 ++++++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 0f108f68da77..8a1dfb20a3de 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2229,59 +2229,49 @@ 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); - + /* + * 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. + */ + 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: */ - status = nfserr_clid_inuse; if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) { /* case 1: callback update */ - if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) - status = nfserr_clid_inuse; - else { - nfsd4_change_callback(conf, &unconf->cl_cb_conn); - nfsd4_probe_callback(conf); - expire_client(unconf); - status = nfs_ok; - } + nfsd4_change_callback(conf, &unconf->cl_cb_conn); + nfsd4_probe_callback(conf); + expire_client(unconf); + status = nfs_ok; } else if (conf && !unconf) { - /* case 2: probable retransmit: */ - if (!same_creds(&conf->cl_cred, &rqstp->rq_cred)) - status = nfserr_clid_inuse; - else - status = nfs_ok; + status = nfs_ok; } else if (!conf && unconf && same_verf(&unconf->cl_confirm, &confirm)) { /* 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; + 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 || !same_verf(&conf->cl_confirm, &confirm)) && (!unconf || !same_verf(&unconf->cl_confirm, &confirm))) { /* case 4: client hasn't noticed we rebooted yet? */ status = nfserr_stale_clientid; } - +out: nfs4_unlock_state(); return status; } -- cgit v1.2.3 From f3d03b9202e4303f0e687f93ffeab87724f6dcfb Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 23 May 2012 11:38:38 -0400 Subject: nfsd4: setclientid: remove pointless assignment Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8a1dfb20a3de..e42b5670ddee 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2263,8 +2263,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, expire_client(conf); } move_to_confirmed(unconf); - conf = unconf; - nfsd4_probe_callback(conf); + nfsd4_probe_callback(unconf); status = nfs_ok; } else if ((!conf || !same_verf(&conf->cl_confirm, &confirm)) && (!unconf || !same_verf(&unconf->cl_confirm, &confirm))) { -- cgit v1.2.3 From 90d700b7792a1a7d57554620796df46246e26ce6 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Sat, 19 May 2012 13:55:22 -0400 Subject: nfsd4: simpler ordering of setclientid_confirm checks The cases here divide into two main categories: - if there's an uncomfirmed record with a matching verifier, then this is a "normal", succesful case: we're either creating a new client, or updating an existing one. - otherwise, this is a weird case: a replay, or a server reboot. Reordering to reflect that makes the code a bit more concise and the logic a lot easier to understand. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e42b5670ddee..991a8a7414f7 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -2245,18 +2245,21 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, if (conf && !same_creds(&conf->cl_cred, &rqstp->rq_cred)) goto out; /* cases below refer to rfc 3530 section 14.2.34: */ - if (conf && unconf && same_verf(&confirm, &unconf->cl_confirm)) { - /* case 1: callback update */ + 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); - status = nfs_ok; - } else if (conf && !unconf) { - status = nfs_ok; - } else if (!conf && unconf - && same_verf(&unconf->cl_confirm, &confirm)) { - /* case 3: normal case; new or rebooted client */ + } 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); @@ -2264,11 +2267,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, } move_to_confirmed(unconf); nfsd4_probe_callback(unconf); - status = nfs_ok; - } else if ((!conf || !same_verf(&conf->cl_confirm, &confirm)) - && (!unconf || !same_verf(&unconf->cl_confirm, &confirm))) { - /* case 4: client hasn't noticed we rebooted yet? */ - status = nfserr_stale_clientid; } out: nfs4_unlock_state(); -- cgit v1.2.3 From 7447758be712e891aa4c57f8fa11902ccfdff151 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 4 May 2012 14:57:52 -0400 Subject: nfsd4: clarify that renewing expired client is a bug This can't happen: - cl_time is zeroed only by unhash_client_locked, which is only ever called under both the state lock and the client lock. - every caller of renew_client() should have looked up a (non-expired) client and then called renew_client() all without dropping the state lock. - the only other caller of renew_client_locked() is release_session_client(), which first checks under the client_lock that the cl_time is nonzero. So make it clear that this is a bug, not something we handle. I can't quite bring myself to make this a BUG(), though, as there are a lot of renew_client() callers, and returning here is probably safer than a BUG(). We'll consider making it a BUG() after some more cleanup. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 991a8a7414f7..e4d9b56a770a 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1022,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); -- cgit v1.2.3 From 0f1ba0ef213193aecf9baf8b4a3919cff4907e5d Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 25 May 2012 21:24:40 -0400 Subject: nfsd4: fix exchange_id to return confirm flag Otherwise nfsd4_set_ex_flags writes over the return flags. Reported-by: Bryan Schumaker Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e4d9b56a770a..83a68983aa26 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1617,7 +1617,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto expire_client; } if (verfs_match) { /* case 2 */ - exid->flags |= EXCHGID4_FLAG_CONFIRMED_R; + conf->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R; new = conf; goto out_copy; } -- cgit v1.2.3 From 778df3f0feaeb03ebf9f370cba8437491ffa889f Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 25 May 2012 21:40:23 -0400 Subject: nfsd4: return "real" sequence id in confirmed case The client should ignore the returned sequence_id in the case where the CONFIRMED flag is set on an exchange_id reply--and in the unconfirmed case "1" is always the right response. So it shouldn't actually matter what we return here. We could continue returning 1 just to catch clients ignoring the spec here, but I'd rather be generous. Other things equal, returning the existing sequence_id seems more informative. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 83a68983aa26..e0ed84d17aa8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1650,7 +1650,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", -- cgit v1.2.3 From 32f16b3823b854a1f73bfad43782fbbcfe086090 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 29 May 2012 14:26:30 -0400 Subject: nfsd4: remove some dprintk's and a comment The comment is redundant, and if we really want dprintk's here they'd probably be better in the common (check-slot_seqid) code. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e0ed84d17aa8..8d10760800ad 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1750,16 +1750,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) { @@ -1768,7 +1762,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) { @@ -1776,7 +1769,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_seq_misordered; goto out; } - confirm_me = true; conf = unconf; } else { -- cgit v1.2.3 From b9831b59f31eca65e1beec01fadf02ca82485813 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 29 May 2012 14:44:28 -0400 Subject: nfsd4: don't remove rebooted client record until confirmation In the NFSv4.1 client-reboot case we're currently removing the client's previous state in exchange_id. That's wrong--we should be waiting till the confirming create_session. Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8d10760800ad..c91e26d84f06 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1614,7 +1614,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, status = nfserr_clid_inuse; goto out; } - goto expire_client; + expire_client(conf); + goto out_new; } if (verfs_match) { /* case 2 */ conf->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R; @@ -1622,8 +1623,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out_copy; } /* case 5, client reboot */ -expire_client: - expire_client(conf); goto out_new; } @@ -1805,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)); -- cgit v1.2.3 From 6eccece90b6addf80ef9e6db79b0bc873301034b Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Tue, 29 May 2012 16:37:44 -0400 Subject: nfsd4: fix, consolidate client_has_state Whoops: first, I reimplemented the already-existing has_resources without noticing; second, I got the test backwards. I did pick a better name, though. Combine the two.... Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c91e26d84f06..0a6901d45349 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1537,9 +1537,9 @@ static bool client_has_state(struct nfs4_client *clp) * * 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); + return !list_empty(&clp->cl_openowners) + || !list_empty(&clp->cl_delegations) + || !list_empty(&clp->cl_sessions); } __be32 @@ -2069,13 +2069,6 @@ 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) { @@ -2089,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; } -- cgit v1.2.3