Implement the exchange_id operation confoming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Based on the client provided name, hash a client id. If a confirmed one is found, compare the op's creds and verifier. If the creds match and the verifier is different then expire the old client (client re-incarnated), otherwise, if both match, assume it's a replay and ignore it. If an unconfirmed client is found, then copy the new creds and verifer if need update, otherwise assume replay. The client is moved to a confirmed state on create_session. In the nfs41 branch set the exchange_id flags to EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_SUPP_MOVED_REFER (pNFS is not supported, Referrals are supported, Migration is not.). Signed-off-by: Benny Halevy <bhalevy@xxxxxxxxxxx> --- fs/nfsd/nfs4state.c | 113 ++++++++++++++++++++++++++++++++++- fs/nfsd/nfs4xdr.c | 143 +++++++++++++++++++++++++++++++++++++++++++- include/linux/nfsd/state.h | 4 + include/linux/nfsd/xdr4.h | 7 ++- 4 files changed, 263 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 5c96c39..76b2870 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -781,12 +781,123 @@ out_err: } #if defined(CONFIG_NFSD_V4_1) +/* + * Set the exchange_id flags returned by the server. + */ +static void +nfsd4_set_ex_flags(struct nfs4_client *new, struct nfsd4_exchange_id *clid) +{ + /* pNFS is not supported */ + new->cl_exchange_flags |= EXCHGID4_FLAG_USE_NON_PNFS; + + /* Referrals are supported, Migration is not. */ + new->cl_exchange_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER; + + /* set the wire flags to return to client. */ + clid->flags = new->cl_exchange_flags; +} + __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_exchange_id *clid) { - return -1; /* stub */ + struct nfs4_client *unconf, *conf, *new; + int status; + unsigned int strhashval; + char dname[HEXDIR_LEN]; + nfs4_verifier verf = clid->verifier; + u32 ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr; + struct xdr_netobj clname = { + .len = clid->id_len, + .data = clid->id, + }; + + dprintk("%s rqstp=%p clid=%p clname.len=%u clname.data=%p " + " ip_addr=%u flags %x\n", + __func__, rqstp, clid, clname.len, clname.data, + ip_addr, clid->flags); + + if (!check_name(clname) || (clid->flags & EXCHGID4_INVAL_FLAG_MASK)) + return nfserr_inval; + + status = nfs4_make_rec_clidname(dname, &clname); + + if (status) + goto error; + + strhashval = clientstr_hashval(dname); + + nfs4_lock_state(); + status = nfserr_clid_inuse; + + conf = find_confirmed_client_by_str(dname, strhashval); + if (conf) { + if (!same_creds(&conf->cl_cred, &rqstp->rq_cred) || + (ip_addr != conf->cl_addr)) { + /* Client collision: send nfserr_clid_inuse */ + goto out; + } + + if (!same_verf(&verf, &conf->cl_verifier)) { + /* Client reboot: destroy old state */ + expire_client(conf); + goto out_new; + } + /* router replay */ + goto out; + } + + unconf = find_unconfirmed_client_by_str(dname, strhashval); + if (unconf) { + status = nfs_ok; + /* Found an unconfirmed record */ + if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred)) { + /* Principal changed: update to the new principal + * and send nfs_ok */ + copy_cred(&unconf->cl_cred, &rqstp->rq_cred); + } + + if (!same_verf(&unconf->cl_verifier, &verf)) { + /* Reboot before confirmation: update the verifier and + * send nfs_ok */ + copy_verf(unconf, &verf); + new = unconf; + goto out_copy; + } + goto out; + } + +out_new: + /* Normal case */ + status = nfserr_resource; + new = create_client(clname, dname); + + if (new == NULL) + goto out; + + copy_verf(new, &verf); + copy_cred(&new->cl_cred, &rqstp->rq_cred); + new->cl_addr = ip_addr; + gen_clid(new); + gen_confirm(new); + add_to_unconfirmed(new, strhashval); +out_copy: + clid->clientid.cl_boot = new->cl_clientid.cl_boot; + clid->clientid.cl_id = new->cl_clientid.cl_id; + + new->cl_seqid = clid->seqid = 1; + nfsd4_set_ex_flags(new, clid); + + dprintk("nfsd4_exchange_id seqid %d flags %x\n", + new->cl_seqid, new->cl_exchange_flags); + status = nfs_ok; + +out: + nfs4_unlock_state(); +error: + dprintk("nfsd4_exchange_id returns %d\n", ntohl(status)); + return status; } __be32 diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index fc613cd..055f1ad 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1021,7 +1021,98 @@ static __be32 nfsd4_decode_exchange_id(struct nfsd4_compoundargs *argp, struct nfsd4_exchange_id *clid) { - return nfserr_opnotsupp; /* stub */ + int dummy; + DECODE_HEAD; + + READ_BUF(NFS4_VERIFIER_SIZE); + COPYMEM(clid->verifier.data, NFS4_VERIFIER_SIZE); + + READ_BUF(4); + READ32(clid->id_len); + + READ_BUF(clid->id_len); + SAVEMEM(clid->id, clid->id_len); + + READ_BUF(4); + READ32(clid->flags); + + /* Ignore state_protect4_a */ + READ_BUF(4); + READ32(dummy); + switch (dummy) { + case SP4_NONE: + break; + case SP4_MACH_CRED: + /* spo_must_enforce */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy * 4); + p += dummy; + + /* spo_must_allow */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy * 4); + p += dummy; + break; + case SP4_SSV: + /* ssp_ops */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy * 4); + p += dummy; + + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy * 4); + p += dummy; + + /* ssp_hash_algs<> */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + + /* ssp_encr_algs<> */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + + /* ssp_window and ssp_num_gss_handles */ + READ_BUF(8); + READ32(dummy); + READ32(dummy); + break; + default: + goto xdr_error; + } + + /* Ignore Implementation ID */ + READ_BUF(4); /* nfs_impl_id4 array length */ + READ32(dummy); + + if (dummy > 1) + goto xdr_error; + + if (dummy == 1) { + /* nii_domain */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + + /* nii_name */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + + /* nii_date */ + READ_BUF(12); + p += 3; + } + DECODE_TAIL; } static __be32 @@ -2692,7 +2783,55 @@ static __be32 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_exchange_id *exid) { - /* stub */ + ENCODE_HEAD; + char *major_id = "fixme_please"; + char *server_scope = "fixme_please"; + int major_id_sz; + int server_scope_sz; + uint64_t minor_id = 0; + + if (nfserr) + goto out; + + /* XXX FIXME We currently use ia dummy as the major id. Need to change + * this to something more meaningful... + */ + major_id_sz = strlen(major_id); + server_scope_sz = strlen(server_scope); + + RESERVE_SPACE( + 8 /* eir_clientid */ + + 4 /* eir_sequenceid */ + + 4 /* eir_flags */ + + 4 /* spr_how (SP4_NONE) */ + + 8 /* so_minor_id */ + + 4 /* so_major_id.len */ + + (XDR_QUADLEN(major_id_sz) * 4) + + 4 /* eir_server_scope.len */ + + (XDR_QUADLEN(server_scope_sz) * 4) + + 4 /* eir_server_impl_id.count (0) */); + + WRITEMEM(&exid->clientid, 8); + WRITE32(exid->seqid); + WRITE32(exid->flags); + + /* state_protect4_r */ + WRITE32(SP4_NONE); + + /* The server_owner struct */ + WRITE64(minor_id); /* Minor id */ + /* major id */ + WRITE32(major_id_sz); + WRITEMEM(major_id, major_id_sz); + + /* Server scope */ + WRITE32(server_scope_sz); + WRITEMEM(server_scope, server_scope_sz); + + /* Implementation id */ + WRITE32(0); /* zero length nfs_impl_id4 array */ + ADJUST_ARGS(); +out: return nfserr; } diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 5cb4142..9bbfd88 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -214,6 +214,10 @@ struct nfs4_client { struct nfs4_callback cl_callback; /* callback info */ atomic_t cl_count; /* ref count */ u32 cl_firststate; /* recovery dir creation */ +#if defined(CONFIG_NFSD_V4_1) + u32 cl_seqid; /* seqid for create_session */ + u32 cl_exchange_flags; +#endif /* CONFIG_NFSD_V4_1 */ }; /* struct nfs4_client_reset diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index d7dbdab..aee8996 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -354,7 +354,12 @@ struct nfsd4_write { #if defined(CONFIG_NFSD_V4_1) struct nfsd4_exchange_id { - int foo; /* stub */ + nfs4_verifier verifier; + u32 id_len; + char *id; + u32 flags; + clientid_t clientid; + u32 seqid; }; struct nfsd4_create_session { -- 1.6.0.2 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html