From 50867d802b534350d2bee81cf0858b8cd871b487 Mon Sep 17 00:00:00 2001 From: rmacklem Date: Fri, 1 Aug 2014 21:10:41 +0000 Subject: MFC: r268115 Merge the NFSv4.1 server code in projects/nfsv4.1-server over into head. The code is not believed to have any effect on the semantics of non-NFSv4.1 server behaviour. It is a rather large merge, but I am hoping that there will not be any regressions for the NFS server. --- sys/conf/files | 1 + sys/fs/nfs/nfs.h | 144 ++++-- sys/fs/nfs/nfs_commonkrpc.c | 29 +- sys/fs/nfs/nfs_commonport.c | 1 + sys/fs/nfs/nfs_commonsubs.c | 110 +++-- sys/fs/nfs/nfs_var.h | 42 +- sys/fs/nfs/nfsclstate.h | 1 + sys/fs/nfs/nfsdport.h | 6 + sys/fs/nfs/nfsport.h | 5 + sys/fs/nfs/nfsproto.h | 123 ++++- sys/fs/nfs/nfsrvstate.h | 47 ++ sys/fs/nfsclient/nfs_clstate.c | 2 +- sys/fs/nfsserver/nfs_nfsdcache.c | 3 + sys/fs/nfsserver/nfs_nfsdkrpc.c | 51 ++- sys/fs/nfsserver/nfs_nfsdport.c | 21 + sys/fs/nfsserver/nfs_nfsdserv.c | 666 ++++++++++++++++++++++++--- sys/fs/nfsserver/nfs_nfsdsocket.c | 144 ++++-- sys/fs/nfsserver/nfs_nfsdstate.c | 913 ++++++++++++++++++++++++++++++++------ sys/fs/nfsserver/nfs_nfsdsubs.c | 84 +++- sys/modules/krpc/Makefile | 1 + sys/rpc/clnt_bck.c | 593 +++++++++++++++++++++++++ sys/rpc/krpc.h | 4 + sys/rpc/svc.h | 8 + sys/rpc/svc_vc.c | 28 +- 24 files changed, 2694 insertions(+), 333 deletions(-) create mode 100644 sys/rpc/clnt_bck.c diff --git a/sys/conf/files b/sys/conf/files index 61a7c80..f04bc1d 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3838,6 +3838,7 @@ pci/viapm.c optional viapm pci rpc/auth_none.c optional krpc | nfslockd | nfsclient | nfsserver | nfscl | nfsd rpc/auth_unix.c optional krpc | nfslockd | nfsclient | nfscl | nfsd rpc/authunix_prot.c optional krpc | nfslockd | nfsclient | nfsserver | nfscl | nfsd +rpc/clnt_bck.c optional krpc | nfslockd | nfsserver | nfscl | nfsd rpc/clnt_dg.c optional krpc | nfslockd | nfsclient | nfscl | nfsd rpc/clnt_rc.c optional krpc | nfslockd | nfsclient | nfscl | nfsd rpc/clnt_vc.c optional krpc | nfslockd | nfsclient | nfsserver | nfscl | nfsd diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h index 5976934..6106de9 100644 --- a/sys/fs/nfs/nfs.h +++ b/sys/fs/nfs/nfs.h @@ -50,7 +50,8 @@ #define NFS_MAXREXMIT 100 /* Stop counting after this many */ #define NFSV4_CALLBACKTIMEO (2 * NFS_HZ) /* Timeout in ticks */ #define NFSV4_CALLBACKRETRY 5 /* Number of retries before failure */ -#define NFSV4_CBSLOTS 8 /* Number of slots for session */ +#define NFSV4_SLOTS 64 /* Number of slots, fore channel */ +#define NFSV4_CBSLOTS 8 /* Number of slots, back channel */ #define NFSV4_CBRETRYCNT 4 /* # of CBRecall retries upon err */ #define NFSV4_UPCALLTIMEO (15 * NFS_HZ) /* Timeout in ticks for upcalls */ /* to gssd or nfsuserd */ @@ -91,6 +92,9 @@ #ifndef NFSLOCKHASHSIZE #define NFSLOCKHASHSIZE 20 /* Size of server nfslock hash table */ #endif +#ifndef NFSSESSIONHASHSIZE +#define NFSSESSIONHASHSIZE 20 /* Size of server session hash table */ +#endif #define NFSSTATEHASHSIZE 10 /* Size of server stateid hash table */ #ifndef NFSUSERHASHSIZE #define NFSUSERHASHSIZE 30 /* Size of user id hash table */ @@ -276,6 +280,8 @@ struct nfsreferral { #define LCL_GSSINTEGRITY 0x00002000 #define LCL_GSSPRIVACY 0x00004000 #define LCL_ADMINREVOKED 0x00008000 +#define LCL_RECLAIMCOMPLETE 0x00010000 +#define LCL_NFSV41 0x00020000 #define LCL_GSS LCL_KERBV /* Or of all mechs */ @@ -318,6 +324,11 @@ struct nfsreferral { #define NFSLCK_SETATTR 0x02000000 #define NFSLCK_DELEGPURGE 0x04000000 #define NFSLCK_DELEGRETURN 0x08000000 +#define NFSLCK_WANTWDELEG 0x10000000 +#define NFSLCK_WANTRDELEG 0x20000000 +#define NFSLCK_WANTNODELEG 0x40000000 +#define NFSLCK_WANTBITS \ + (NFSLCK_WANTWDELEG | NFSLCK_WANTRDELEG | NFSLCK_WANTNODELEG) /* And bits for nid_flag */ #define NFSID_INITIALIZE 0x0001 @@ -341,68 +352,120 @@ struct nfsreferral { * THE MACROS MUST BE MANUALLY MODIFIED IF NFSATTRBIT_MAXWORDS CHANGES!! * It is (NFSATTRBIT_MAX + 31) / 32. */ -#define NFSATTRBIT_MAXWORDS 2 +#define NFSATTRBIT_MAXWORDS 3 typedef struct { u_int32_t bits[NFSATTRBIT_MAXWORDS]; } nfsattrbit_t; -#define NFSZERO_ATTRBIT(b) do { (b)->bits[0] = 0; (b)->bits[1] = 0; } while (0) -#define NFSSET_ATTRBIT(t, f) do { (t)->bits[0] = (f)->bits[0]; \ - (t)->bits[1] = (f)->bits[1]; } while (0) +#define NFSZERO_ATTRBIT(b) do { \ + (b)->bits[0] = 0; \ + (b)->bits[1] = 0; \ + (b)->bits[2] = 0; \ +} while (0) + +#define NFSSET_ATTRBIT(t, f) do { \ + (t)->bits[0] = (f)->bits[0]; \ + (t)->bits[1] = (f)->bits[1]; \ + (t)->bits[2] = (f)->bits[2]; \ +} while (0) + #define NFSSETSUPP_ATTRBIT(b) do { \ (b)->bits[0] = NFSATTRBIT_SUPP0; \ - (b)->bits[1] = (NFSATTRBIT_SUPP1 | NFSATTRBIT_SUPPSETONLY); } while (0) + (b)->bits[1] = (NFSATTRBIT_SUPP1 | NFSATTRBIT_SUPPSETONLY); \ + (b)->bits[2] = NFSATTRBIT_SUPP2; \ +} while (0) + #define NFSISSET_ATTRBIT(b, p) ((b)->bits[(p) / 32] & (1 << ((p) % 32))) #define NFSSETBIT_ATTRBIT(b, p) ((b)->bits[(p) / 32] |= (1 << ((p) % 32))) #define NFSCLRBIT_ATTRBIT(b, p) ((b)->bits[(p) / 32] &= ~(1 << ((p) % 32))) + #define NFSCLRALL_ATTRBIT(b, a) do { \ - (b)->bits[0] &= ~((a)->bits[0]); \ - (b)->bits[1] &= ~((a)->bits[1]); \ - } while (0) + (b)->bits[0] &= ~((a)->bits[0]); \ + (b)->bits[1] &= ~((a)->bits[1]); \ + (b)->bits[2] &= ~((a)->bits[2]); \ +} while (0) + #define NFSCLRNOT_ATTRBIT(b, a) do { \ - (b)->bits[0] &= ((a)->bits[0]); \ - (b)->bits[1] &= ((a)->bits[1]); \ - } while (0) + (b)->bits[0] &= ((a)->bits[0]); \ + (b)->bits[1] &= ((a)->bits[1]); \ + (b)->bits[2] &= ((a)->bits[2]); \ +} while (0) + #define NFSCLRNOTFILLABLE_ATTRBIT(b) do { \ - (b)->bits[0] &= NFSATTRBIT_SUPP0; \ - (b)->bits[1] &= NFSATTRBIT_SUPP1; } while (0) + (b)->bits[0] &= NFSATTRBIT_SUPP0; \ + (b)->bits[1] &= NFSATTRBIT_SUPP1; \ + (b)->bits[2] &= NFSATTRBIT_SUPP2; \ +} while (0) + #define NFSCLRNOTSETABLE_ATTRBIT(b) do { \ - (b)->bits[0] &= NFSATTRBIT_SETABLE0; \ - (b)->bits[1] &= NFSATTRBIT_SETABLE1; } while (0) -#define NFSNONZERO_ATTRBIT(b) ((b)->bits[0] || (b)->bits[1]) -#define NFSEQUAL_ATTRBIT(b, p) \ - ((b)->bits[0] == (p)->bits[0] && (b)->bits[1] == (p)->bits[1]) + (b)->bits[0] &= NFSATTRBIT_SETABLE0; \ + (b)->bits[1] &= NFSATTRBIT_SETABLE1; \ + (b)->bits[2] &= NFSATTRBIT_SETABLE2; \ +} while (0) + +#define NFSNONZERO_ATTRBIT(b) ((b)->bits[0] || (b)->bits[1] || (b)->bits[2]) +#define NFSEQUAL_ATTRBIT(b, p) ((b)->bits[0] == (p)->bits[0] && \ + (b)->bits[1] == (p)->bits[1] && (b)->bits[2] == (p)->bits[2]) + #define NFSGETATTR_ATTRBIT(b) do { \ - (b)->bits[0] = NFSATTRBIT_GETATTR0; \ - (b)->bits[1] = NFSATTRBIT_GETATTR1; } while (0) + (b)->bits[0] = NFSATTRBIT_GETATTR0; \ + (b)->bits[1] = NFSATTRBIT_GETATTR1; \ + (b)->bits[2] = NFSATTRBIT_GETATTR2; \ +} while (0) + #define NFSWCCATTR_ATTRBIT(b) do { \ - (b)->bits[0] = NFSATTRBIT_WCCATTR0; \ - (b)->bits[1] = NFSATTRBIT_WCCATTR1; } while (0) + (b)->bits[0] = NFSATTRBIT_WCCATTR0; \ + (b)->bits[1] = NFSATTRBIT_WCCATTR1; \ + (b)->bits[2] = NFSATTRBIT_WCCATTR2; \ +} while (0) + #define NFSWRITEGETATTR_ATTRBIT(b) do { \ - (b)->bits[0] = NFSATTRBIT_WRITEGETATTR0; \ - (b)->bits[1] = NFSATTRBIT_WRITEGETATTR1; } while (0) + (b)->bits[0] = NFSATTRBIT_WRITEGETATTR0; \ + (b)->bits[1] = NFSATTRBIT_WRITEGETATTR1; \ + (b)->bits[2] = NFSATTRBIT_WRITEGETATTR2; \ +} while (0) + #define NFSCBGETATTR_ATTRBIT(b, c) do { \ - (c)->bits[0] = ((b)->bits[0] & NFSATTRBIT_CBGETATTR0); \ - (c)->bits[1] = ((b)->bits[1] & NFSATTRBIT_CBGETATTR1); } while (0) + (c)->bits[0] = ((b)->bits[0] & NFSATTRBIT_CBGETATTR0); \ + (c)->bits[1] = ((b)->bits[1] & NFSATTRBIT_CBGETATTR1); \ + (c)->bits[2] = ((b)->bits[2] & NFSATTRBIT_CBGETATTR2); \ +} while (0) + #define NFSPATHCONF_GETATTRBIT(b) do { \ - (b)->bits[0] = NFSGETATTRBIT_PATHCONF0; \ - (b)->bits[1] = NFSGETATTRBIT_PATHCONF1; } while (0) + (b)->bits[0] = NFSGETATTRBIT_PATHCONF0; \ + (b)->bits[1] = NFSGETATTRBIT_PATHCONF1; \ + (b)->bits[2] = NFSGETATTRBIT_PATHCONF2; \ +} while (0) + #define NFSSTATFS_GETATTRBIT(b) do { \ - (b)->bits[0] = NFSGETATTRBIT_STATFS0; \ - (b)->bits[1] = NFSGETATTRBIT_STATFS1; } while (0) + (b)->bits[0] = NFSGETATTRBIT_STATFS0; \ + (b)->bits[1] = NFSGETATTRBIT_STATFS1; \ + (b)->bits[2] = NFSGETATTRBIT_STATFS2; \ +} while (0) + #define NFSISSETSTATFS_ATTRBIT(b) \ (((b)->bits[0] & NFSATTRBIT_STATFS0) || \ - ((b)->bits[1] & NFSATTRBIT_STATFS1)) + ((b)->bits[1] & NFSATTRBIT_STATFS1) || \ + ((b)->bits[2] & NFSATTRBIT_STATFS2)) + #define NFSCLRSTATFS_ATTRBIT(b) do { \ - (b)->bits[0] &= ~NFSATTRBIT_STATFS0; \ - (b)->bits[1] &= ~NFSATTRBIT_STATFS1; } while (0) + (b)->bits[0] &= ~NFSATTRBIT_STATFS0; \ + (b)->bits[1] &= ~NFSATTRBIT_STATFS1; \ + (b)->bits[2] &= ~NFSATTRBIT_STATFS2; \ +} while (0) + #define NFSREADDIRPLUS_ATTRBIT(b) do { \ - (b)->bits[0] = NFSATTRBIT_READDIRPLUS0; \ - (b)->bits[1] = NFSATTRBIT_READDIRPLUS1; } while (0) + (b)->bits[0] = NFSATTRBIT_READDIRPLUS0; \ + (b)->bits[1] = NFSATTRBIT_READDIRPLUS1; \ + (b)->bits[2] = NFSATTRBIT_READDIRPLUS2; \ +} while (0) + #define NFSREFERRAL_ATTRBIT(b) do { \ - (b)->bits[0] = NFSATTRBIT_REFERRAL0; \ - (b)->bits[1] = NFSATTRBIT_REFERRAL1; } while (0) + (b)->bits[0] = NFSATTRBIT_REFERRAL0; \ + (b)->bits[1] = NFSATTRBIT_REFERRAL1; \ + (b)->bits[2] = NFSATTRBIT_REFERRAL2; \ +} while (0) /* * Store uid, gid creds that were used when the stateid was acquired. @@ -529,6 +592,9 @@ struct nfsrv_descript { int nd_gssnamelen; /* principal name length */ char *nd_gssname; /* principal name */ uint32_t *nd_slotseq; /* ptr to slot seq# in req */ + uint8_t nd_sessionid[NFSX_V4SESSIONID]; /* Session id */ + uint32_t nd_slotid; /* Slotid for this RPC */ + SVCXPRT *nd_xprt; /* Server RPC handle */ }; #define nd_princlen nd_gssnamelen @@ -562,6 +628,8 @@ struct nfsrv_descript { #define ND_NFSCL 0x01000000 #define ND_NFSV41 0x02000000 #define ND_HASSEQUENCE 0x04000000 +#define ND_CACHETHIS 0x08000000 +#define ND_LASTOP 0x10000000 /* * ND_GSS should be the "or" of all GSS type authentications. diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c index bdba851..1c0189f 100644 --- a/sys/fs/nfs/nfs_commonkrpc.c +++ b/sys/fs/nfs/nfs_commonkrpc.c @@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include @@ -739,8 +740,12 @@ tryagain: } nd->nd_mrep = NULL; - stat = CLNT_CALL_MBUF(nrp->nr_client, &ext, procnum, nd->nd_mreq, - &nd->nd_mrep, timo); + if (clp != NULL && sep != NULL) + stat = clnt_bck_call(nrp->nr_client, &ext, procnum, + nd->nd_mreq, &nd->nd_mrep, timo, sep->nfsess_xprt); + else + stat = CLNT_CALL_MBUF(nrp->nr_client, &ext, procnum, + nd->nd_mreq, &nd->nd_mrep, timo); if (rep != NULL) { /* @@ -795,7 +800,8 @@ tryagain: nd->nd_md = nd->nd_mrep; nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t); nd->nd_repstat = 0; - if (nd->nd_procnum != NFSPROC_NULL) { + if (nd->nd_procnum != NFSPROC_NULL && + nd->nd_procnum != NFSV4PROC_CBNULL) { /* If sep == NULL, set it to the default in nmp. */ if (sep == NULL && nmp != NULL) sep = NFSMNT_MDSSESSION(nmp); @@ -827,11 +833,20 @@ tryagain: /* * If the first op is Sequence, free up the slot. */ - if (nmp != NULL && i == NFSV4OP_SEQUENCE && j != 0) + if ((nmp != NULL && i == NFSV4OP_SEQUENCE && j != 0) || + (clp != NULL && i == NFSV4OP_CBSEQUENCE && j != 0)) NFSCL_DEBUG(1, "failed seq=%d\n", j); - if (nmp != NULL && i == NFSV4OP_SEQUENCE && j == 0) { - NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID + - 5 * NFSX_UNSIGNED); + if ((nmp != NULL && i == NFSV4OP_SEQUENCE && j == 0) || + (clp != NULL && i == NFSV4OP_CBSEQUENCE && j == 0) + ) { + if (i == NFSV4OP_SEQUENCE) + NFSM_DISSECT(tl, uint32_t *, + NFSX_V4SESSIONID + + 5 * NFSX_UNSIGNED); + else + NFSM_DISSECT(tl, uint32_t *, + NFSX_V4SESSIONID + + 4 * NFSX_UNSIGNED); mtx_lock(&sep->nfsess_mtx); tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; retseq = fxdr_unsigned(uint32_t, *tl++); diff --git a/sys/fs/nfs/nfs_commonport.c b/sys/fs/nfs/nfs_commonport.c index 602cf23..27aca26 100644 --- a/sys/fs/nfs/nfs_commonport.c +++ b/sys/fs/nfs/nfs_commonport.c @@ -112,6 +112,7 @@ MALLOC_DEFINE(M_NEWNFSDEVINFO, "NFSCL devinfo", "NFSv4.1 Device Info"); MALLOC_DEFINE(M_NEWNFSSOCKREQ, "NFSCL sockreq", "NFS Sock Req"); MALLOC_DEFINE(M_NEWNFSCLDS, "NFSCL session", "NFSv4.1 Session"); MALLOC_DEFINE(M_NEWNFSLAYRECALL, "NFSCL layrecall", "NFSv4.1 Layout Recall"); +MALLOC_DEFINE(M_NEWNFSDSESSION, "NFSD session", "NFSD Session for a client"); /* * Definition of mutex locks. diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c index 5c50dfd..b94091c 100644 --- a/sys/fs/nfs/nfs_commonsubs.c +++ b/sys/fs/nfs/nfs_commonsubs.c @@ -1733,6 +1733,23 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp, } attrsum += NFSX_HYPER; break; + case NFSATTRBIT_SUPPATTREXCLCREAT: + retnotsup = 0; + error = nfsrv_getattrbits(nd, &retattrbits, + &cnt, &retnotsup); + if (error) + goto nfsmout; + if (compare && !(*retcmpp)) { + NFSSETSUPP_ATTRBIT(&checkattrbits); + NFSCLRNOTSETABLE_ATTRBIT(&checkattrbits); + NFSCLRBIT_ATTRBIT(&checkattrbits, + NFSATTRBIT_TIMEACCESSSET); + if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits) + || retnotsup) + *retcmpp = NFSERR_NOTSAME; + } + attrsum += cnt; + break; default: printf("EEK! nfsv4_loadattr unknown attr=%d\n", bitpos); @@ -2469,6 +2486,12 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp, txdr_hyper(uquad, tl); retnum += NFSX_HYPER; break; + case NFSATTRBIT_SUPPATTREXCLCREAT: + NFSSETSUPP_ATTRBIT(&attrbits); + NFSCLRNOTSETABLE_ATTRBIT(&attrbits); + NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET); + retnum += nfsrv_putattrbit(nd, &attrbits); + break; default: printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos); }; @@ -3663,6 +3686,9 @@ nfsmout: /* * Handle an NFSv4.1 Sequence request for the session. + * If reply != NULL, use it to return the cached reply, as required. + * The client gets a cached reply via this call for callbacks, however the + * server gets a cached reply via the nfsv4_seqsess_cachereply() call. */ int nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot, @@ -3671,7 +3697,8 @@ nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot, int error; error = 0; - *reply = NULL; + if (reply != NULL) + *reply = NULL; if (slotid > maxslot) return (NFSERR_BADSLOT); if (seqid == slots[slotid].nfssl_seq) { @@ -3679,13 +3706,18 @@ nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot, if (slots[slotid].nfssl_inprog != 0) error = NFSERR_DELAY; else if (slots[slotid].nfssl_reply != NULL) { - *reply = slots[slotid].nfssl_reply; - slots[slotid].nfssl_reply = NULL; + if (reply != NULL) { + *reply = slots[slotid].nfssl_reply; + slots[slotid].nfssl_reply = NULL; + } slots[slotid].nfssl_inprog = 1; + error = NFSERR_REPLYFROMCACHE; } else - error = NFSERR_SEQMISORDERED; + /* No reply cached, so just do it. */ + slots[slotid].nfssl_inprog = 1; } else if ((slots[slotid].nfssl_seq + 1) == seqid) { - m_freem(slots[slotid].nfssl_reply); + if (slots[slotid].nfssl_reply != NULL) + m_freem(slots[slotid].nfssl_reply); slots[slotid].nfssl_reply = NULL; slots[slotid].nfssl_inprog = 1; slots[slotid].nfssl_seq++; @@ -3696,12 +3728,22 @@ nfsv4_seqsession(uint32_t seqid, uint32_t slotid, uint32_t highslot, /* * Cache this reply for the slot. + * Use the "rep" argument to return the cached reply if repstat is set to + * NFSERR_REPLYFROMCACHE. The client never sets repstat to this value. */ void -nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, struct mbuf *rep) +nfsv4_seqsess_cacherep(uint32_t slotid, struct nfsslot *slots, int repstat, + struct mbuf **rep) { - slots[slotid].nfssl_reply = rep; + if (repstat == NFSERR_REPLYFROMCACHE) { + *rep = slots[slotid].nfssl_reply; + slots[slotid].nfssl_reply = NULL; + } else { + if (slots[slotid].nfssl_reply != NULL) + m_freem(slots[slotid].nfssl_reply); + slots[slotid].nfssl_reply = *rep; + } slots[slotid].nfssl_inprog = 0; } @@ -3713,9 +3755,36 @@ nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd, struct nfsclsession *sep, int dont_replycache) { uint32_t *tl, slotseq = 0; + int error, maxslot, slotpos; + uint8_t sessionid[NFSX_V4SESSIONID]; + + error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq, + sessionid); + if (error != 0) + return; + KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot")); + + /* Build the Sequence arguments. */ + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED); + bcopy(sessionid, tl, NFSX_V4SESSIONID); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + nd->nd_slotseq = tl; + *tl++ = txdr_unsigned(slotseq); + *tl++ = txdr_unsigned(slotpos); + *tl++ = txdr_unsigned(maxslot); + if (dont_replycache == 0) + *tl = newnfs_true; + else + *tl = newnfs_false; + nd->nd_flag |= ND_HASSEQUENCE; +} + +int +nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep, + int *slotposp, int *maxslotp, uint32_t *slotseqp, uint8_t *sessionid) +{ int i, maxslot, slotpos; uint64_t bitval; - uint8_t sessionid[NFSX_V4SESSIONID]; /* Find an unused slot. */ slotpos = -1; @@ -3728,7 +3797,7 @@ nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd, slotpos = i; sep->nfsess_slots |= bitval; sep->nfsess_slotseq[i]++; - slotseq = sep->nfsess_slotseq[i]; + *slotseqp = sep->nfsess_slotseq[i]; break; } bitval <<= 1; @@ -3739,10 +3808,11 @@ nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd, * This RPC attempt will fail when it calls * newnfs_request(). */ - if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) + if (nmp != NULL && + (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) { mtx_unlock(&sep->nfsess_mtx); - return; + return (ESTALE); } /* Wake up once/sec, to check for a forced dismount. */ (void)mtx_sleep(&sep->nfsess_slots, &sep->nfsess_mtx, @@ -3758,21 +3828,9 @@ nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd, } bcopy(sep->nfsess_sessionid, sessionid, NFSX_V4SESSIONID); mtx_unlock(&sep->nfsess_mtx); - KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot")); - - /* Build the Sequence arguments. */ - NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED); - bcopy(sessionid, tl, NFSX_V4SESSIONID); - tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; - nd->nd_slotseq = tl; - *tl++ = txdr_unsigned(slotseq); - *tl++ = txdr_unsigned(slotpos); - *tl++ = txdr_unsigned(maxslot); - if (dont_replycache == 0) - *tl = newnfs_true; - else - *tl = newnfs_false; - nd->nd_flag |= ND_HASSEQUENCE; + *slotposp = slotpos; + *maxslotp = maxslot; + return (0); } /* diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index 895829d..2abd7e4 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -61,6 +61,7 @@ union nethostaddr; struct nfsstate; struct nfslock; struct nfsclient; +struct nfsdsession; struct nfslockconflict; struct nfsd_idargs; struct nfsd_clid; @@ -90,8 +91,11 @@ NFS_READDIR_ARGS; /* nfs_nfsdstate.c */ int nfsrv_setclient(struct nfsrv_descript *, struct nfsclient **, nfsquad_t *, nfsquad_t *, NFSPROC_T *); -int nfsrv_getclient(nfsquad_t, int, struct nfsclient **, nfsquad_t, - struct nfsrv_descript *, NFSPROC_T *); +int nfsrv_getclient(nfsquad_t, int, struct nfsclient **, struct nfsdsession *, + nfsquad_t, uint32_t, struct nfsrv_descript *, NFSPROC_T *); +int nfsrv_destroyclient(nfsquad_t, NFSPROC_T *); +int nfsrv_destroysession(struct nfsrv_descript *, uint8_t *); +int nfsrv_freestateid(struct nfsrv_descript *, nfsv4stateid_t *, NFSPROC_T *); int nfsrv_adminrevoke(struct nfsd_clid *, NFSPROC_T *); void nfsrv_dumpclients(struct nfsd_dumpclients *, int); void nfsrv_dumplocks(vnode_t, struct nfsd_dumplocks *, int, NFSPROC_T *); @@ -105,8 +109,8 @@ int nfsrv_opencheck(nfsquad_t, nfsv4stateid_t *, struct nfsstate *, vnode_t, struct nfsrv_descript *, NFSPROC_T *, int); int nfsrv_openupdate(vnode_t, struct nfsstate *, nfsquad_t, nfsv4stateid_t *, struct nfsrv_descript *, NFSPROC_T *); -int nfsrv_delegupdate(nfsquad_t, nfsv4stateid_t *, vnode_t, int, - struct ucred *, NFSPROC_T *); +int nfsrv_delegupdate(struct nfsrv_descript *, nfsquad_t, nfsv4stateid_t *, + vnode_t, int, struct ucred *, NFSPROC_T *); int nfsrv_releaselckown(struct nfsstate *, nfsquad_t, NFSPROC_T *); void nfsrv_zapclient(struct nfsclient *, NFSPROC_T *); int nfssvc_idname(struct nfsd_idargs *); @@ -127,6 +131,10 @@ int nfsrv_checkgetattr(struct nfsrv_descript *, vnode_t, int nfsrv_nfsuserdport(u_short, NFSPROC_T *); void nfsrv_nfsuserddelport(void); void nfsrv_throwawayallstate(NFSPROC_T *); +int nfsrv_checksequence(struct nfsrv_descript *, uint32_t, uint32_t *, + uint32_t *, int, uint32_t *, NFSPROC_T *); +int nfsrv_checkreclaimcomplete(struct nfsrv_descript *); +void nfsrv_cache_session(uint8_t *, uint32_t, int, struct mbuf **); /* nfs_nfsdserv.c */ int nfsrvd_access(struct nfsrv_descript *, int, @@ -211,10 +219,27 @@ int nfsrvd_releaselckown(struct nfsrv_descript *, int, vnode_t, NFSPROC_T *, struct nfsexstuff *); int nfsrvd_pathconf(struct nfsrv_descript *, int, vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_exchangeid(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_createsession(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_sequence(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_reclaimcomplete(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_destroyclientid(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_destroysession(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_freestateid(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); +int nfsrvd_notsupp(struct nfsrv_descript *, int, + vnode_t, NFSPROC_T *, struct nfsexstuff *); /* nfs_nfsdsocket.c */ void nfsrvd_rephead(struct nfsrv_descript *); -void nfsrvd_dorpc(struct nfsrv_descript *, int, NFSPROC_T *); +void nfsrvd_dorpc(struct nfsrv_descript *, int, u_char *, int, u_int32_t, + NFSPROC_T *); /* nfs_nfsdcache.c */ void nfsrvd_initcache(void); @@ -264,9 +289,11 @@ int nfsv4_getipaddr(struct nfsrv_descript *, struct sockaddr_storage *, int *); int nfsv4_seqsession(uint32_t, uint32_t, uint32_t, struct nfsslot *, struct mbuf **, uint16_t); -void nfsv4_seqsess_cacherep(uint32_t, struct nfsslot *, struct mbuf *); +void nfsv4_seqsess_cacherep(uint32_t, struct nfsslot *, int, struct mbuf **); void nfsv4_setsequence(struct nfsmount *, struct nfsrv_descript *, struct nfsclsession *, int); +int nfsv4_sequencelookup(struct nfsmount *, struct nfsclsession *, int *, + int *, uint32_t *, uint8_t *); void nfsv4_freeslot(struct nfsclsession *, int); /* nfs_clcomsubs.c */ @@ -322,6 +349,8 @@ int nfsrv_parsename(struct nfsrv_descript *, char *, u_long *, NFSPATHLEN_T *); void nfsd_init(void); int nfsd_checkrootexp(struct nfsrv_descript *); +void nfsd_getminorvers(struct nfsrv_descript *, u_char *, u_char **, int *, + u_int32_t *); /* nfs_clvfsops.c */ void nfscl_retopts(struct nfsmount *, char *, size_t); @@ -628,6 +657,7 @@ int nfsvno_advlock(vnode_t, int, u_int64_t, u_int64_t, NFSPROC_T *); int nfsrv_v4rootexport(void *, struct ucred *, NFSPROC_T *); int nfsvno_testexp(struct nfsrv_descript *, struct nfsexstuff *); uint32_t nfsrv_hashfh(fhandle_t *); +uint32_t nfsrv_hashsessionid(uint8_t *); void nfsrv_backupstable(void); /* nfs_commonkrpc.c */ diff --git a/sys/fs/nfs/nfsclstate.h b/sys/fs/nfs/nfsclstate.h index aa2bfee..c66d68a 100644 --- a/sys/fs/nfs/nfsclstate.h +++ b/sys/fs/nfs/nfsclstate.h @@ -57,6 +57,7 @@ struct nfsclsession { struct mtx nfsess_mtx; struct nfsslot nfsess_cbslots[NFSV4_CBSLOTS]; nfsquad_t nfsess_clientid; + SVCXPRT *nfsess_xprt; /* For backchannel callback */ uint32_t nfsess_slotseq[64]; /* Max for 64bit nm_slots */ uint64_t nfsess_slots; uint32_t nfsess_sequenceid; diff --git a/sys/fs/nfs/nfsdport.h b/sys/fs/nfs/nfsdport.h index a09a6dd..efa4b12 100644 --- a/sys/fs/nfs/nfsdport.h +++ b/sys/fs/nfs/nfsdport.h @@ -115,3 +115,9 @@ struct nfsexstuff { #define NFSRV_MINFH (sizeof (fhandle_t)) #define NFSRV_MAXFH (sizeof (fhandle_t)) +/* Use this macro for debug printfs. */ +#define NFSD_DEBUG(level, ...) do { \ + if (nfsd_debuglevel >= (level)) \ + printf(__VA_ARGS__); \ + } while (0) + diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h index cec50a6..557db11 100644 --- a/sys/fs/nfs/nfsport.h +++ b/sys/fs/nfs/nfsport.h @@ -637,6 +637,9 @@ void nfsrvd_rcv(struct socket *, void *, int); #define NFSUNLOCKSOCKREQ(r) mtx_unlock(&((r)->nr_mtx)) #define NFSLOCKDS(d) mtx_lock(&((d)->nfsclds_mtx)) #define NFSUNLOCKDS(d) mtx_unlock(&((d)->nfsclds_mtx)) +#define NFSSESSIONMUTEXPTR(s) (&((s)->mtx)) +#define NFSLOCKSESSION(s) mtx_lock(&((s)->mtx)) +#define NFSUNLOCKSESSION(s) mtx_unlock(&((s)->mtx)) /* * Use these macros to initialize/free a mutex. @@ -732,6 +735,7 @@ MALLOC_DECLARE(M_NEWNFSDEVINFO); MALLOC_DECLARE(M_NEWNFSSOCKREQ); MALLOC_DECLARE(M_NEWNFSCLDS); MALLOC_DECLARE(M_NEWNFSLAYRECALL); +MALLOC_DECLARE(M_NEWNFSDSESSION); #define M_NFSRVCACHE M_NEWNFSRVCACHE #define M_NFSDCLIENT M_NEWNFSDCLIENT #define M_NFSDSTATE M_NEWNFSDSTATE @@ -757,6 +761,7 @@ MALLOC_DECLARE(M_NEWNFSLAYRECALL); #define M_NFSSOCKREQ M_NEWNFSSOCKREQ #define M_NFSCLDS M_NEWNFSCLDS #define M_NFSLAYRECALL M_NEWNFSLAYRECALL +#define M_NFSDSESSION M_NEWNFSDSESSION #define NFSINT_SIGMASK(set) \ (SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \ diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h index 6836661..731be07 100644 --- a/sys/fs/nfs/nfsproto.h +++ b/sys/fs/nfs/nfsproto.h @@ -389,9 +389,13 @@ #define NFSV4OPEN_CLAIMPREVIOUS 1 #define NFSV4OPEN_CLAIMDELEGATECUR 2 #define NFSV4OPEN_CLAIMDELEGATEPREV 3 +#define NFSV4OPEN_CLAIMFH 4 +#define NFSV4OPEN_CLAIMDELEGATECURFH 5 +#define NFSV4OPEN_CLAIMDELEGATEPREVFH 6 #define NFSV4OPEN_DELEGATENONE 0 #define NFSV4OPEN_DELEGATEREAD 1 #define NFSV4OPEN_DELEGATEWRITE 2 +#define NFSV4OPEN_DELEGATENONEEXT 3 #define NFSV4OPEN_LIMITSIZE 1 #define NFSV4OPEN_LIMITBLOCKS 2 @@ -479,6 +483,14 @@ #define NFSV4OPEN_ACCESSREAD 0x00000001 #define NFSV4OPEN_ACCESSWRITE 0x00000002 #define NFSV4OPEN_ACCESSBOTH 0x00000003 +#define NFSV4OPEN_WANTDELEGMASK 0x0000ff00 +#define NFSV4OPEN_WANTREADDELEG 0x00000100 +#define NFSV4OPEN_WANTWRITEDELEG 0x00000200 +#define NFSV4OPEN_WANTANYDELEG 0x00000300 +#define NFSV4OPEN_WANTNODELEG 0x00000400 +#define NFSV4OPEN_WANTCANCEL 0x00000500 +#define NFSV4OPEN_WANTSIGNALDELEG 0x00010000 +#define NFSV4OPEN_WANTPUSHDELEG 0x00020000 #define NFSV4OPEN_DENYNONE 0x00000000 #define NFSV4OPEN_DENYREAD 0x00000001 @@ -486,16 +498,35 @@ #define NFSV4OPEN_DENYBOTH 0x00000003 /* + * Delegate_none_ext reply values. + */ +#define NFSV4OPEN_NOTWANTED 0 +#define NFSV4OPEN_CONTENTION 1 +#define NFSV4OPEN_RESOURCE 2 +#define NFSV4OPEN_NOTSUPPFTYPE 3 +#define NFSV4OPEN_NOTSUPPWRITEFTYPE 4 +#define NFSV4OPEN_NOTSUPPUPGRADE 5 +#define NFSV4OPEN_NOTSUPPDOWNGRADE 6 +#define NFSV4OPEN_CANCELLED 7 +#define NFSV4OPEN_ISDIR 8 + +/* * Open result flags - * (The first two are in the spec. The rest are used internally.) + * (The first four are in the spec. The rest are used internally.) */ #define NFSV4OPEN_RESULTCONFIRM 0x00000002 #define NFSV4OPEN_LOCKTYPEPOSIX 0x00000004 +#define NFSV4OPEN_PRESERVEUNLINKED 0x00000008 +#define NFSV4OPEN_MAYNOTIFYLOCK 0x00000020 #define NFSV4OPEN_RFLAGS \ - (NFSV4OPEN_RESULTCONFIRM | NFSV4OPEN_LOCKTYPEPOSIX) + (NFSV4OPEN_RESULTCONFIRM | NFSV4OPEN_LOCKTYPEPOSIX | \ + NFSV4OPEN_PRESERVEUNLINKED | NFSV4OPEN_MAYNOTIFYLOCK) #define NFSV4OPEN_RECALL 0x00010000 #define NFSV4OPEN_READDELEGATE 0x00020000 #define NFSV4OPEN_WRITEDELEGATE 0x00040000 +#define NFSV4OPEN_WDRESOURCE 0x00080000 +#define NFSV4OPEN_WDCONTENTION 0x00100000 +#define NFSV4OPEN_WDNOTWANTED 0x00200000 /* * NFS V4 File Handle types @@ -805,6 +836,27 @@ struct nfsv3_sattr { #define NFSATTRBIT_TIMEMODIFY 53 #define NFSATTRBIT_TIMEMODIFYSET 54 #define NFSATTRBIT_MOUNTEDONFILEID 55 +#define NFSATTRBIT_DIRNOTIFDELAY 56 +#define NFSATTRBIT_DIRENTNOTIFDELAY 57 +#define NFSATTRBIT_DACL 58 +#define NFSATTRBIT_SACL 59 +#define NFSATTRBIT_CHANGEPOLICY 60 +#define NFSATTRBIT_FSSTATUS 61 +#define NFSATTRBIT_FSLAYOUTTYPE 62 +#define NFSATTRBIT_LAYOUTHINT 63 +#define NFSATTRBIT_LAYOUTTYPE 64 +#define NFSATTRBIT_LAYOUTBLKSIZE 65 +#define NFSATTRBIT_LAYOUTALIGNMENT 66 +#define NFSATTRBIT_FSLOCATIONSINFO 67 +#define NFSATTRBIT_MDSTHRESHOLD 68 +#define NFSATTRBIT_RETENTIONGET 69 +#define NFSATTRBIT_RETENTIONSET 70 +#define NFSATTRBIT_RETENTEVTGET 71 +#define NFSATTRBIT_RETENTEVTSET 72 +#define NFSATTRBIT_RETENTIONHOLD 73 +#define NFSATTRBIT_MODESETMASKED 74 +#define NFSATTRBIT_SUPPATTREXCLCREAT 75 +#define NFSATTRBIT_FSCHARSETCAP 76 #define NFSATTRBM_SUPPORTEDATTRS 0x00000001 #define NFSATTRBM_TYPE 0x00000002 @@ -862,8 +914,29 @@ struct nfsv3_sattr { #define NFSATTRBM_TIMEMODIFY 0x00200000 #define NFSATTRBM_TIMEMODIFYSET 0x00400000 #define NFSATTRBM_MOUNTEDONFILEID 0x00800000 - -#define NFSATTRBIT_MAX 56 +#define NFSATTRBM_DIRNOTIFDELAY 0x01000000 +#define NFSATTRBM_DIRENTNOTIFDELAY 0x02000000 +#define NFSATTRBM_DACL 0x04000000 +#define NFSATTRBM_SACL 0x08000000 +#define NFSATTRBM_CHANGEPOLICY 0x10000000 +#define NFSATTRBM_FSSTATUS 0x20000000 +#define NFSATTRBM_FSLAYOUTTYPE 0x40000000 +#define NFSATTRBM_LAYOUTHINT 0x80000000 +#define NFSATTRBM_LAYOUTTYPE 0x00000001 +#define NFSATTRBM_LAYOUTBLKSIZE 0x00000002 +#define NFSATTRBM_LAYOUTALIGNMENT 0x00000004 +#define NFSATTRBM_FSLOCATIONSINFO 0x00000008 +#define NFSATTRBM_MDSTHRESHOLD 0x00000010 +#define NFSATTRBM_RETENTIONGET 0x00000020 +#define NFSATTRBM_RETENTIONSET 0x00000040 +#define NFSATTRBM_RETENTEVTGET 0x00000080 +#define NFSATTRBM_RETENTEVTSET 0x00000100 +#define NFSATTRBM_RETENTIONHOLD 0x00000200 +#define NFSATTRBM_MODESETMASKED 0x00000400 +#define NFSATTRBM_SUPPATTREXCLCREAT 0x00000800 +#define NFSATTRBM_FSCHARSETCAP 0x00001000 + +#define NFSATTRBIT_MAX 77 /* * Sets of attributes that are supported, by words in the bitmap. @@ -871,6 +944,7 @@ struct nfsv3_sattr { /* * NFSATTRBIT_SUPPORTED - SUPP0 - bits 0<->31 * SUPP1 - bits 32<->63 + * SUPP2 - bits 64<->95 */ #define NFSATTRBIT_SUPP0 \ (NFSATTRBM_SUPPORTEDATTRS | \ @@ -937,6 +1011,8 @@ struct nfsv3_sattr { #define NFSATTRBIT_SUPP1 NFSATTRBIT_S1 #endif +#define NFSATTRBIT_SUPP2 NFSATTRBM_SUPPATTREXCLCREAT + /* * NFSATTRBIT_SUPPSETONLY is the OR of NFSATTRBIT_TIMEACCESSSET and * NFSATTRBIT_TIMEMODIFYSET. @@ -947,6 +1023,7 @@ struct nfsv3_sattr { /* * NFSATTRBIT_SETABLE - SETABLE0 - bits 0<->31 * SETABLE1 - bits 32<->63 + * SETABLE2 - bits 64<->95 */ #define NFSATTRBIT_SETABLE0 \ (NFSATTRBM_SIZE | \ @@ -957,6 +1034,7 @@ struct nfsv3_sattr { NFSATTRBM_OWNERGROUP | \ NFSATTRBM_TIMEACCESSSET | \ NFSATTRBM_TIMEMODIFYSET) +#define NFSATTRBIT_SETABLE2 0 /* * Set of attributes that the getattr vnode op needs. @@ -987,6 +1065,11 @@ struct nfsv3_sattr { NFSATTRBM_TIMEMODIFY) /* + * NFSATTRBIT_GETATTR2 - bits 64<->95 + */ +#define NFSATTRBIT_GETATTR2 0 + +/* * Subset of the above that the Write RPC gets. * OR of the following bits. * NFSATTRBIT_WRITEGETATTR0 - bits 0<->31 @@ -1013,6 +1096,11 @@ struct nfsv3_sattr { NFSATTRBM_TIMEMODIFY) /* + * NFSATTRBIT_WRITEGETATTR2 - bits 64<->95 + */ +#define NFSATTRBIT_WRITEGETATTR2 0 + +/* * Set of attributes that the wccattr operation op needs. * OR of the following bits. * NFSATTRBIT_WCCATTR0 - bits 0<->31 @@ -1026,6 +1114,11 @@ struct nfsv3_sattr { (NFSATTRBM_TIMEMODIFY) /* + * NFSATTRBIT_WCCATTR2 - bits 64<->95 + */ +#define NFSATTRBIT_WCCATTR2 0 + +/* * NFSATTRBIT_CBGETATTR0 - bits 0<->31 */ #define NFSATTRBIT_CBGETATTR0 (NFSATTRBM_CHANGE | NFSATTRBM_SIZE) @@ -1036,6 +1129,11 @@ struct nfsv3_sattr { #define NFSATTRBIT_CBGETATTR1 0x0 /* + * NFSATTRBIT_CBGETATTR2 - bits 64<->95 + */ +#define NFSATTRBIT_CBGETATTR2 0x0 + +/* * Sets of attributes that require a VFS_STATFS() call to get the * values of. * NFSATTRBIT_STATFS0 - bits 0<->31 @@ -1067,6 +1165,11 @@ struct nfsv3_sattr { NFSATTRBM_TIMEDELTA) /* + * NFSATTRBIT_STATFS2 - bits 64<->95 + */ +#define NFSATTRBIT_STATFS2 0 + +/* * These are the bits that are needed by the nfs_statfs() call. * (The regular getattr bits are or'd in so the vnode gets the correct * type, etc.) @@ -1094,6 +1197,11 @@ struct nfsv3_sattr { NFSATTRBM_TIMEDELTA) /* + * NFSGETATTRBIT_STATFS2 - bits 64<->95 + */ +#define NFSGETATTRBIT_STATFS2 0 + +/* * Set of attributes for the equivalent of an nfsv3 pathconf rpc. * NFSGETATTRBIT_PATHCONF0 - bits 0<->31 */ @@ -1111,6 +1219,11 @@ struct nfsv3_sattr { NFSATTRBM_NOTRUNC) /* + * NFSGETATTRBIT_PATHCONF2 - bits 64<->95 + */ +#define NFSGETATTRBIT_PATHCONF2 0 + +/* * Sets of attributes required by readdir and readdirplus. * NFSATTRBIT_READDIRPLUS0 (NFSATTRBIT_GETATTR0 | NFSATTRBIT_FILEHANDLE | * NFSATTRBIT_RDATTRERROR) @@ -1118,6 +1231,7 @@ struct nfsv3_sattr { #define NFSATTRBIT_READDIRPLUS0 (NFSATTRBIT_GETATTR0 | NFSATTRBM_FILEHANDLE | \ NFSATTRBM_RDATTRERROR) #define NFSATTRBIT_READDIRPLUS1 NFSATTRBIT_GETATTR1 +#define NFSATTRBIT_READDIRPLUS2 0 /* * Set of attributes supported by Referral vnodes. @@ -1125,6 +1239,7 @@ struct nfsv3_sattr { #define NFSATTRBIT_REFERRAL0 (NFSATTRBM_TYPE | NFSATTRBM_FSID | \ NFSATTRBM_RDATTRERROR | NFSATTRBM_FSLOCATIONS) #define NFSATTRBIT_REFERRAL1 NFSATTRBM_MOUNTEDONFILEID +#define NFSATTRBIT_REFERRAL2 0 /* * Structure for data handled by the statfs rpc. Since some fields are diff --git a/sys/fs/nfs/nfsrvstate.h b/sys/fs/nfs/nfsrvstate.h index 964b998..59a0542 100644 --- a/sys/fs/nfs/nfsrvstate.h +++ b/sys/fs/nfs/nfsrvstate.h @@ -42,6 +42,8 @@ LIST_HEAD(nfsclienthashhead, nfsclient); LIST_HEAD(nfsstatehead, nfsstate); LIST_HEAD(nfslockhead, nfslock); LIST_HEAD(nfslockhashhead, nfslockfile); +LIST_HEAD(nfssessionhead, nfsdsession); +LIST_HEAD(nfssessionhashhead, nfsdsession); /* * List head for nfsusrgrp. @@ -64,6 +66,13 @@ TAILQ_HEAD(nfsuserlruhead, nfsusrgrp); (&nfsgroupnamehash[((l)>=4?(*(p)+*((p)+1)+*((p)+2)+*((p)+3)):*(p)) \ % NFSGROUPHASHSIZE]) +struct nfssessionhash { + struct mtx mtx; + struct nfssessionhashhead list; +}; +#define NFSSESSIONHASH(f) \ + (&nfssessionhash[nfsrv_hashsessionid(f) % NFSSESSIONHASHSIZE]) + /* * Client server structure for V4. It is doubly linked into two lists. * The first is a hash table based on the clientid and the second is a @@ -76,6 +85,7 @@ struct nfsclient { struct nfsstatehead lc_open; /* Open owner list */ struct nfsstatehead lc_deleg; /* Delegations */ struct nfsstatehead lc_olddeleg; /* and old delegations */ + struct nfssessionhead lc_session; /* List of NFSv4.1 sessions */ time_t lc_expiry; /* Expiry time (sec) */ time_t lc_delegtime; /* Old deleg expiry (sec) */ nfsquad_t lc_clientid; /* 64 bit clientid */ @@ -101,6 +111,43 @@ struct nfsclient { #define CLOPS_RENEWOP 0x0004 /* + * Structure for an NFSv4.1 session. + * Locking rules for this structure. + * To add/delete one of these structures from the lists, you must lock + * both: NFSLOCKSESSION(session hashhead) and NFSLOCKSTATE() in that order. + * To traverse the lists looking for one of these, you must hold one + * of these two locks. + * The exception is if the thread holds the exclusive root sleep lock. + * In this case, all other nfsd threads are blocked, so locking the + * mutexes isn't required. + * When manipulating sess_refcnt, NFSLOCKSTATE() must be locked. + * When manipulating the fields withinsess_cbsess except nfsess_xprt, + * sess_cbsess.nfsess_mtx must be locked. + * When manipulating sess_slots and sess_cbsess.nfsess_xprt, + * NFSLOCKSESSION(session hashhead) must be locked. + */ +struct nfsdsession { + uint64_t sess_refcnt; /* Reference count. */ + LIST_ENTRY(nfsdsession) sess_hash; /* Hash list of sessions. */ + LIST_ENTRY(nfsdsession) sess_list; /* List of client sessions. */ + struct nfsslot sess_slots[NFSV4_SLOTS]; + struct nfsclient *sess_clp; /* Associated clientid. */ + uint32_t sess_crflags; + uint32_t sess_cbprogram; + uint32_t sess_maxreq; + uint32_t sess_maxresp; + uint32_t sess_maxrespcached; + uint32_t sess_maxops; + uint32_t sess_maxslots; + uint32_t sess_cbmaxreq; + uint32_t sess_cbmaxresp; + uint32_t sess_cbmaxrespcached; + uint32_t sess_cbmaxops; + uint8_t sess_sessionid[NFSX_V4SESSIONID]; + struct nfsclsession sess_cbsess; /* Callback session. */ +}; + +/* * Nfs state structure. I couldn't resist overloading this one, since * it makes cleanup, etc. simpler. These structures are used in four ways: * - open_owner structures chained off of nfsclient diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index 6f0692c..2600b80 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -3548,7 +3548,7 @@ out: if (clp != NULL) { nfsv4_seqsess_cacherep(slotid, NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots, - rep); + NFSERR_OK, &rep); NFSUNLOCKCLSTATE(); } else { NFSUNLOCKCLSTATE(); diff --git a/sys/fs/nfsserver/nfs_nfsdcache.c b/sys/fs/nfsserver/nfs_nfsdcache.c index 2c4a654..0f78b3f 100644 --- a/sys/fs/nfsserver/nfs_nfsdcache.c +++ b/sys/fs/nfsserver/nfs_nfsdcache.c @@ -977,6 +977,9 @@ nfsrvd_refcache(struct nfsrvcache *rp) { struct mtx *mutex; + if (rp == NULL) + /* For NFSv4.1, there is no cache entry. */ + return; mutex = nfsrc_cachemutex(rp); mtx_lock(mutex); if (rp->rc_refcnt < 0) diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c index 9649093..d2145cc 100644 --- a/sys/fs/nfsserver/nfs_nfsdkrpc.c +++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c @@ -305,7 +305,10 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, SVCXPRT *xprt, struct nfsrvcache **rpp) { struct thread *td = curthread; - int cacherep = RC_DOIT, isdgram; + int cacherep = RC_DOIT, isdgram, taglen = -1; + struct mbuf *m; + u_char tag[NFSV4_SMALLSTR + 1], *tagstr = NULL; + u_int32_t minorvers = 0; uint32_t ack; *rpp = NULL; @@ -339,10 +342,18 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, SVCXPRT *xprt, nd->nd_retxid = xid; nd->nd_tcpconntime = NFSD_MONOSEC; nd->nd_sockref = xprt->xp_sockref; - cacherep = nfsrvd_getcache(nd); - ack = 0; - SVC_ACK(xprt, &ack); - nfsrc_trimcache(xprt->xp_sockref, ack, 0); + if ((nd->nd_flag & ND_NFSV4) != 0) + nfsd_getminorvers(nd, tag, &tagstr, &taglen, + &minorvers); + if ((nd->nd_flag & ND_NFSV41) != 0) + /* NFSv4.1 caches replies in the session slots. */ + cacherep = RC_DOIT; + else { + cacherep = nfsrvd_getcache(nd); + ack = 0; + SVC_ACK(xprt, &ack); + nfsrc_trimcache(xprt->xp_sockref, ack, 0); + } } /* @@ -352,13 +363,33 @@ nfs_proc(struct nfsrv_descript *nd, u_int32_t xid, SVCXPRT *xprt, * RC_DROPIT - just throw the request away */ if (cacherep == RC_DOIT) { - nfsrvd_dorpc(nd, isdgram, td); - if (nd->nd_repstat == NFSERR_DONTREPLY) - cacherep = RC_DROPIT; - else + if ((nd->nd_flag & ND_NFSV41) != 0) + nd->nd_xprt = xprt; + nfsrvd_dorpc(nd, isdgram, tagstr, taglen, minorvers, td); + if ((nd->nd_flag & ND_NFSV41) != 0) { + if (nd->nd_repstat != NFSERR_REPLYFROMCACHE && + (nd->nd_flag & ND_SAVEREPLY) != 0) { + /* Cache a copy of the reply. */ + m = m_copym(nd->nd_mreq, 0, M_COPYALL, + M_WAITOK); + } else + m = NULL; + if ((nd->nd_flag & ND_HASSEQUENCE) != 0) + nfsrv_cache_session(nd->nd_sessionid, + nd->nd_slotid, nd->nd_repstat, &m); + if (nd->nd_repstat == NFSERR_REPLYFROMCACHE) + nd->nd_repstat = 0; cacherep = RC_REPLY; - *rpp = nfsrvd_updatecache(nd); + } else { + if (nd->nd_repstat == NFSERR_DONTREPLY) + cacherep = RC_DROPIT; + else + cacherep = RC_REPLY; + *rpp = nfsrvd_updatecache(nd); + } } + if (tagstr != NULL && taglen > NFSV4_SMALLSTR) + free(tagstr, M_TEMP); NFSEXITCODE2(0, nd); return (cacherep); diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index 189742b..5799958 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -58,6 +58,7 @@ extern struct nfsrv_stablefirst nfsrv_stablefirst; extern void (*nfsd_call_servertimer)(void); extern SVCPOOL *nfsrvd_pool; extern struct nfsv4lock nfsd_suspend_lock; +extern struct nfssessionhash nfssessionhash[NFSSESSIONHASHSIZE]; struct vfsoptlist nfsv4root_opt, nfsv4root_newopt; NFSDLOCKMUTEX; struct nfsrchash_bucket nfsrchash_table[NFSRVCACHE_HASHSIZE]; @@ -67,6 +68,7 @@ struct mtx nfs_v4root_mutex; struct nfsrvfh nfs_rootfh, nfs_pubfh; int nfs_pubfhset = 0, nfs_rootfhset = 0; struct proc *nfsd_master_proc = NULL; +int nfsd_debuglevel = 0; static pid_t nfsd_master_pid = (pid_t)-1; static char nfsd_master_comm[MAXCOMLEN + 1]; static struct timeval nfsd_master_start; @@ -93,6 +95,8 @@ SYSCTL_INT(_vfs_nfsd, OID_AUTO, issue_delegations, CTLFLAG_RW, &nfsrv_issuedelegs, 0, "Enable nfsd to issue delegations"); SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_locallocks, CTLFLAG_RW, &nfsrv_dolocallocks, 0, "Enable nfsd to acquire local locks on files"); +SYSCTL_INT(_vfs_nfsd, OID_AUTO, debuglevel, CTLFLAG_RW, &nfsd_debuglevel, + 0, "Debug level for new nfs server"); SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_stringtouid, CTLFLAG_RW, &nfsd_enable_stringtouid, 0, "Enable nfsd to accept numeric owner_names"); @@ -3261,6 +3265,18 @@ nfsrv_hashfh(fhandle_t *fhp) } /* + * Calculate a hash value for the sessionid. + */ +uint32_t +nfsrv_hashsessionid(uint8_t *sessionid) +{ + uint32_t hashval; + + hashval = hash32_buf(sessionid, NFSX_V4SESSIONID, 0); + return (hashval); +} + +/* * Signal the userland master nfsd to backup the stable restart file. */ void @@ -3318,6 +3334,9 @@ nfsd_modevent(module_t mod, int type, void *data) mtx_init(&nfs_v4root_mutex, "nfs_v4root_mutex", NULL, MTX_DEF); mtx_init(&nfsv4root_mnt.mnt_mtx, "struct mount mtx", NULL, MTX_DEF); + for (i = 0; i < NFSSESSIONHASHSIZE; i++) + mtx_init(&nfssessionhash[i].mtx, "nfs_session_mutex", + NULL, MTX_DEF); lockinit(&nfsv4root_mnt.mnt_explock, PVFS, "explock", 0, 0); nfsrvd_initcache(); nfsd_init(); @@ -3365,6 +3384,8 @@ nfsd_modevent(module_t mod, int type, void *data) mtx_destroy(&nfsrc_udpmtx); mtx_destroy(&nfs_v4root_mutex); mtx_destroy(&nfsv4root_mnt.mnt_mtx); + for (i = 0; i < NFSSESSIONHASHSIZE; i++) + mtx_destroy(&nfssessionhash[i].mtx); lockdestroy(&nfsv4root_mnt.mnt_explock); loaded = 0; break; diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c index 69c3af0..0d3ff76 100644 --- a/sys/fs/nfsserver/nfs_nfsdserv.c +++ b/sys/fs/nfsserver/nfs_nfsdserv.c @@ -666,10 +666,14 @@ nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); clientid.lval[0] = stp->ls_stateid.other[0] = *tl++; clientid.lval[1] = stp->ls_stateid.other[1] = *tl++; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK1 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -818,10 +822,14 @@ nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); clientid.lval[0] = stp->ls_stateid.other[0] = *tl++; clientid.lval[1] = stp->ls_stateid.other[1] = *tl++; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK2 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2204,10 +2212,14 @@ nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_opentolockseq = fxdr_unsigned(int, *tl++); clientid.lval[0] = *tl++; clientid.lval[1] = *tl++; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK3 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2227,10 +2239,14 @@ nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_seq = fxdr_unsigned(int, *tl); clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK4 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2376,10 +2392,14 @@ nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram, tl += 2; clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK5 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2487,10 +2507,14 @@ nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram, } clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK6 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2531,7 +2555,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, struct nfsexstuff *exp) { u_int32_t *tl; - int i; + int i, retext; struct nfsstate *stp = NULL; int error = 0, create, claim, exclusive_flag = 0; u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask; @@ -2568,6 +2592,39 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_uid = nd->nd_cred->cr_uid; stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++); i = fxdr_unsigned(int, *tl++); + retext = 0; + if ((i & (NFSV4OPEN_WANTDELEGMASK | NFSV4OPEN_WANTSIGNALDELEG | + NFSV4OPEN_WANTPUSHDELEG)) != 0 && (nd->nd_flag & ND_NFSV41) != 0) { + retext = 1; + /* For now, ignore these. */ + i &= ~(NFSV4OPEN_WANTPUSHDELEG | NFSV4OPEN_WANTSIGNALDELEG); + switch (i & NFSV4OPEN_WANTDELEGMASK) { + case NFSV4OPEN_WANTANYDELEG: + stp->ls_flags |= (NFSLCK_WANTRDELEG | + NFSLCK_WANTWDELEG); + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTREADDELEG: + stp->ls_flags |= NFSLCK_WANTRDELEG; + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTWRITEDELEG: + stp->ls_flags |= NFSLCK_WANTWDELEG; + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTNODELEG: + stp->ls_flags |= NFSLCK_WANTNODELEG; + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + case NFSV4OPEN_WANTCANCEL: + printf("NFSv4: ignore Open WantCancel\n"); + i &= ~NFSV4OPEN_WANTDELEGMASK; + break; + default: + /* nd_repstat will be set to NFSERR_INVAL below. */ + break; + }; + } switch (i) { case NFSV4OPEN_ACCESSREAD: stp->ls_flags |= NFSLCK_READACCESS; @@ -2599,10 +2656,14 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, }; clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK7 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2642,6 +2703,28 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, cverf[0] = *tl++; cverf[1] = *tl; break; + case NFSCREATE_EXCLUSIVE41: + NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF); + cverf[0] = *tl++; + cverf[1] = *tl; + error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p); + if (error != 0) + goto nfsmout; + if (NFSISSET_ATTRBIT(&attrbits, + NFSATTRBIT_TIMEACCESSSET)) + nd->nd_repstat = NFSERR_INVAL; + /* + * If the na_gid being set is the same as that of + * the directory it is going in, clear it, since + * that is what will be set by default. This allows + * a user that isn't in that group to do the create. + */ + if (nd->nd_repstat == 0 && NFSVNO_ISSETGID(&nva) && + nva.na_gid == dirfor.na_gid) + NFSVNO_UNSET(&nva, gid); + if (nd->nd_repstat == 0) + nd->nd_repstat = nfsrv_checkuidgid(nd, &nva); + break; default: nd->nd_repstat = NFSERR_BADXDR; goto nfsmout; @@ -2723,27 +2806,38 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, exclusive_flag = 1; if (!named.ni_vp) nva.na_mode = 0; + break; + case NFSCREATE_EXCLUSIVE41: + exclusive_flag = 1; + break; }; } nfsvno_open(nd, &named, clientid, &stateid, stp, &exclusive_flag, &nva, cverf, create, aclp, &attrbits, nd->nd_cred, p, exp, &vp); - } else if (claim == NFSV4OPEN_CLAIMPREVIOUS) { - NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); - i = fxdr_unsigned(int, *tl); - switch (i) { - case NFSV4OPEN_DELEGATEREAD: - stp->ls_flags |= NFSLCK_DELEGREAD; - break; - case NFSV4OPEN_DELEGATEWRITE: - stp->ls_flags |= NFSLCK_DELEGWRITE; - case NFSV4OPEN_DELEGATENONE: - break; - default: - nd->nd_repstat = NFSERR_BADXDR; - goto nfsmout; - }; - stp->ls_flags |= NFSLCK_RECLAIM; + } else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim == + NFSV4OPEN_CLAIMFH) { + if (claim == NFSV4OPEN_CLAIMPREVIOUS) { + NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); + i = fxdr_unsigned(int, *tl); + switch (i) { + case NFSV4OPEN_DELEGATEREAD: + stp->ls_flags |= NFSLCK_DELEGREAD; + break; + case NFSV4OPEN_DELEGATEWRITE: + stp->ls_flags |= NFSLCK_DELEGWRITE; + case NFSV4OPEN_DELEGATENONE: + break; + default: + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + }; + stp->ls_flags |= NFSLCK_RECLAIM; + } else { + /* CLAIM_NULL_FH */ + if (nd->nd_repstat == 0 && create == NFSV4OPEN_CREATE) + nd->nd_repstat = NFSERR_INVAL; + } vp = dp; NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); if ((vp->v_iflag & VI_DOOMED) == 0) @@ -2832,7 +2926,21 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, *tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD); else if (rflags & NFSV4OPEN_WRITEDELEGATE) *tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE); - else + else if (retext != 0) { + *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONEEXT); + if ((rflags & NFSV4OPEN_WDCONTENTION) != 0) { + NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSV4OPEN_CONTENTION); + *tl = newnfs_false; + } else if ((rflags & NFSV4OPEN_WDRESOURCE) != 0) { + NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSV4OPEN_RESOURCE); + *tl = newnfs_false; + } else { + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OPEN_NOTWANTED); + } + } else *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE); if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) { NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED); @@ -2908,10 +3016,14 @@ nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_flags = NFSLCK_CLOSE; clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK8 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -2948,14 +3060,18 @@ nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram, NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK9 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } - nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL, + nd->nd_repstat = nfsrv_delegupdate(nd, clientid, NULL, NULL, NFSV4OP_DELEGPURGE, nd->nd_cred, p); nfsmout: NFSEXITCODE2(error, nd); @@ -2979,14 +3095,18 @@ nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram, NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER); clientid.lval[0] = stateid.other[0]; clientid.lval[1] = stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK10 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } - nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp, + nd->nd_repstat = nfsrv_delegupdate(nd, clientid, &stateid, vp, NFSV4OP_DELEGRETURN, nd->nd_cred, p); nfsmout: vput(vp); @@ -3024,6 +3144,10 @@ nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram, nfsv4stateid_t stateid; nfsquad_t clientid; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED); stp->ls_ownerlen = 0; stp->ls_op = nd->nd_rp; @@ -3036,10 +3160,14 @@ nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_flags = NFSLCK_CONFIRM; clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK11 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -3112,10 +3240,14 @@ nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram, clientid.lval[0] = stp->ls_stateid.other[0]; clientid.lval[1] = stp->ls_stateid.other[1]; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK12 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -3144,6 +3276,10 @@ nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram, int error = 0; nfsquad_t clientid; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto nfsmout; @@ -3151,15 +3287,19 @@ nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram, NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER); clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK13 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW), - NULL, (nfsquad_t)((u_quad_t)0), nd, p); + NULL, NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); nfsmout: NFSEXITCODE2(error, nd); return (error); @@ -3283,6 +3423,10 @@ nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram, u_char *verf, *ucp, *ucp2, addrbuf[24]; nfsquad_t clientid, confirm; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto out; @@ -3395,6 +3539,10 @@ nfsrvd_setclientidcfrm(struct nfsrv_descript *nd, int error = 0; nfsquad_t clientid, confirm; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto nfsmout; @@ -3410,7 +3558,7 @@ nfsrvd_setclientidcfrm(struct nfsrv_descript *nd, * returns the appropriate NFSERR status. */ nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW), - NULL, confirm, nd, p); + NULL, NULL, confirm, 0, nd, p); nfsmout: NFSEXITCODE2(error, nd); return (error); @@ -3485,6 +3633,10 @@ nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram, int error = 0, len; nfsquad_t clientid; + if ((nd->nd_flag & ND_NFSV41) != 0) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { nd->nd_repstat = NFSERR_WRONGSEC; goto nfsmout; @@ -3503,10 +3655,14 @@ nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram, stp->ls_uid = nd->nd_cred->cr_uid; clientid.lval[0] = *tl++; clientid.lval[1] = *tl; - if (nd->nd_flag & ND_IMPLIEDCLID) { - if (nd->nd_clientid.qval != clientid.qval) - printf("EEK! multiple clids\n"); + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) { + if ((nd->nd_flag & ND_NFSV41) != 0) + clientid.qval = nd->nd_clientid.qval; + else if (nd->nd_clientid.qval != clientid.qval) + printf("EEK14 multiple clids\n"); } else { + if ((nd->nd_flag & ND_NFSV41) != 0) + printf("EEK! no clientid from session\n"); nd->nd_flag |= ND_IMPLIEDCLID; nd->nd_clientid.qval = clientid.qval; } @@ -3524,3 +3680,385 @@ nfsmout: NFSEXITCODE2(error, nd); return (error); } + +/* + * nfsv4 exchange_id service + */ +APPLESTATIC int +nfsrvd_exchangeid(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + int error = 0, i, idlen; + struct nfsclient *clp = NULL; + nfsquad_t clientid, confirm; + uint8_t *verf; + uint32_t sp4type, v41flags; + uint64_t owner_minor; + struct timespec verstime; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED); + verf = (uint8_t *)tl; + tl += (NFSX_VERF / NFSX_UNSIGNED); + i = fxdr_unsigned(int, *tl); + if (i > NFSV4_OPAQUELIMIT || i <= 0) { + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } + idlen = i; + if (nd->nd_flag & ND_GSS) + i += nd->nd_princlen; + clp = (struct nfsclient *)malloc(sizeof(struct nfsclient) + i, + M_NFSDCLIENT, M_WAITOK | M_ZERO); + NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx); + NFSSOCKADDRALLOC(clp->lc_req.nr_nam); + NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in)); + clp->lc_req.nr_cred = NULL; + NFSBCOPY(verf, clp->lc_verf, NFSX_VERF); + clp->lc_idlen = idlen; + error = nfsrv_mtostr(nd, clp->lc_id, idlen); + if (error != 0) + goto nfsmout; + if ((nd->nd_flag & ND_GSS) != 0) { + clp->lc_flags = LCL_GSS | LCL_NFSV41; + if ((nd->nd_flag & ND_GSSINTEGRITY) != 0) + clp->lc_flags |= LCL_GSSINTEGRITY; + else if ((nd->nd_flag & ND_GSSPRIVACY) != 0) + clp->lc_flags |= LCL_GSSPRIVACY; + } else + clp->lc_flags = LCL_NFSV41; + if ((nd->nd_flag & ND_GSS) != 0 && nd->nd_princlen > 0) { + clp->lc_flags |= LCL_NAME; + clp->lc_namelen = nd->nd_princlen; + clp->lc_name = &clp->lc_id[idlen]; + NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen); + } else { + clp->lc_uid = nd->nd_cred->cr_uid; + clp->lc_gid = nd->nd_cred->cr_gid; + } + NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); + v41flags = fxdr_unsigned(uint32_t, *tl++); + if ((v41flags & ~(NFSV4EXCH_SUPPMOVEDREFER | NFSV4EXCH_SUPPMOVEDMIGR | + NFSV4EXCH_BINDPRINCSTATEID | NFSV4EXCH_MASKPNFS | + NFSV4EXCH_UPDCONFIRMEDRECA)) != 0) { + nd->nd_repstat = NFSERR_INVAL; + goto nfsmout; + } + if ((v41flags & NFSV4EXCH_UPDCONFIRMEDRECA) != 0) + confirm.lval[1] = 1; + else + confirm.lval[1] = 0; + v41flags = NFSV4EXCH_USENONPNFS; + sp4type = fxdr_unsigned(uint32_t, *tl); + if (sp4type != NFSV4EXCH_SP4NONE) { + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } + + /* + * nfsrv_setclient() does the actual work of adding it to the + * client list. If there is no error, the structure has been + * linked into the client list and clp should no longer be used + * here. When an error is returned, it has not been linked in, + * so it should be free'd. + */ + nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p); + if (clp != NULL) { + NFSSOCKADDRFREE(clp->lc_req.nr_nam); + NFSFREEMUTEX(&clp->lc_req.nr_mtx); + free(clp, M_NFSDCLIENT); + } + if (nd->nd_repstat == 0) { + if (confirm.lval[1] != 0) + v41flags |= NFSV4EXCH_CONFIRMEDR; + NFSM_BUILD(tl, uint32_t *, 2 * NFSX_HYPER + 3 * NFSX_UNSIGNED); + *tl++ = clientid.lval[0]; /* ClientID */ + *tl++ = clientid.lval[1]; + *tl++ = txdr_unsigned(confirm.lval[0]); /* SequenceID */ + *tl++ = txdr_unsigned(v41flags); /* Exch flags */ + *tl++ = txdr_unsigned(NFSV4EXCH_SP4NONE); /* No SSV */ + owner_minor = 0; /* Owner */ + txdr_hyper(owner_minor, tl); /* Minor */ + (void)nfsm_strtom(nd, nd->nd_cred->cr_prison->pr_hostuuid, + strlen(nd->nd_cred->cr_prison->pr_hostuuid)); /* Major */ + NFSM_BUILD(tl, uint32_t *, 3 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSX_UNSIGNED); + *tl++ = time_uptime; /* Make scope a unique value. */ + *tl = txdr_unsigned(1); + (void)nfsm_strtom(nd, "freebsd.org", strlen("freebsd.org")); + (void)nfsm_strtom(nd, version, strlen(version)); + NFSM_BUILD(tl, uint32_t *, NFSX_V4TIME); + verstime.tv_sec = 1293840000; /* Jan 1, 2011 */ + verstime.tv_nsec = 0; + txdr_nfsv4time(&verstime, tl); + } + NFSEXITCODE2(0, nd); + return (0); +nfsmout: + if (clp != NULL) { + NFSSOCKADDRFREE(clp->lc_req.nr_nam); + NFSFREEMUTEX(&clp->lc_req.nr_mtx); + free(clp, M_NFSDCLIENT); + } + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 create session service + */ +APPLESTATIC int +nfsrvd_createsession(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + int error = 0; + nfsquad_t clientid, confirm; + struct nfsdsession *sep = NULL; + uint32_t rdmacnt; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + sep = (struct nfsdsession *)malloc(sizeof(struct nfsdsession), + M_NFSDSESSION, M_WAITOK | M_ZERO); + sep->sess_refcnt = 1; + mtx_init(&sep->sess_cbsess.nfsess_mtx, "nfscbsession", NULL, MTX_DEF); + NFSM_DISSECT(tl, uint32_t *, NFSX_HYPER + 2 * NFSX_UNSIGNED); + clientid.lval[0] = *tl++; + clientid.lval[1] = *tl++; + confirm.lval[0] = fxdr_unsigned(uint32_t, *tl++); + sep->sess_crflags = fxdr_unsigned(uint32_t, *tl); + /* Persistent sessions and RDMA are not supported. */ + sep->sess_crflags &= NFSV4CRSESS_CONNBACKCHAN; + + /* Fore channel attributes. */ + NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED); + tl++; /* Header pad always 0. */ + sep->sess_maxreq = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxresp = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxrespcached = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxops = fxdr_unsigned(uint32_t, *tl++); + sep->sess_maxslots = fxdr_unsigned(uint32_t, *tl++); + if (sep->sess_maxslots > NFSV4_SLOTS) + sep->sess_maxslots = NFSV4_SLOTS; + rdmacnt = fxdr_unsigned(uint32_t, *tl); + if (rdmacnt > 1) { + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } else if (rdmacnt == 1) + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + + /* Back channel attributes. */ + NFSM_DISSECT(tl, uint32_t *, 7 * NFSX_UNSIGNED); + tl++; /* Header pad always 0. */ + sep->sess_cbmaxreq = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbmaxresp = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbmaxrespcached = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbmaxops = fxdr_unsigned(uint32_t, *tl++); + sep->sess_cbsess.nfsess_foreslots = fxdr_unsigned(uint32_t, *tl++); + rdmacnt = fxdr_unsigned(uint32_t, *tl); + if (rdmacnt > 1) { + nd->nd_repstat = NFSERR_BADXDR; + goto nfsmout; + } else if (rdmacnt == 1) + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + sep->sess_cbprogram = fxdr_unsigned(uint32_t, *tl); + + /* + * nfsrv_getclient() searches the client list for a match and + * returns the appropriate NFSERR status. + */ + nd->nd_repstat = nfsrv_getclient(clientid, CLOPS_CONFIRM | CLOPS_RENEW, + NULL, sep, confirm, sep->sess_cbprogram, nd, p); + if (nd->nd_repstat == 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID); + NFSBCOPY(sep->sess_sessionid, tl, NFSX_V4SESSIONID); + NFSM_BUILD(tl, uint32_t *, 18 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(confirm.lval[0]); /* sequenceid */ + *tl++ = txdr_unsigned(sep->sess_crflags); + + /* Fore channel attributes. */ + *tl++ = 0; + *tl++ = txdr_unsigned(sep->sess_maxreq); + *tl++ = txdr_unsigned(sep->sess_maxresp); + *tl++ = txdr_unsigned(sep->sess_maxrespcached); + *tl++ = txdr_unsigned(sep->sess_maxops); + *tl++ = txdr_unsigned(sep->sess_maxslots); + *tl++ = txdr_unsigned(1); + *tl++ = txdr_unsigned(0); /* No RDMA. */ + + /* Back channel attributes. */ + *tl++ = 0; + *tl++ = txdr_unsigned(sep->sess_cbmaxreq); + *tl++ = txdr_unsigned(sep->sess_cbmaxresp); + *tl++ = txdr_unsigned(sep->sess_cbmaxrespcached); + *tl++ = txdr_unsigned(sep->sess_cbmaxops); + *tl++ = txdr_unsigned(sep->sess_cbsess.nfsess_foreslots); + *tl++ = txdr_unsigned(1); + *tl = txdr_unsigned(0); /* No RDMA. */ + } +nfsmout: + if (nd->nd_repstat != 0 && sep != NULL) + free(sep, M_NFSDSESSION); + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 sequence service + */ +APPLESTATIC int +nfsrvd_sequence(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + uint32_t highest_slotid, sequenceid, sflags, target_highest_slotid; + int cache_this, error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, NFSX_V4SESSIONID); + NFSBCOPY(tl, nd->nd_sessionid, NFSX_V4SESSIONID); + NFSM_DISSECT(tl, uint32_t *, 4 * NFSX_UNSIGNED); + sequenceid = fxdr_unsigned(uint32_t, *tl++); + nd->nd_slotid = fxdr_unsigned(uint32_t, *tl++); + highest_slotid = fxdr_unsigned(uint32_t, *tl++); + if (*tl == newnfs_true) + cache_this = 1; + else + cache_this = 0; + nd->nd_flag |= ND_HASSEQUENCE; + nd->nd_repstat = nfsrv_checksequence(nd, sequenceid, &highest_slotid, + &target_highest_slotid, cache_this, &sflags, p); + if (nd->nd_repstat == 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID); + NFSBCOPY(nd->nd_sessionid, tl, NFSX_V4SESSIONID); + NFSM_BUILD(tl, uint32_t *, 5 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(sequenceid); + *tl++ = txdr_unsigned(nd->nd_slotid); + *tl++ = txdr_unsigned(highest_slotid); + *tl++ = txdr_unsigned(target_highest_slotid); + *tl = txdr_unsigned(sflags); + } +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 reclaim complete service + */ +APPLESTATIC int +nfsrvd_reclaimcomplete(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + if (*tl == newnfs_true) + nd->nd_repstat = NFSERR_NOTSUPP; + else + nd->nd_repstat = nfsrv_checkreclaimcomplete(nd); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 destroy clientid service + */ +APPLESTATIC int +nfsrvd_destroyclientid(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + nfsquad_t clientid; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + clientid.lval[0] = *tl++; + clientid.lval[1] = *tl; + nd->nd_repstat = nfsrv_destroyclient(clientid, p); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 destroy session service + */ +APPLESTATIC int +nfsrvd_destroysession(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint8_t *cp, sessid[NFSX_V4SESSIONID]; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(cp, uint8_t *, NFSX_V4SESSIONID); + NFSBCOPY(cp, sessid, NFSX_V4SESSIONID); + nd->nd_repstat = nfsrv_destroysession(nd, sessid); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 free stateid service + */ +APPLESTATIC int +nfsrvd_freestateid(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + uint32_t *tl; + nfsv4stateid_t stateid; + int error = 0; + + if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { + nd->nd_repstat = NFSERR_WRONGSEC; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, NFSX_STATEID); + stateid.seqid = fxdr_unsigned(uint32_t, *tl++); + NFSBCOPY(tl, stateid.other, NFSX_STATEIDOTHER); + nd->nd_repstat = nfsrv_freestateid(nd, &stateid, p); +nfsmout: + NFSEXITCODE2(error, nd); + return (error); +} + +/* + * nfsv4 service not supported + */ +APPLESTATIC int +nfsrvd_notsupp(struct nfsrv_descript *nd, __unused int isdgram, + __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) +{ + + nd->nd_repstat = NFSERR_NOTSUPP; + NFSEXITCODE2(0, nd); + return (0); +} + diff --git a/sys/fs/nfsserver/nfs_nfsdsocket.c b/sys/fs/nfsserver/nfs_nfsdsocket.c index bd97336..0ae53ff 100644 --- a/sys/fs/nfsserver/nfs_nfsdsocket.c +++ b/sys/fs/nfsserver/nfs_nfsdsocket.c @@ -48,6 +48,7 @@ extern struct nfsv4lock nfsv4rootfs_lock; extern struct nfsrv_stablefirst nfsrv_stablefirst; extern struct nfsclienthashhead nfsclienthash[NFSCLIENTHASHSIZE]; extern int nfsrc_floodlevel, nfsrc_tcpsavedreplies; +extern int nfsd_debuglevel; NFSV4ROOTLOCKMUTEX; NFSSTATESPINLOCK; @@ -131,7 +132,7 @@ int (*nfsrv3_procs2[NFS_V3NPROCS])(struct nfsrv_descript *, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, }; -int (*nfsrv4_ops0[NFSV4OP_NOPS])(struct nfsrv_descript *, +int (*nfsrv4_ops0[NFSV41_NOPS])(struct nfsrv_descript *, int, vnode_t , NFSPROC_T *, struct nfsexstuff *) = { (int (*)(struct nfsrv_descript *, int, vnode_t , NFSPROC_T *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , NFSPROC_T *, struct nfsexstuff *))0, @@ -173,9 +174,28 @@ int (*nfsrv4_ops0[NFSV4OP_NOPS])(struct nfsrv_descript *, nfsrvd_verify, nfsrvd_write, nfsrvd_releaselckown, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_exchangeid, + nfsrvd_createsession, + nfsrvd_destroysession, + nfsrvd_freestateid, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_sequence, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_notsupp, + nfsrvd_destroyclientid, + nfsrvd_reclaimcomplete, }; -int (*nfsrv4_ops1[NFSV4OP_NOPS])(struct nfsrv_descript *, +int (*nfsrv4_ops1[NFSV41_NOPS])(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *) = { (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, @@ -218,9 +238,28 @@ int (*nfsrv4_ops1[NFSV4OP_NOPS])(struct nfsrv_descript *, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t *, fhandle_t *, NFSPROC_T *, struct nfsexstuff *))0, }; -int (*nfsrv4_ops2[NFSV4OP_NOPS])(struct nfsrv_descript *, +int (*nfsrv4_ops2[NFSV41_NOPS])(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *) = { (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, @@ -263,6 +302,25 @@ int (*nfsrv4_ops2[NFSV4OP_NOPS])(struct nfsrv_descript *, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, + (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , NFSPROC_T *, struct nfsexstuff *, struct nfsexstuff *))0, }; #endif /* !APPLEKEXT */ @@ -304,7 +362,7 @@ static int nfs_writerpc[NFS_NPROCS] = { 0, 0, 1, 0, 0, 0, 0, /* local functions */ static void nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, - NFSPROC_T *p); + u_char *tag, int taglen, u_int32_t minorvers, NFSPROC_T *p); /* @@ -314,7 +372,7 @@ static void nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, static int nfs_retfh[NFS_V3NPROCS] = { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0 }; -extern struct nfsv4_opflag nfsv4_opflag[NFSV4OP_NOPS]; +extern struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS]; static int nfsv3to4op[NFS_V3NPROCS] = { NFSPROC_NULL, @@ -349,8 +407,8 @@ static int nfsv3to4op[NFS_V3NPROCS] = { * The NFS V4 Compound RPC is performed separately by nfsrvd_compound(). */ APPLESTATIC void -nfsrvd_dorpc(struct nfsrv_descript *nd, int isdgram, - NFSPROC_T *p) +nfsrvd_dorpc(struct nfsrv_descript *nd, int isdgram, u_char *tag, int taglen, + u_int32_t minorvers, NFSPROC_T *p) { int error = 0, lktype; vnode_t vp; @@ -427,7 +485,7 @@ nfsrvd_dorpc(struct nfsrv_descript *nd, int isdgram, * The group is indicated by the value in nfs_retfh[]. */ if (nd->nd_flag & ND_NFSV4) { - nfsrvd_compound(nd, isdgram, p); + nfsrvd_compound(nd, isdgram, tag, taglen, minorvers, p); } else { if (nfs_retfh[nd->nd_procnum] == 1) { if (vp) @@ -482,15 +540,14 @@ out: * vnode pointer handling. */ static void -nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, - NFSPROC_T *p) +nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, u_char *tag, + int taglen, u_int32_t minorvers, NFSPROC_T *p) { - int i, op; + int i, op, op0 = 0; u_int32_t *tl; struct nfsclient *clp, *nclp; - int numops, taglen = -1, error = 0, igotlock; - u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp; - u_char tag[NFSV4_SMALLSTR + 1], *tagstr; + int numops, error = 0, igotlock; + u_int32_t retops = 0, *retopsp = NULL, *repp; vnode_t vp, nvp, savevp; struct nfsrvfh fh; mount_t new_mp, temp_mp = NULL; @@ -595,31 +652,17 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, savevp = vp = NULL; save_fsid.val[0] = save_fsid.val[1] = 0; cur_fsid.val[0] = cur_fsid.val[1] = 0; - NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); - taglen = fxdr_unsigned(int, *tl); + + /* If taglen < 0, there was a parsing error in nfsd_getminorvers(). */ if (taglen < 0) { error = EBADRPC; goto nfsmout; } - if (taglen <= NFSV4_SMALLSTR) - tagstr = tag; - else - tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK); - error = nfsrv_mtostr(nd, tagstr, taglen); - if (error) { - if (taglen > NFSV4_SMALLSTR) - free(tagstr, M_TEMP); - taglen = -1; - goto nfsmout; - } + (void) nfsm_strtom(nd, tag, taglen); - if (taglen > NFSV4_SMALLSTR) { - free(tagstr, M_TEMP); - } NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED); - NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); - minorvers = fxdr_unsigned(u_int32_t, *tl++); - if (minorvers != NFSV4_MINORVERSION) + NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); + if (minorvers != NFSV4_MINORVERSION && minorvers != NFSV41_MINORVERSION) nd->nd_repstat = NFSERR_MINORVERMISMATCH; if (nd->nd_repstat) numops = 0; @@ -638,7 +681,10 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED); *repp = *tl; op = fxdr_unsigned(int, *tl); - if (op < NFSV4OP_ACCESS || op >= NFSV4OP_NOPS) { + NFSD_DEBUG(4, "op=%d\n", op); + if (op < NFSV4OP_ACCESS || + (op >= NFSV4OP_NOPS && (nd->nd_flag & ND_NFSV41) == 0) || + (op >= NFSV41_NOPS && (nd->nd_flag & ND_NFSV41) != 0)) { nd->nd_repstat = NFSERR_OPILLEGAL; *repp++ = txdr_unsigned(NFSV4OP_OPILLEGAL); *repp = nfsd_errmap(nd); @@ -647,6 +693,10 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, } else { repp++; } + if (i == 0) + op0 = op; + if (i == numops - 1) + nd->nd_flag |= ND_LASTOP; /* * Check for a referral on the current FH and, if so, return @@ -661,6 +711,29 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, break; } + /* + * For NFSv4.1, check for a Sequence Operation being first + * or one of the other allowed operations by itself. + */ + if ((nd->nd_flag & ND_NFSV41) != 0) { + if (i != 0 && op == NFSV4OP_SEQUENCE) + nd->nd_repstat = NFSERR_SEQUENCEPOS; + else if (i == 0 && op != NFSV4OP_SEQUENCE && + op != NFSV4OP_EXCHANGEID && + op != NFSV4OP_CREATESESSION && + op != NFSV4OP_BINDCONNTOSESS && + op != NFSV4OP_DESTROYCLIENTID && + op != NFSV4OP_DESTROYSESSION) + nd->nd_repstat = NFSERR_OPNOTINSESS; + else if (i != 0 && op0 != NFSV4OP_SEQUENCE) + nd->nd_repstat = NFSERR_NOTONLYOP; + if (nd->nd_repstat != 0) { + *repp = nfsd_errmap(nd); + retops++; + break; + } + } + nd->nd_procnum = op; /* * If over flood level, reply NFSERR_RESOURCE, if at the first @@ -672,7 +745,8 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram, * If nfsrv_mallocmget_limit() returns True, the system is near * to its limit for memory that malloc()/mget() can allocate. */ - if (i == 0 && nd->nd_rp->rc_refcnt == 0 && + if (i == 0 && (nd->nd_rp == NULL || + nd->nd_rp->rc_refcnt == 0) && (nfsrv_mallocmget_limit() || nfsrc_tcpsavedreplies > nfsrc_floodlevel)) { if (nfsrc_tcpsavedreplies > nfsrc_floodlevel) { diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c index 88abfd4..2faf064 100644 --- a/sys/fs/nfsserver/nfs_nfsdstate.c +++ b/sys/fs/nfsserver/nfs_nfsdstate.c @@ -51,6 +51,7 @@ NFSSTATESPINLOCK; */ struct nfsclienthashhead nfsclienthash[NFSCLIENTHASHSIZE]; struct nfslockhashhead nfslockhash[NFSLOCKHASHSIZE]; +struct nfssessionhash nfssessionhash[NFSSESSIONHASHSIZE]; #endif /* !APPLEKEXT */ static u_int32_t nfsrv_openpluslock = 0, nfsrv_delegatecnt = 0; @@ -89,10 +90,13 @@ static void nfsrv_updatelock(struct nfsstate *stp, struct nfslock **new_lopp, static int nfsrv_getipnumber(u_char *cp); static int nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, nfsv4stateid_t *stateidp, int specialid); -static int nfsrv_checkgrace(u_int32_t flags); +static int nfsrv_checkgrace(struct nfsrv_descript *nd, struct nfsclient *clp, + u_int32_t flags); static int nfsrv_docallback(struct nfsclient *clp, int procnum, nfsv4stateid_t *stateidp, int trunc, fhandle_t *fhp, struct nfsvattr *nap, nfsattrbit_t *attrbitp, NFSPROC_T *p); +static int nfsrv_cbcallargs(struct nfsrv_descript *nd, struct nfsclient *clp, + uint32_t callback, int op, const char *optag, struct nfsdsession **sepp); static u_int32_t nfsrv_nextclientindex(void); static u_int32_t nfsrv_nextstateindex(struct nfsclient *clp); static void nfsrv_markstable(struct nfsclient *clp); @@ -123,6 +127,11 @@ static void nfsrv_locallock_commit(struct nfslockfile *lfp, int flags, uint64_t first, uint64_t end); static void nfsrv_locklf(struct nfslockfile *lfp); static void nfsrv_unlocklf(struct nfslockfile *lfp); +static struct nfsdsession *nfsrv_findsession(uint8_t *sessionid); +static int nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid); +static int nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, + int dont_replycache, struct nfsdsession **sepp); +static int nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp); /* * Scan the client list for a match and either return the current one, @@ -191,6 +200,18 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, } if (!gotit || (clp->lc_flags & (LCL_NEEDSCONFIRM | LCL_ADMINREVOKED))) { + if ((nd->nd_flag & ND_NFSV41) != 0 && confirmp->lval[1] != 0) { + /* + * For NFSv4.1, if confirmp->lval[1] is non-zero, the + * client is trying to update a confirmed clientid. + */ + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + confirmp->lval[1] = 0; + error = NFSERR_NOENT; + goto out; + } /* * Get rid of the old one. */ @@ -205,7 +226,12 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, * Add it after assigning a client id to it. */ new_clp->lc_flags |= LCL_NEEDSCONFIRM; - confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; + if ((nd->nd_flag & ND_NFSV41) != 0) + new_clp->lc_confirm.lval[0] = confirmp->lval[0] = + ++confirm_index; + else + confirmp->qval = new_clp->lc_confirm.qval = + ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = (u_int32_t)nfsrvboottime; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = @@ -217,6 +243,7 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, LIST_INIT(&new_clp->lc_open); LIST_INIT(&new_clp->lc_deleg); LIST_INIT(&new_clp->lc_olddeleg); + LIST_INIT(&new_clp->lc_session); for (i = 0; i < NFSSTATEHASHSIZE; i++) LIST_INIT(&new_clp->lc_stateid[i]); LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, @@ -289,7 +316,12 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, */ LIST_REMOVE(clp, lc_hash); new_clp->lc_flags |= LCL_NEEDSCONFIRM; - confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; + if ((nd->nd_flag & ND_NFSV41) != 0) + new_clp->lc_confirm.lval[0] = confirmp->lval[0] = + ++confirm_index; + else + confirmp->qval = new_clp->lc_confirm.qval = + ++confirm_index; clientidp->lval[0] = new_clp->lc_clientid.lval[0] = nfsrvboottime; clientidp->lval[1] = new_clp->lc_clientid.lval[1] = @@ -342,60 +374,68 @@ nfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, *new_clpp = NULL; goto out; } - /* - * id and verifier match, so update the net address info - * and get rid of any existing callback authentication - * handle, so a new one will be acquired. - */ - LIST_REMOVE(clp, lc_hash); - new_clp->lc_flags |= (LCL_NEEDSCONFIRM | LCL_DONTCLEAN); - new_clp->lc_expiry = nfsrv_leaseexpiry(); - confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; - clientidp->lval[0] = new_clp->lc_clientid.lval[0] = - clp->lc_clientid.lval[0]; - clientidp->lval[1] = new_clp->lc_clientid.lval[1] = - clp->lc_clientid.lval[1]; - new_clp->lc_delegtime = clp->lc_delegtime; - new_clp->lc_stateindex = clp->lc_stateindex; - new_clp->lc_statemaxindex = clp->lc_statemaxindex; - new_clp->lc_cbref = 0; - LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); - LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) - tstp->ls_clp = new_clp; - LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); - LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) - tstp->ls_clp = new_clp; - LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); - LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) - tstp->ls_clp = new_clp; - for (i = 0; i < NFSSTATEHASHSIZE; i++) { - LIST_NEWHEAD(&new_clp->lc_stateid[i], &clp->lc_stateid[i], - ls_hash); - LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) + + /* For NFSv4.1, mark that we found a confirmed clientid. */ + if ((nd->nd_flag & ND_NFSV41) != 0) + confirmp->lval[1] = 1; + else { + /* + * id and verifier match, so update the net address info + * and get rid of any existing callback authentication + * handle, so a new one will be acquired. + */ + LIST_REMOVE(clp, lc_hash); + new_clp->lc_flags |= (LCL_NEEDSCONFIRM | LCL_DONTCLEAN); + new_clp->lc_expiry = nfsrv_leaseexpiry(); + confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; + clientidp->lval[0] = new_clp->lc_clientid.lval[0] = + clp->lc_clientid.lval[0]; + clientidp->lval[1] = new_clp->lc_clientid.lval[1] = + clp->lc_clientid.lval[1]; + new_clp->lc_delegtime = clp->lc_delegtime; + new_clp->lc_stateindex = clp->lc_stateindex; + new_clp->lc_statemaxindex = clp->lc_statemaxindex; + new_clp->lc_cbref = 0; + LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); + LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) tstp->ls_clp = new_clp; + LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); + LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) + tstp->ls_clp = new_clp; + LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); + LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) + tstp->ls_clp = new_clp; + for (i = 0; i < NFSSTATEHASHSIZE; i++) { + LIST_NEWHEAD(&new_clp->lc_stateid[i], + &clp->lc_stateid[i], ls_hash); + LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) + tstp->ls_clp = new_clp; + } + LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, + lc_hash); + newnfsstats.srvclients++; + nfsrv_openpluslock++; + nfsrv_clients++; } - LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, - lc_hash); - newnfsstats.srvclients++; - nfsrv_openpluslock++; - nfsrv_clients++; NFSLOCKV4ROOTMUTEX(); nfsv4_unlock(&nfsv4rootfs_lock, 1); NFSUNLOCKV4ROOTMUTEX(); - /* - * Must wait until any outstanding callback on the old clp - * completes. - */ - NFSLOCKSTATE(); - while (clp->lc_cbref) { - clp->lc_flags |= LCL_WAKEUPWANTED; - (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, "nfsd clp", - 10 * hz); + if ((nd->nd_flag & ND_NFSV41) == 0) { + /* + * Must wait until any outstanding callback on the old clp + * completes. + */ + NFSLOCKSTATE(); + while (clp->lc_cbref) { + clp->lc_flags |= LCL_WAKEUPWANTED; + (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, + "nfsdclp", 10 * hz); + } + NFSUNLOCKSTATE(); + nfsrv_zapclient(clp, p); + *new_clpp = NULL; } - NFSUNLOCKSTATE(); - nfsrv_zapclient(clp, p); - *new_clpp = NULL; out: NFSEXITCODE2(error, nd); @@ -407,17 +447,23 @@ out: */ APPLESTATIC int nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, - nfsquad_t confirm, struct nfsrv_descript *nd, NFSPROC_T *p) + struct nfsdsession *nsep, nfsquad_t confirm, uint32_t cbprogram, + struct nfsrv_descript *nd, NFSPROC_T *p) { struct nfsclient *clp; struct nfsstate *stp; int i; struct nfsclienthashhead *hp; int error = 0, igotlock, doneok; + struct nfssessionhash *shp; + struct nfsdsession *sep; + uint64_t sessid[2]; + static uint64_t next_sess = 0; if (clpp) *clpp = NULL; - if (nfsrvboottime != clientid.lval[0]) { + if ((nd == NULL || (nd->nd_flag & ND_NFSV41) == 0 || + opflags != CLOPS_RENEW) && nfsrvboottime != clientid.lval[0]) { error = NFSERR_STALECLIENTID; goto out; } @@ -434,17 +480,39 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); } while (!igotlock); + /* + * Create a new sessionid here, since we need to do it where + * there is a mutex held to serialize update of next_sess. + */ + if ((nd->nd_flag & ND_NFSV41) != 0) { + sessid[0] = ++next_sess; + sessid[1] = clientid.qval; + } NFSUNLOCKV4ROOTMUTEX(); } else if (opflags != CLOPS_RENEW) { NFSLOCKSTATE(); } - hp = NFSCLIENTHASH(clientid); - LIST_FOREACH(clp, hp, lc_hash) { - if (clp->lc_clientid.lval[1] == clientid.lval[1]) - break; + /* For NFSv4.1, the clp is acquired from the associated session. */ + if (nd != NULL && (nd->nd_flag & ND_NFSV41) != 0 && + opflags == CLOPS_RENEW) { + clp = NULL; + if ((nd->nd_flag & ND_HASSEQUENCE) != 0) { + shp = NFSSESSIONHASH(nd->nd_sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(nd->nd_sessionid); + if (sep != NULL) + clp = sep->sess_clp; + NFSUNLOCKSESSION(shp); + } + } else { + hp = NFSCLIENTHASH(clientid); + LIST_FOREACH(clp, hp, lc_hash) { + if (clp->lc_clientid.lval[1] == clientid.lval[1]) + break; + } } - if (clp == LIST_END(hp)) { + if (clp == NULL) { if (opflags & CLOPS_CONFIRM) error = NFSERR_STALECLIENTID; else @@ -470,7 +538,10 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, * Perform any operations specified by the opflags. */ if (opflags & CLOPS_CONFIRM) { - if (clp->lc_confirm.qval != confirm.qval) + if (((nd->nd_flag & ND_NFSV41) != 0 && + clp->lc_confirm.lval[0] != confirm.lval[0]) || + ((nd->nd_flag & ND_NFSV41) == 0 && + clp->lc_confirm.qval != confirm.qval)) error = NFSERR_STALECLIENTID; else if (nfsrv_notsamecredname(nd, clp)) error = NFSERR_CLIDINUSE; @@ -485,7 +556,7 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, */ nfsrv_cleanclient(clp, p); nfsrv_freedeleglist(&clp->lc_olddeleg); - if (nfsrv_checkgrace(0)) { + if (nfsrv_checkgrace(nd, clp, 0)) { /* In grace, so just delete delegations */ nfsrv_freedeleglist(&clp->lc_deleg); } else { @@ -496,10 +567,43 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, LIST_NEWHEAD(&clp->lc_olddeleg, &clp->lc_deleg, ls_list); } + if ((nd->nd_flag & ND_NFSV41) != 0) + clp->lc_program = cbprogram; } clp->lc_flags &= ~(LCL_NEEDSCONFIRM | LCL_DONTCLEAN); if (clp->lc_program) clp->lc_flags |= LCL_NEEDSCBNULL; + /* For NFSv4.1, link the session onto the client. */ + if (nsep != NULL) { + /* Hold a reference on the xprt for a backchannel. */ + if ((nsep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) + != 0 && clp->lc_req.nr_client == NULL) { + clp->lc_req.nr_client = (struct __rpc_client *) + clnt_bck_create(nd->nd_xprt->xp_socket, + cbprogram, NFSV4_CBVERS); + if (clp->lc_req.nr_client != NULL) { + SVC_ACQUIRE(nd->nd_xprt); + nd->nd_xprt->xp_p2 = + clp->lc_req.nr_client->cl_private; + /* Disable idle timeout. */ + nd->nd_xprt->xp_idletimeout = 0; + nsep->sess_cbsess.nfsess_xprt = nd->nd_xprt; + } else + nsep->sess_crflags &= ~NFSV4CRSESS_CONNBACKCHAN; + } + NFSBCOPY(sessid, nsep->sess_sessionid, + NFSX_V4SESSIONID); + NFSBCOPY(sessid, nsep->sess_cbsess.nfsess_sessionid, + NFSX_V4SESSIONID); + shp = NFSSESSIONHASH(nsep->sess_sessionid); + NFSLOCKSESSION(shp); + LIST_INSERT_HEAD(&shp->list, nsep, sess_hash); + NFSLOCKSTATE(); + LIST_INSERT_HEAD(&clp->lc_session, nsep, sess_list); + nsep->sess_clp = clp; + NFSUNLOCKSTATE(); + NFSUNLOCKSESSION(shp); + } } } else if (clp->lc_flags & LCL_NEEDSCONFIRM) { error = NFSERR_EXPIRED; @@ -546,6 +650,74 @@ out: } /* + * Perform the NFSv4.1 destroy clientid. + */ +int +nfsrv_destroyclient(nfsquad_t clientid, NFSPROC_T *p) +{ + struct nfsclient *clp; + struct nfsclienthashhead *hp; + int error = 0, i, igotlock; + + if (nfsrvboottime != clientid.lval[0]) { + error = NFSERR_STALECLIENTID; + goto out; + } + + /* Lock out other nfsd threads */ + NFSLOCKV4ROOTMUTEX(); + nfsv4_relref(&nfsv4rootfs_lock); + do { + igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, + NFSV4ROOTLOCKMUTEXPTR, NULL); + } while (igotlock == 0); + NFSUNLOCKV4ROOTMUTEX(); + + hp = NFSCLIENTHASH(clientid); + LIST_FOREACH(clp, hp, lc_hash) { + if (clp->lc_clientid.lval[1] == clientid.lval[1]) + break; + } + if (clp == NULL) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + /* Just return ok, since it is gone. */ + goto out; + } + + /* Scan for state on the clientid. */ + for (i = 0; i < NFSSTATEHASHSIZE; i++) + if (!LIST_EMPTY(&clp->lc_stateid[i])) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + error = NFSERR_CLIENTIDBUSY; + goto out; + } + if (!LIST_EMPTY(&clp->lc_session) || !LIST_EMPTY(&clp->lc_deleg)) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + error = NFSERR_CLIENTIDBUSY; + goto out; + } + + /* Destroy the clientid and return ok. */ + nfsrv_cleanclient(clp, p); + nfsrv_freedeleglist(&clp->lc_deleg); + nfsrv_freedeleglist(&clp->lc_olddeleg); + LIST_REMOVE(clp, lc_hash); + NFSLOCKV4ROOTMUTEX(); + nfsv4_unlock(&nfsv4rootfs_lock, 1); + NFSUNLOCKV4ROOTMUTEX(); + nfsrv_zapclient(clp, p); +out: + NFSEXITCODE2(error, nd); + return (error); +} + +/* * Called from the new nfssvc syscall to admin revoke a clientid. * Returns 0 for success, error otherwise. */ @@ -983,9 +1155,13 @@ APPLESTATIC void nfsrv_cleanclient(struct nfsclient *clp, NFSPROC_T *p) { struct nfsstate *stp, *nstp; + struct nfsdsession *sep, *nsep; LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) nfsrv_freeopenowner(stp, 1, p); + if ((clp->lc_flags & LCL_ADMINREVOKED) == 0) + LIST_FOREACH_SAFE(sep, &clp->lc_session, sess_list, nsep) + (void)nfsrv_freesession(sep, NULL); } /* @@ -1425,8 +1601,8 @@ tryagain: * lease, but the concensus seems to be that it is ok * for a server to do so. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); /* * Since NFSERR_EXPIRED, NFSERR_ADMINREVOKED are not valid @@ -1438,8 +1614,8 @@ tryagain: error = 0; lckstp = new_stp; } else { - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error == 0) /* * Look up the stateid @@ -1528,8 +1704,11 @@ tryagain: * allow for either server configuration.) */ if (!error && stp->ls_stateid.seqid!=new_stp->ls_stateid.seqid && - (!(new_stp->ls_flags & NFSLCK_CHECK) || - nfsrv_returnoldstateid)) + (((nd->nd_flag & ND_NFSV41) == 0 && + (!(new_stp->ls_flags & NFSLCK_CHECK) || + nfsrv_returnoldstateid)) || + ((nd->nd_flag & ND_NFSV41) != 0 && + new_stp->ls_stateid.seqid != 0))) error = NFSERR_OLDSTATEID; } } @@ -1538,7 +1717,7 @@ tryagain: * Now we can check for grace. */ if (!error) - error = nfsrv_checkgrace(new_stp->ls_flags); + error = nfsrv_checkgrace(nd, clp, new_stp->ls_flags); if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && nfsrv_checkstable(clp)) error = NFSERR_NOGRACE; @@ -1785,6 +1964,8 @@ tryagain: end = new_lop->lo_end; nfsrv_updatelock(stp, new_lopp, &other_lop, lfp); stateidp->seqid = ++(stp->ls_stateid.seqid); + if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) + stateidp->seqid = stp->ls_stateid.seqid = 1; stateidp->other[0] = stp->ls_stateid.other[0]; stateidp->other[1] = stp->ls_stateid.other[1]; stateidp->other[2] = stp->ls_stateid.other[2]; @@ -1892,6 +2073,8 @@ tryagain: if (!(new_stp->ls_flags & NFSLCK_OPENTOLOCK)) { nfsrv_updatelock(lckstp, new_lopp, &other_lop, lfp); stateidp->seqid = ++(lckstp->ls_stateid.seqid); + if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) + stateidp->seqid = lckstp->ls_stateid.seqid = 1; stateidp->other[0] = lckstp->ls_stateid.other[0]; stateidp->other[1] = lckstp->ls_stateid.other[1]; stateidp->other[2] = lckstp->ls_stateid.other[2]; @@ -1991,8 +2174,8 @@ tryagain: /* * Get the nfsclient structure. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); /* * Look up the open owner. See if it needs confirmation and @@ -2018,7 +2201,7 @@ tryagain: * Check for grace. */ if (!error) - error = nfsrv_checkgrace(new_stp->ls_flags); + error = nfsrv_checkgrace(nd, clp, new_stp->ls_flags); if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && nfsrv_checkstable(clp)) error = NFSERR_NOGRACE; @@ -2084,7 +2267,9 @@ tryagain: */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (!(stp->ls_flags & NFSLCK_OLDDELEG) && - stateidp->seqid == stp->ls_stateid.seqid && + (((nd->nd_flag & ND_NFSV41) != 0 && + stateidp->seqid == 0) || + stateidp->seqid == stp->ls_stateid.seqid) && !NFSBCMP(stateidp->other, stp->ls_stateid.other, NFSX_STATEIDOTHER)) break; @@ -2252,8 +2437,8 @@ tryagain: * storage file must be written prior to completion of state * expiration. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (!error && (clp->lc_flags & LCL_NEEDSCBNULL) && clp->lc_program) { /* @@ -2341,7 +2526,9 @@ tryagain: */ LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { if (!(stp->ls_flags & NFSLCK_OLDDELEG) && - stateidp->seqid == stp->ls_stateid.seqid && + (((nd->nd_flag & ND_NFSV41) != 0 && + stateidp->seqid == 0) || + stateidp->seqid == stp->ls_stateid.seqid) && !NFSBCMP(stateidp->other, stp->ls_stateid.other, NFSX_STATEIDOTHER)) break; @@ -2506,7 +2693,7 @@ tryagain: LIST_REMOVE(stp, ls_list); LIST_REMOVE(stp, ls_hash); stp->ls_flags &= ~NFSLCK_OLDDELEG; - stp->ls_stateid.seqid = delegstateidp->seqid = 0; + stp->ls_stateid.seqid = delegstateidp->seqid = 1; stp->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; stp->ls_stateid.other[1] = delegstateidp->other[1] = @@ -2527,7 +2714,7 @@ tryagain: /* * Now, do the associated open. */ - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2592,7 +2779,7 @@ tryagain: * First, add the delegation. (Although we must issue the * delegation, we can also ask for an immediate return.) */ - new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; + new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = @@ -2631,7 +2818,7 @@ tryagain: /* * Now, do the associated open. */ - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2686,7 +2873,7 @@ tryagain: stp = LIST_FIRST(&ownerstp->ls_open); stp->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS) | NFSLCK_OPEN; - stp->ls_stateid.seqid = 0; + stp->ls_stateid.seqid = 1; stp->ls_uid = new_stp->ls_uid; if (lfp != stp->ls_lfp) { LIST_REMOVE(stp, ls_file); @@ -2697,18 +2884,28 @@ tryagain: } else if (openstp) { openstp->ls_flags |= (new_stp->ls_flags & NFSLCK_SHAREBITS); openstp->ls_stateid.seqid++; + if ((nd->nd_flag & ND_NFSV41) != 0 && + openstp->ls_stateid.seqid == 0) + openstp->ls_stateid.seqid = 1; /* * This is where we can choose to issue a delegation. */ - if (delegate && nfsrv_issuedelegs && - writedeleg && !NFSVNO_EXRDONLY(exp) && - (nfsrv_writedelegifpos || !readonly) && - (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == - LCL_CALLBACKSON && - !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && - NFSVNO_DELEGOK(vp)) { - new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; + if (delegate == 0 || writedeleg == 0 || + NFSVNO_EXRDONLY(exp) || (readonly != 0 && + nfsrv_writedelegifpos == 0) || + !NFSVNO_DELEGOK(vp) || + (new_stp->ls_flags & NFSLCK_WANTRDELEG) != 0 || + (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != + LCL_CALLBACKSON) + *rflagsp |= NFSV4OPEN_WDCONTENTION; + else if (nfsrv_issuedelegs == 0 || + NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt)) + *rflagsp |= NFSV4OPEN_WDRESOURCE; + else if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0) + *rflagsp |= NFSV4OPEN_WDNOTWANTED; + else { + new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] @@ -2733,7 +2930,7 @@ tryagain: nfsrv_delegatecnt++; } } else { - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2756,13 +2953,18 @@ tryagain: /* * This is where we can choose to issue a delegation. */ - if (delegate && nfsrv_issuedelegs && - (writedeleg || readonly) && - (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == - LCL_CALLBACKSON && - !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && - NFSVNO_DELEGOK(vp)) { - new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; + if (delegate == 0 || (writedeleg == 0 && readonly == 0) || + !NFSVNO_DELEGOK(vp) || + (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != + LCL_CALLBACKSON) + *rflagsp |= NFSV4OPEN_WDCONTENTION; + else if (nfsrv_issuedelegs == 0 || + NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt)) + *rflagsp |= NFSV4OPEN_WDRESOURCE; + else if ((new_stp->ls_flags & NFSLCK_WANTNODELEG) != 0) + *rflagsp |= NFSV4OPEN_WDNOTWANTED; + else { + new_deleg->ls_stateid.seqid = delegstateidp->seqid = 1; new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = clp->lc_clientid.lval[0]; new_deleg->ls_stateid.other[1] = delegstateidp->other[1] @@ -2770,7 +2972,8 @@ tryagain: new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = nfsrv_nextstateindex(clp); if (writedeleg && !NFSVNO_EXRDONLY(exp) && - (nfsrv_writedelegifpos || !readonly)) { + (nfsrv_writedelegifpos || !readonly) && + (new_stp->ls_flags & NFSLCK_WANTRDELEG) == 0) { new_deleg->ls_flags = (NFSLCK_DELEGWRITE | NFSLCK_READACCESS | NFSLCK_WRITEACCESS); *rflagsp |= NFSV4OPEN_WRITEDELEGATE; @@ -2800,7 +3003,7 @@ tryagain: * Needs confirmation (unless a reclaim) and hang the * new open off it. */ - new_open->ls_stateid.seqid = 0; + new_open->ls_stateid.seqid = 1; new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); @@ -2814,6 +3017,64 @@ tryagain: LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); if (new_stp->ls_flags & NFSLCK_RECLAIM) { new_stp->ls_flags = 0; + } else if ((nd->nd_flag & ND_NFSV41) != 0) { + /* NFSv4.1 never needs confirmation. */ + new_stp->ls_flags = 0; + + /* + * This is where we can choose to issue a delegation. + */ + if (delegate && nfsrv_issuedelegs && + (writedeleg || readonly) && + (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == + LCL_CALLBACKSON && + !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && + NFSVNO_DELEGOK(vp) && + ((nd->nd_flag & ND_NFSV41) == 0 || + (new_stp->ls_flags & NFSLCK_WANTNODELEG) == 0)) { + new_deleg->ls_stateid.seqid = + delegstateidp->seqid = 1; + new_deleg->ls_stateid.other[0] = + delegstateidp->other[0] + = clp->lc_clientid.lval[0]; + new_deleg->ls_stateid.other[1] = + delegstateidp->other[1] + = clp->lc_clientid.lval[1]; + new_deleg->ls_stateid.other[2] = + delegstateidp->other[2] + = nfsrv_nextstateindex(clp); + if (writedeleg && !NFSVNO_EXRDONLY(exp) && + (nfsrv_writedelegifpos || !readonly) && + ((nd->nd_flag & ND_NFSV41) == 0 || + (new_stp->ls_flags & NFSLCK_WANTRDELEG) == + 0)) { + new_deleg->ls_flags = + (NFSLCK_DELEGWRITE | + NFSLCK_READACCESS | + NFSLCK_WRITEACCESS); + *rflagsp |= NFSV4OPEN_WRITEDELEGATE; + } else { + new_deleg->ls_flags = + (NFSLCK_DELEGREAD | + NFSLCK_READACCESS); + *rflagsp |= NFSV4OPEN_READDELEGATE; + } + new_deleg->ls_uid = new_stp->ls_uid; + new_deleg->ls_lfp = lfp; + new_deleg->ls_clp = clp; + new_deleg->ls_filerev = filerev; + new_deleg->ls_compref = nd->nd_compref; + LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, + ls_file); + LIST_INSERT_HEAD(NFSSTATEHASH(clp, + new_deleg->ls_stateid), new_deleg, ls_hash); + LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, + ls_list); + new_deleg = NULL; + newnfsstats.srvdelegates++; + nfsrv_openpluslock++; + nfsrv_delegatecnt++; + } } else { *rflagsp |= NFSV4OPEN_RESULTCONFIRM; new_stp->ls_flags = NFSLCK_NEEDSCONFIRM; @@ -2881,8 +3142,8 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, /* * Get the open structure via clientid and stateid. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (!error) error = nfsrv_getstate(clp, &new_stp->ls_stateid, new_stp->ls_flags, &stp); @@ -2901,7 +3162,10 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, error = nfsrv_checkseqid(nd, new_stp->ls_seq, stp->ls_openowner, new_stp->ls_op); if (!error && stp->ls_stateid.seqid != new_stp->ls_stateid.seqid && - !(new_stp->ls_flags & NFSLCK_CONFIRM)) + (((nd->nd_flag & ND_NFSV41) == 0 && + !(new_stp->ls_flags & NFSLCK_CONFIRM)) || + ((nd->nd_flag & ND_NFSV41) != 0 && + new_stp->ls_stateid.seqid != 0))) error = NFSERR_OLDSTATEID; if (!error && vnode_vtype(vp) != VREG) { if (vnode_vtype(vp) == VDIR) @@ -2929,6 +3193,8 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, * Set the return stateid. */ stateidp->seqid = stp->ls_stateid.seqid + 1; + if ((nd->nd_flag & ND_NFSV41) != 0 && stateidp->seqid == 0) + stateidp->seqid = 1; stateidp->other[0] = stp->ls_stateid.other[0]; stateidp->other[1] = stp->ls_stateid.other[1]; stateidp->other[2] = stp->ls_stateid.other[2]; @@ -2944,6 +3210,9 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, printf("Nfsv4d: stray open confirm\n"); stp->ls_openowner->ls_flags = 0; stp->ls_stateid.seqid++; + if ((nd->nd_flag & ND_NFSV41) != 0 && + stp->ls_stateid.seqid == 0) + stp->ls_stateid.seqid = 1; if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) { clp->lc_flags |= LCL_STAMPEDSTABLE; len = clp->lc_idlen; @@ -2980,6 +3249,9 @@ nfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, } stp->ls_flags = (bits | NFSLCK_OPEN); stp->ls_stateid.seqid++; + if ((nd->nd_flag & ND_NFSV41) != 0 && + stp->ls_stateid.seqid == 0) + stp->ls_stateid.seqid = 1; NFSUNLOCKSTATE(); } @@ -3001,8 +3273,9 @@ out: * Delegation update. Does the purge and return. */ APPLESTATIC int -nfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, - vnode_t vp, int op, struct ucred *cred, NFSPROC_T *p) +nfsrv_delegupdate(struct nfsrv_descript *nd, nfsquad_t clientid, + nfsv4stateid_t *stateidp, vnode_t vp, int op, struct ucred *cred, + NFSPROC_T *p) { struct nfsstate *stp; struct nfsclient *clp; @@ -3032,8 +3305,8 @@ nfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, * Get the open structure via clientid and stateid. */ if (!error) - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, nd, p); if (error) { if (error == NFSERR_CBPATHDOWN) error = 0; @@ -3042,7 +3315,8 @@ nfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, } if (!error && op == NFSV4OP_DELEGRETURN) { error = nfsrv_getstate(clp, stateidp, NFSLCK_DELEGRETURN, &stp); - if (!error && stp->ls_stateid.seqid != stateidp->seqid) + if (!error && stp->ls_stateid.seqid != stateidp->seqid && + ((nd->nd_flag & ND_NFSV41) == 0 || stateidp->seqid != 0)) error = NFSERR_OLDSTATEID; } /* @@ -3101,8 +3375,8 @@ nfsrv_releaselckown(struct nfsstate *new_stp, nfsquad_t clientid, /* * Get the lock owner by name. */ - error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, - (nfsquad_t)((u_quad_t)0), NULL, p); + error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, NULL, + (nfsquad_t)((u_quad_t)0), 0, NULL, p); if (error) { NFSUNLOCKSTATE(); goto out; @@ -3419,6 +3693,9 @@ nfsrv_checkseqid(struct nfsrv_descript *nd, u_int32_t seqid, { int error = 0; + if ((nd->nd_flag & ND_NFSV41) != 0) + /* NFSv4.1 ignores the open_seqid and lock_seqid. */ + goto out; if (op != nd->nd_rp) panic("nfsrvstate checkseqid"); if (!(op->rc_flag & RC_INPROG)) @@ -3637,7 +3914,7 @@ nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, goto out; NFSLOCKSTATE(); - ret = nfsrv_checkgrace(flags); + ret = nfsrv_checkgrace(NULL, NULL, flags); NFSUNLOCKSTATE(); out: @@ -3649,11 +3926,12 @@ out: * Check for grace. */ static int -nfsrv_checkgrace(u_int32_t flags) +nfsrv_checkgrace(struct nfsrv_descript *nd, struct nfsclient *clp, + u_int32_t flags) { int error = 0; - if (nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) { + if ((nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) != 0) { if (flags & NFSLCK_RECLAIM) { error = NFSERR_NOGRACE; goto out; @@ -3663,6 +3941,12 @@ nfsrv_checkgrace(u_int32_t flags) error = NFSERR_GRACE; goto out; } + if (nd != NULL && clp != NULL && + (nd->nd_flag & ND_NFSV41) != 0 && + (clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0) { + error = NFSERR_NOGRACE; + goto out; + } /* * If grace is almost over and we are still getting Reclaims, @@ -3693,6 +3977,7 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, struct ucred *cred; int error = 0; u_int32_t callback; + struct nfsdsession *sep = NULL; cred = newnfs_getcred(); NFSLOCKSTATE(); /* mostly for lc_cbref++ */ @@ -3707,7 +3992,12 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, * structure for newnfs_connect() to use. */ clp->lc_req.nr_prog = clp->lc_program; - clp->lc_req.nr_vers = NFSV4_CBVERS; +#ifdef notnow + if ((clp->lc_flags & LCL_NFSV41) != 0) + clp->lc_req.nr_vers = NFSV41_CBVERS; + else +#endif + clp->lc_req.nr_vers = NFSV4_CBVERS; /* * First, fill in some of the fields of nd and cr. @@ -3715,6 +4005,8 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, nd->nd_flag = ND_NFSV4; if (clp->lc_flags & LCL_GSS) nd->nd_flag |= ND_KERBV; + if ((clp->lc_flags & LCL_NFSV41) != 0) + nd->nd_flag |= ND_NFSV41; nd->nd_repstat = 0; cred->cr_uid = clp->lc_uid; cred->cr_gid = clp->lc_gid; @@ -3735,22 +4027,23 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, */ if (procnum == NFSV4OP_CBGETATTR) { nd->nd_procnum = NFSV4PROC_CBCOMPOUND; - (void) nfsm_strtom(nd, "CB Getattr", 10); - NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED); - *tl++ = txdr_unsigned(NFSV4_MINORVERSION); - *tl++ = txdr_unsigned(callback); - *tl++ = txdr_unsigned(1); - *tl = txdr_unsigned(NFSV4OP_CBGETATTR); - (void) nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); - (void) nfsrv_putattrbit(nd, attrbitp); + error = nfsrv_cbcallargs(nd, clp, callback, NFSV4OP_CBGETATTR, + "CB Getattr", &sep); + if (error != 0) { + mbuf_freem(nd->nd_mreq); + goto errout; + } + (void)nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); + (void)nfsrv_putattrbit(nd, attrbitp); } else if (procnum == NFSV4OP_CBRECALL) { nd->nd_procnum = NFSV4PROC_CBCOMPOUND; - (void) nfsm_strtom(nd, "CB Recall", 9); - NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID); - *tl++ = txdr_unsigned(NFSV4_MINORVERSION); - *tl++ = txdr_unsigned(callback); - *tl++ = txdr_unsigned(1); - *tl++ = txdr_unsigned(NFSV4OP_CBRECALL); + error = nfsrv_cbcallargs(nd, clp, callback, NFSV4OP_CBRECALL, + "CB Recall", &sep); + if (error != 0) { + mbuf_freem(nd->nd_mreq); + goto errout; + } + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID); *tl++ = txdr_unsigned(stateidp->seqid); NFSBCOPY((caddr_t)stateidp->other, (caddr_t)tl, NFSX_STATEIDOTHER); @@ -3759,9 +4052,20 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, *tl = newnfs_true; else *tl = newnfs_false; - (void) nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); - } else { + (void)nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); + } else if (procnum == NFSV4PROC_CBNULL) { nd->nd_procnum = NFSV4PROC_CBNULL; + if ((clp->lc_flags & LCL_NFSV41) != 0) { + error = nfsv4_getcbsession(clp, &sep); + if (error != 0) { + mbuf_freem(nd->nd_mreq); + goto errout; + } + } + } else { + error = NFSERR_SERVERFAULT; + mbuf_freem(nd->nd_mreq); + goto errout; } /* @@ -3769,7 +4073,9 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, */ (void) newnfs_sndlock(&clp->lc_req.nr_lock); if (clp->lc_req.nr_client == NULL) { - if (nd->nd_procnum == NFSV4PROC_CBNULL) + if ((clp->lc_flags & LCL_NFSV41) != 0) + error = ECONNREFUSED; + else if (nd->nd_procnum == NFSV4PROC_CBNULL) error = newnfs_connect(NULL, &clp->lc_req, cred, NULL, 1); else @@ -3778,10 +4084,19 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, } newnfs_sndunlock(&clp->lc_req.nr_lock); if (!error) { - error = newnfs_request(nd, NULL, clp, &clp->lc_req, NULL, - NULL, cred, clp->lc_program, NFSV4_CBVERS, NULL, 1, NULL, - NULL); + if ((nd->nd_flag & ND_NFSV41) != 0) { + KASSERT(sep != NULL, ("sep NULL")); + error = newnfs_request(nd, NULL, clp, &clp->lc_req, + NULL, NULL, cred, clp->lc_program, + clp->lc_req.nr_vers, NULL, 1, NULL, + &sep->sess_cbsess); + nfsrv_freesession(sep, NULL); + } else + error = newnfs_request(nd, NULL, clp, &clp->lc_req, + NULL, NULL, cred, clp->lc_program, + clp->lc_req.nr_vers, NULL, 1, NULL, NULL); } +errout: NFSFREECRED(cred); /* @@ -3811,7 +4126,7 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, NFSUNLOCKSTATE(); if (nd->nd_repstat) error = nd->nd_repstat; - else if (procnum == NFSV4OP_CBGETATTR) + else if (error == 0 && procnum == NFSV4OP_CBGETATTR) error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, p, NULL); @@ -3830,6 +4145,38 @@ nfsrv_docallback(struct nfsclient *clp, int procnum, } /* + * Set up the compound RPC for the callback. + */ +static int +nfsrv_cbcallargs(struct nfsrv_descript *nd, struct nfsclient *clp, + uint32_t callback, int op, const char *optag, struct nfsdsession **sepp) +{ + uint32_t *tl; + int error, len; + + len = strlen(optag); + (void)nfsm_strtom(nd, optag, len); + NFSM_BUILD(tl, uint32_t *, 4 * NFSX_UNSIGNED); + if ((nd->nd_flag & ND_NFSV41) != 0) { + *tl++ = txdr_unsigned(NFSV41_MINORVERSION); + *tl++ = txdr_unsigned(callback); + *tl++ = txdr_unsigned(2); + *tl = txdr_unsigned(NFSV4OP_CBSEQUENCE); + error = nfsv4_setcbsequence(nd, clp, 1, sepp); + if (error != 0) + return (error); + NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(op); + } else { + *tl++ = txdr_unsigned(NFSV4_MINORVERSION); + *tl++ = txdr_unsigned(callback); + *tl++ = txdr_unsigned(1); + *tl = txdr_unsigned(op); + } + return (0); +} + +/* * Return the next index# for a clientid. Mostly just increment and return * the next one, but... if the 32bit unsigned does actually wrap around, * it should be rebooted. @@ -5281,3 +5628,307 @@ nfsrv_throwawayallstate(NFSPROC_T *p) } } +/* + * Check the sequence# for the session and slot provided as an argument. + * Also, renew the lease if the session will return NFS_OK. + */ +int +nfsrv_checksequence(struct nfsrv_descript *nd, uint32_t sequenceid, + uint32_t *highest_slotidp, uint32_t *target_highest_slotidp, int cache_this, + uint32_t *sflagsp, NFSPROC_T *p) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + int error; + SVCXPRT *savxprt; + + shp = NFSSESSIONHASH(nd->nd_sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(nd->nd_sessionid); + if (sep == NULL) { + NFSUNLOCKSESSION(shp); + return (NFSERR_BADSESSION); + } + error = nfsv4_seqsession(sequenceid, nd->nd_slotid, *highest_slotidp, + sep->sess_slots, NULL, NFSV4_SLOTS - 1); + if (error != 0) { + NFSUNLOCKSESSION(shp); + return (error); + } + if (cache_this != 0) + nd->nd_flag |= ND_SAVEREPLY; + /* Renew the lease. */ + sep->sess_clp->lc_expiry = nfsrv_leaseexpiry(); + nd->nd_clientid.qval = sep->sess_clp->lc_clientid.qval; + nd->nd_flag |= ND_IMPLIEDCLID; + + /* + * If this session handles the backchannel, save the nd_xprt for this + * RPC, since this is the one being used. + */ + if (sep->sess_cbsess.nfsess_xprt != NULL && + (sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) { + savxprt = sep->sess_cbsess.nfsess_xprt; + SVC_ACQUIRE(nd->nd_xprt); + nd->nd_xprt->xp_p2 = savxprt->xp_p2; + nd->nd_xprt->xp_idletimeout = 0; /* Disable timeout. */ + sep->sess_cbsess.nfsess_xprt = nd->nd_xprt; + SVC_RELEASE(savxprt); + } + + *sflagsp = 0; + if (sep->sess_clp->lc_req.nr_client == NULL) + *sflagsp |= NFSV4SEQ_CBPATHDOWN; + NFSUNLOCKSESSION(shp); + if (error == NFSERR_EXPIRED) { + *sflagsp |= NFSV4SEQ_EXPIREDALLSTATEREVOKED; + error = 0; + } else if (error == NFSERR_ADMINREVOKED) { + *sflagsp |= NFSV4SEQ_ADMINSTATEREVOKED; + error = 0; + } + *highest_slotidp = *target_highest_slotidp = NFSV4_SLOTS - 1; + return (0); +} + +/* + * Check/set reclaim complete for this session/clientid. + */ +int +nfsrv_checkreclaimcomplete(struct nfsrv_descript *nd) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + int error = 0; + + shp = NFSSESSIONHASH(nd->nd_sessionid); + NFSLOCKSTATE(); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(nd->nd_sessionid); + if (sep == NULL) { + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + return (NFSERR_BADSESSION); + } + + /* Check to see if reclaim complete has already happened. */ + if ((sep->sess_clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0) + error = NFSERR_COMPLETEALREADY; + else + sep->sess_clp->lc_flags |= LCL_RECLAIMCOMPLETE; + NFSUNLOCKSESSION(shp); + NFSUNLOCKSTATE(); + return (error); +} + +/* + * Cache the reply in a session slot. + */ +void +nfsrv_cache_session(uint8_t *sessionid, uint32_t slotid, int repstat, + struct mbuf **m) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + + shp = NFSSESSIONHASH(sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(sessionid); + if (sep == NULL) { + NFSUNLOCKSESSION(shp); + printf("nfsrv_cache_session: no session\n"); + m_freem(*m); + return; + } + nfsv4_seqsess_cacherep(slotid, sep->sess_slots, repstat, m); + NFSUNLOCKSESSION(shp); +} + +/* + * Search for a session that matches the sessionid. + */ +static struct nfsdsession * +nfsrv_findsession(uint8_t *sessionid) +{ + struct nfsdsession *sep; + struct nfssessionhash *shp; + + shp = NFSSESSIONHASH(sessionid); + LIST_FOREACH(sep, &shp->list, sess_hash) { + if (!NFSBCMP(sessionid, sep->sess_sessionid, NFSX_V4SESSIONID)) + break; + } + return (sep); +} + +/* + * Destroy a session. + */ +int +nfsrv_destroysession(struct nfsrv_descript *nd, uint8_t *sessionid) +{ + int error, samesess; + + samesess = 0; + if (!NFSBCMP(sessionid, nd->nd_sessionid, NFSX_V4SESSIONID)) { + samesess = 1; + if ((nd->nd_flag & ND_LASTOP) == 0) + return (NFSERR_BADSESSION); + } + error = nfsrv_freesession(NULL, sessionid); + if (error == 0 && samesess != 0) + nd->nd_flag &= ~ND_HASSEQUENCE; + return (error); +} + +/* + * Free up a session structure. + */ +static int +nfsrv_freesession(struct nfsdsession *sep, uint8_t *sessionid) +{ + struct nfssessionhash *shp; + int i; + + if (sep == NULL) { + shp = NFSSESSIONHASH(sessionid); + NFSLOCKSESSION(shp); + sep = nfsrv_findsession(sessionid); + } else { + shp = NFSSESSIONHASH(sep->sess_sessionid); + NFSLOCKSESSION(shp); + } + if (sep != NULL) { + NFSLOCKSTATE(); + sep->sess_refcnt--; + if (sep->sess_refcnt > 0) { + NFSUNLOCKSTATE(); + NFSUNLOCKSESSION(shp); + return (0); + } + LIST_REMOVE(sep, sess_hash); + LIST_REMOVE(sep, sess_list); + NFSUNLOCKSTATE(); + } + NFSUNLOCKSESSION(shp); + if (sep == NULL) + return (NFSERR_BADSESSION); + for (i = 0; i < NFSV4_SLOTS; i++) + if (sep->sess_slots[i].nfssl_reply != NULL) + m_freem(sep->sess_slots[i].nfssl_reply); + if (sep->sess_cbsess.nfsess_xprt != NULL) + SVC_RELEASE(sep->sess_cbsess.nfsess_xprt); + free(sep, M_NFSDSESSION); + return (0); +} + +/* + * Free a stateid. + * RFC5661 says that it should fail when there are associated opens, locks + * or delegations. Since stateids represent opens, I don't see how you can + * free an open stateid (it will be free'd when closed), so this function + * only works for lock stateids (freeing the lock_owner) or delegations. + */ +int +nfsrv_freestateid(struct nfsrv_descript *nd, nfsv4stateid_t *stateidp, + NFSPROC_T *p) +{ + struct nfsclient *clp; + struct nfsstate *stp; + int error; + + NFSLOCKSTATE(); + /* + * Look up the stateid + */ + error = nfsrv_getclient((nfsquad_t)((u_quad_t)0), CLOPS_RENEW, &clp, + NULL, (nfsquad_t)((u_quad_t)0), 0, nd, p); + if (error == 0) { + /* First, check for a delegation. */ + LIST_FOREACH(stp, &clp->lc_deleg, ls_list) { + if (!NFSBCMP(stp->ls_stateid.other, stateidp->other, + NFSX_STATEIDOTHER)) + break; + } + if (stp != NULL) { + nfsrv_freedeleg(stp); + NFSUNLOCKSTATE(); + return (error); + } + } + /* Not a delegation, try for a lock_owner. */ + if (error == 0) + error = nfsrv_getstate(clp, stateidp, 0, &stp); + if (error == 0 && ((stp->ls_flags & (NFSLCK_OPEN | NFSLCK_DELEGREAD | + NFSLCK_DELEGWRITE)) != 0 || (stp->ls_flags & NFSLCK_LOCK) == 0)) + /* Not a lock_owner stateid. */ + error = NFSERR_LOCKSHELD; + if (error == 0 && !LIST_EMPTY(&stp->ls_lock)) + error = NFSERR_LOCKSHELD; + if (error == 0) + nfsrv_freelockowner(stp, NULL, 0, p); + NFSUNLOCKSTATE(); + return (error); +} + +/* + * Generate the xdr for an NFSv4.1 CBSequence Operation. + */ +static int +nfsv4_setcbsequence(struct nfsrv_descript *nd, struct nfsclient *clp, + int dont_replycache, struct nfsdsession **sepp) +{ + struct nfsdsession *sep; + uint32_t *tl, slotseq = 0; + int maxslot, slotpos; + uint8_t sessionid[NFSX_V4SESSIONID]; + int error; + + error = nfsv4_getcbsession(clp, sepp); + if (error != 0) + return (error); + sep = *sepp; + (void)nfsv4_sequencelookup(NULL, &sep->sess_cbsess, &slotpos, &maxslot, + &slotseq, sessionid); + KASSERT(maxslot >= 0, ("nfsv4_setcbsequence neg maxslot")); + + /* Build the Sequence arguments. */ + NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 5 * NFSX_UNSIGNED); + bcopy(sessionid, tl, NFSX_V4SESSIONID); + tl += NFSX_V4SESSIONID / NFSX_UNSIGNED; + nd->nd_slotseq = tl; + *tl++ = txdr_unsigned(slotseq); + *tl++ = txdr_unsigned(slotpos); + *tl++ = txdr_unsigned(maxslot); + if (dont_replycache == 0) + *tl++ = newnfs_true; + else + *tl++ = newnfs_false; + *tl = 0; /* No referring call list, for now. */ + nd->nd_flag |= ND_HASSEQUENCE; + return (0); +} + +/* + * Get a session for the callback. + */ +static int +nfsv4_getcbsession(struct nfsclient *clp, struct nfsdsession **sepp) +{ + struct nfsdsession *sep; + + NFSLOCKSTATE(); + LIST_FOREACH(sep, &clp->lc_session, sess_list) { + if ((sep->sess_crflags & NFSV4CRSESS_CONNBACKCHAN) != 0) + break; + } + if (sep == NULL) { + NFSUNLOCKSTATE(); + return (NFSERR_BADSESSION); + } + sep->sess_refcnt++; + *sepp = sep; + NFSUNLOCKSTATE(); + return (0); +} + diff --git a/sys/fs/nfsserver/nfs_nfsdsubs.c b/sys/fs/nfsserver/nfs_nfsdsubs.c index 404ad87..de496db 100644 --- a/sys/fs/nfsserver/nfs_nfsdsubs.c +++ b/sys/fs/nfsserver/nfs_nfsdsubs.c @@ -46,6 +46,7 @@ extern u_int32_t newnfs_true, newnfs_false; extern int nfs_pubfhset; extern struct nfsclienthashhead nfsclienthash[NFSCLIENTHASHSIZE]; extern struct nfslockhashhead nfslockhash[NFSLOCKHASHSIZE]; +extern struct nfssessionhash nfssessionhash[NFSSESSIONHASHSIZE]; extern int nfsrv_useacl; extern uid_t nfsrv_defaultuid; extern gid_t nfsrv_defaultgid; @@ -56,6 +57,8 @@ static nfstype newnfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, extern nfstype nfsv34_type[9]; #endif /* !APPLEKEXT */ +static u_int32_t nfsrv_isannfserr(u_int32_t); + SYSCTL_DECL(_vfs_nfsd); static int disable_checkutf8 = 0; @@ -68,16 +71,16 @@ static char nfsrv_hexdigit(char, int *); /* * Maps errno values to nfs error numbers. * Use NFSERR_IO as the catch all for ones not specifically defined in - * RFC 1094. + * RFC 1094. (It now includes the errors added for NFSv3.) */ -static u_char nfsrv_v2errmap[ELAST] = { +static u_char nfsrv_v2errmap[NFSERR_REMOTE] = { NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO, - NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR, - NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, + NFSERR_IO, NFSERR_EXIST, NFSERR_XDEV, NFSERR_NODEV, NFSERR_NOTDIR, + NFSERR_ISDIR, NFSERR_INVAL, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS, - NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, + NFSERR_MLINK, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, @@ -85,9 +88,7 @@ static u_char nfsrv_v2errmap[ELAST] = { NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO, NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE, - NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, - NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, - NFSERR_IO, + NFSERR_REMOTE, }; /* @@ -1493,19 +1494,41 @@ nfsd_errmap(struct nfsrv_descript *nd) else if (nd->nd_repstat == NFSERR_MINORVERMISMATCH || nd->nd_repstat == NFSERR_OPILLEGAL) return (txdr_unsigned(nd->nd_repstat)); - else + else if ((nd->nd_flag & ND_NFSV41) != 0) { + if (nd->nd_repstat == EOPNOTSUPP) + nd->nd_repstat = NFSERR_NOTSUPP; + nd->nd_repstat = nfsrv_isannfserr(nd->nd_repstat); + return (txdr_unsigned(nd->nd_repstat)); + } else errp = defaulterrp = nfsrv_v4errmap[nd->nd_procnum]; while (*++errp) if (*errp == nd->nd_repstat) return (txdr_unsigned(nd->nd_repstat)); return (txdr_unsigned(*defaulterrp)); } - if (nd->nd_repstat <= ELAST) + if (nd->nd_repstat <= NFSERR_REMOTE) return (txdr_unsigned(nfsrv_v2errmap[nd->nd_repstat - 1])); return (txdr_unsigned(NFSERR_IO)); } /* + * Check to see if the error is a valid NFS one. If not, replace it with + * NFSERR_IO. + */ +static u_int32_t +nfsrv_isannfserr(u_int32_t errval) +{ + + if (errval == NFSERR_OK) + return (errval); + if (errval >= NFSERR_BADHANDLE && errval <= NFSERR_DELEGREVOKED) + return (errval); + if (errval > 0 && errval <= NFSERR_REMOTE) + return (nfsrv_v2errmap[errval - 1]); + return (NFSERR_IO); +} + +/* * Check to see if setting a uid/gid is permitted when creating a new * file object. (Called when uid and/or gid is specified in the * settable attributes for V4. @@ -2005,6 +2028,8 @@ nfsd_init(void) LIST_INIT(&nfsclienthash[i]); for (i = 0; i < NFSLOCKHASHSIZE; i++) LIST_INIT(&nfslockhash[i]); + for (i = 0; i < NFSSESSIONHASHSIZE; i++) + LIST_INIT(&nfssessionhash[i].list); /* and the v2 pubfh should be all zeros */ NFSBZERO(nfs_v2pubfh, NFSX_V2FH); @@ -2032,3 +2057,42 @@ nfsd_checkrootexp(struct nfsrv_descript *nd) return (1); } +/* + * Parse the first part of an NFSv4 compound to find out what the minor + * version# is. + */ +void +nfsd_getminorvers(struct nfsrv_descript *nd, u_char *tag, u_char **tagstrp, + int *taglenp, u_int32_t *minversp) +{ + uint32_t *tl; + int error = 0, taglen = -1; + u_char *tagstr = NULL; + + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + taglen = fxdr_unsigned(int, *tl); + if (taglen < 0 || taglen > NFSV4_OPAQUELIMIT) { + error = EBADRPC; + goto nfsmout; + } + if (taglen <= NFSV4_SMALLSTR) + tagstr = tag; + else + tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK); + error = nfsrv_mtostr(nd, tagstr, taglen); + if (error != 0) + goto nfsmout; + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + *minversp = fxdr_unsigned(u_int32_t, *tl); + *tagstrp = tagstr; + if (*minversp == NFSV41_MINORVERSION) + nd->nd_flag |= ND_NFSV41; +nfsmout: + if (error != 0) { + if (tagstr != NULL && taglen > NFSV4_SMALLSTR) + free(tagstr, M_TEMP); + taglen = -1; + } + *taglenp = taglen; +} + diff --git a/sys/modules/krpc/Makefile b/sys/modules/krpc/Makefile index 2f3d3a7..b6a21a4 100644 --- a/sys/modules/krpc/Makefile +++ b/sys/modules/krpc/Makefile @@ -5,6 +5,7 @@ KMOD= krpc SRCS= auth_none.c \ auth_unix.c \ authunix_prot.c \ + clnt_bck.c \ clnt_dg.c \ clnt_rc.c \ clnt_vc.c \ diff --git a/sys/rpc/clnt_bck.c b/sys/rpc/clnt_bck.c new file mode 100644 index 0000000..b63b2b1 --- /dev/null +++ b/sys/rpc/clnt_bck.c @@ -0,0 +1,593 @@ +/* $NetBSD: clnt_vc.c,v 1.4 2000/07/14 08:40:42 fvdl Exp $ */ + +/*- + * Copyright (c) 2009, Sun Microsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Sun Microsystems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char *sccsid2 = "@(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro"; +static char *sccsid = "@(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC"; +static char sccsid3[] = "@(#)clnt_vc.c 1.19 89/03/16 Copyr 1988 Sun Micro"; +#endif +#include +__FBSDID("$FreeBSD$"); + +/* + * clnt_tcp.c, Implements a TCP/IP based, client side RPC. + * + * Copyright (C) 1984, Sun Microsystems, Inc. + * + * TCP based RPC supports 'batched calls'. + * A sequence of calls may be batched-up in a send buffer. The rpc call + * return immediately to the client even though the call was not necessarily + * sent. The batching occurs if the results' xdr routine is NULL (0) AND + * the rpc timeout value is zero (see clnt.h, rpc). + * + * Clients should NOT casually batch calls that in fact return results; that is, + * the server side should be aware that a call is batched and not produce any + * return message. Batched calls that produce many result messages can + * deadlock (netlock) the client and the server.... + * + * Now go hang yourself. + */ + +/* + * This code handles the special case of a NFSv4.n backchannel for + * callback RPCs. It is similar to clnt_vc.c, but uses the TCP + * connection provided by the client to the server. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +struct cmessage { + struct cmsghdr cmsg; + struct cmsgcred cmcred; +}; + +static void clnt_bck_geterr(CLIENT *, struct rpc_err *); +static bool_t clnt_bck_freeres(CLIENT *, xdrproc_t, void *); +static void clnt_bck_abort(CLIENT *); +static bool_t clnt_bck_control(CLIENT *, u_int, void *); +static void clnt_bck_close(CLIENT *); +static void clnt_bck_destroy(CLIENT *); + +static struct clnt_ops clnt_bck_ops = { + .cl_abort = clnt_bck_abort, + .cl_geterr = clnt_bck_geterr, + .cl_freeres = clnt_bck_freeres, + .cl_close = clnt_bck_close, + .cl_destroy = clnt_bck_destroy, + .cl_control = clnt_bck_control +}; + +/* + * Create a client handle for a connection. + * Default options are set, which the user can change using clnt_control()'s. + * This code handles the special case of an NFSv4.1 session backchannel + * call, which is sent on a TCP connection created against the server + * by a client. + */ +void * +clnt_bck_create( + struct socket *so, /* Server transport socket. */ + const rpcprog_t prog, /* program number */ + const rpcvers_t vers) /* version number */ +{ + CLIENT *cl; /* client handle */ + struct ct_data *ct = NULL; /* client handle */ + struct timeval now; + struct rpc_msg call_msg; + static uint32_t disrupt; + XDR xdrs; + + if (disrupt == 0) + disrupt = (uint32_t)(long)so; + + cl = (CLIENT *)mem_alloc(sizeof (*cl)); + ct = (struct ct_data *)mem_alloc(sizeof (*ct)); + + mtx_init(&ct->ct_lock, "ct->ct_lock", NULL, MTX_DEF); + ct->ct_threads = 0; + ct->ct_closing = FALSE; + ct->ct_closed = FALSE; + ct->ct_upcallrefs = 0; + ct->ct_closeit = FALSE; + + /* + * Set up private data struct + */ + ct->ct_wait.tv_sec = -1; + ct->ct_wait.tv_usec = -1; + + /* + * Initialize call message + */ + getmicrotime(&now); + ct->ct_xid = ((uint32_t)++disrupt) ^ __RPC_GETXID(&now); + call_msg.rm_xid = ct->ct_xid; + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = (uint32_t)prog; + call_msg.rm_call.cb_vers = (uint32_t)vers; + + /* + * pre-serialize the static part of the call msg and stash it away + */ + xdrmem_create(&xdrs, ct->ct_mcallc, MCALL_MSG_SIZE, + XDR_ENCODE); + if (!xdr_callhdr(&xdrs, &call_msg)) + goto err; + ct->ct_mpos = XDR_GETPOS(&xdrs); + XDR_DESTROY(&xdrs); + ct->ct_waitchan = "rpcbck"; + ct->ct_waitflag = 0; + cl->cl_refs = 1; + cl->cl_ops = &clnt_bck_ops; + cl->cl_private = ct; + cl->cl_auth = authnone_create(); + TAILQ_INIT(&ct->ct_pending); + return (cl); + +err: + if (cl) { + if (ct) { + mtx_destroy(&ct->ct_lock); + mem_free(ct, sizeof (struct ct_data)); + } + if (cl) + mem_free(cl, sizeof (CLIENT)); + } + return (NULL); +} + +enum clnt_stat +clnt_bck_call( + CLIENT *cl, /* client handle */ + struct rpc_callextra *ext, /* call metadata */ + rpcproc_t proc, /* procedure number */ + struct mbuf *args, /* pointer to args */ + struct mbuf **resultsp, /* pointer to results */ + struct timeval utimeout, + SVCXPRT *xprt) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + AUTH *auth; + struct rpc_err *errp; + enum clnt_stat stat; + XDR xdrs; + struct rpc_msg reply_msg; + bool_t ok; + int nrefreshes = 2; /* number of times to refresh cred */ + struct timeval timeout; + uint32_t xid; + struct mbuf *mreq = NULL, *results; + struct ct_request *cr; + int error; + + cr = malloc(sizeof(struct ct_request), M_RPC, M_WAITOK); + + mtx_lock(&ct->ct_lock); + + if (ct->ct_closing || ct->ct_closed) { + mtx_unlock(&ct->ct_lock); + free(cr, M_RPC); + return (RPC_CANTSEND); + } + ct->ct_threads++; + + if (ext) { + auth = ext->rc_auth; + errp = &ext->rc_err; + } else { + auth = cl->cl_auth; + errp = &ct->ct_error; + } + + cr->cr_mrep = NULL; + cr->cr_error = 0; + + if (ct->ct_wait.tv_usec == -1) + timeout = utimeout; /* use supplied timeout */ + else + timeout = ct->ct_wait; /* use default timeout */ + +call_again: + mtx_assert(&ct->ct_lock, MA_OWNED); + + ct->ct_xid++; + xid = ct->ct_xid; + + mtx_unlock(&ct->ct_lock); + + /* + * Leave space to pre-pend the record mark. + */ + mreq = m_gethdr(M_WAITOK, MT_DATA); + mreq->m_data += sizeof(uint32_t); + KASSERT(ct->ct_mpos + sizeof(uint32_t) <= MHLEN, + ("RPC header too big")); + bcopy(ct->ct_mcallc, mreq->m_data, ct->ct_mpos); + mreq->m_len = ct->ct_mpos; + + /* + * The XID is the first thing in the request. + */ + *mtod(mreq, uint32_t *) = htonl(xid); + + xdrmbuf_create(&xdrs, mreq, XDR_ENCODE); + + errp->re_status = stat = RPC_SUCCESS; + + if ((!XDR_PUTINT32(&xdrs, &proc)) || + (!AUTH_MARSHALL(auth, xid, &xdrs, + m_copym(args, 0, M_COPYALL, M_WAITOK)))) { + errp->re_status = stat = RPC_CANTENCODEARGS; + mtx_lock(&ct->ct_lock); + goto out; + } + mreq->m_pkthdr.len = m_length(mreq, NULL); + + /* + * Prepend a record marker containing the packet length. + */ + M_PREPEND(mreq, sizeof(uint32_t), M_WAITOK); + *mtod(mreq, uint32_t *) = + htonl(0x80000000 | (mreq->m_pkthdr.len - sizeof(uint32_t))); + + cr->cr_xid = xid; + mtx_lock(&ct->ct_lock); + /* + * Check to see if the client end has already started to close down + * the connection. The svc code will have set ct_error.re_status + * to RPC_CANTRECV if this is the case. + * If the client starts to close down the connection after this + * point, it will be detected later when cr_error is checked, + * since the request is in the ct_pending queue. + */ + if (ct->ct_error.re_status == RPC_CANTRECV) { + if (errp != &ct->ct_error) { + errp->re_errno = ct->ct_error.re_errno; + errp->re_status = RPC_CANTRECV; + } + stat = RPC_CANTRECV; + goto out; + } + TAILQ_INSERT_TAIL(&ct->ct_pending, cr, cr_link); + mtx_unlock(&ct->ct_lock); + + /* + * sosend consumes mreq. + */ + sx_xlock(&xprt->xp_lock); + error = sosend(xprt->xp_socket, NULL, NULL, mreq, NULL, 0, curthread); +if (error != 0) printf("sosend=%d\n", error); + mreq = NULL; + if (error == EMSGSIZE) { +printf("emsgsize\n"); + SOCKBUF_LOCK(&xprt->xp_socket->so_snd); + sbwait(&xprt->xp_socket->so_snd); + SOCKBUF_UNLOCK(&xprt->xp_socket->so_snd); + sx_xunlock(&xprt->xp_lock); + AUTH_VALIDATE(auth, xid, NULL, NULL); + mtx_lock(&ct->ct_lock); + TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); + goto call_again; + } + sx_xunlock(&xprt->xp_lock); + + reply_msg.acpted_rply.ar_verf.oa_flavor = AUTH_NULL; + reply_msg.acpted_rply.ar_verf.oa_base = cr->cr_verf; + reply_msg.acpted_rply.ar_verf.oa_length = 0; + reply_msg.acpted_rply.ar_results.where = NULL; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t)xdr_void; + + mtx_lock(&ct->ct_lock); + if (error) { + TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); + errp->re_errno = error; + errp->re_status = stat = RPC_CANTSEND; + goto out; + } + + /* + * Check to see if we got an upcall while waiting for the + * lock. In both these cases, the request has been removed + * from ct->ct_pending. + */ + if (cr->cr_error) { + TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); + errp->re_errno = cr->cr_error; + errp->re_status = stat = RPC_CANTRECV; + goto out; + } + if (cr->cr_mrep) { + TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); + goto got_reply; + } + + /* + * Hack to provide rpc-based message passing + */ + if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { + TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); + errp->re_status = stat = RPC_TIMEDOUT; + goto out; + } + + error = msleep(cr, &ct->ct_lock, ct->ct_waitflag, ct->ct_waitchan, + tvtohz(&timeout)); + + TAILQ_REMOVE(&ct->ct_pending, cr, cr_link); + + if (error) { + /* + * The sleep returned an error so our request is still + * on the list. Turn the error code into an + * appropriate client status. + */ + errp->re_errno = error; + switch (error) { + case EINTR: + stat = RPC_INTR; + break; + case EWOULDBLOCK: + stat = RPC_TIMEDOUT; + break; + default: + stat = RPC_CANTRECV; + }; + errp->re_status = stat; + goto out; + } else { + /* + * We were woken up by the svc thread. If the + * upcall had a receive error, report that, + * otherwise we have a reply. + */ + if (cr->cr_error) { + errp->re_errno = cr->cr_error; + errp->re_status = stat = RPC_CANTRECV; + goto out; + } + } + +got_reply: + /* + * Now decode and validate the response. We need to drop the + * lock since xdr_replymsg may end up sleeping in malloc. + */ + mtx_unlock(&ct->ct_lock); + + if (ext && ext->rc_feedback) + ext->rc_feedback(FEEDBACK_OK, proc, ext->rc_feedback_arg); + + xdrmbuf_create(&xdrs, cr->cr_mrep, XDR_DECODE); + ok = xdr_replymsg(&xdrs, &reply_msg); + cr->cr_mrep = NULL; + + if (ok) { + if ((reply_msg.rm_reply.rp_stat == MSG_ACCEPTED) && + (reply_msg.acpted_rply.ar_stat == SUCCESS)) + errp->re_status = stat = RPC_SUCCESS; + else + stat = _seterr_reply(&reply_msg, errp); + + if (stat == RPC_SUCCESS) { + results = xdrmbuf_getall(&xdrs); + if (!AUTH_VALIDATE(auth, xid, + &reply_msg.acpted_rply.ar_verf, &results)) { + errp->re_status = stat = RPC_AUTHERROR; + errp->re_why = AUTH_INVALIDRESP; + } else { + KASSERT(results, + ("auth validated but no result")); + *resultsp = results; + } + } /* end successful completion */ + /* + * If unsuccesful AND error is an authentication error + * then refresh credentials and try again, else break + */ + else if (stat == RPC_AUTHERROR) + /* maybe our credentials need to be refreshed ... */ + if (nrefreshes > 0 && AUTH_REFRESH(auth, &reply_msg)) { + nrefreshes--; + XDR_DESTROY(&xdrs); + mtx_lock(&ct->ct_lock); + goto call_again; + } + /* end of unsuccessful completion */ + /* end of valid reply message */ + } else + errp->re_status = stat = RPC_CANTDECODERES; + XDR_DESTROY(&xdrs); + mtx_lock(&ct->ct_lock); +out: + mtx_assert(&ct->ct_lock, MA_OWNED); + + KASSERT(stat != RPC_SUCCESS || *resultsp, + ("RPC_SUCCESS without reply")); + + if (mreq != NULL) + m_freem(mreq); + if (cr->cr_mrep != NULL) + m_freem(cr->cr_mrep); + + ct->ct_threads--; + if (ct->ct_closing) + wakeup(ct); + + mtx_unlock(&ct->ct_lock); + + if (auth && stat != RPC_SUCCESS) + AUTH_VALIDATE(auth, xid, NULL, NULL); + + free(cr, M_RPC); + + return (stat); +} + +static void +clnt_bck_geterr(CLIENT *cl, struct rpc_err *errp) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + *errp = ct->ct_error; +} + +static bool_t +clnt_bck_freeres(CLIENT *cl, xdrproc_t xdr_res, void *res_ptr) +{ + XDR xdrs; + bool_t dummy; + + xdrs.x_op = XDR_FREE; + dummy = (*xdr_res)(&xdrs, res_ptr); + + return (dummy); +} + +/*ARGSUSED*/ +static void +clnt_bck_abort(CLIENT *cl) +{ +} + +static bool_t +clnt_bck_control(CLIENT *cl, u_int request, void *info) +{ + + return (TRUE); +} + +static void +clnt_bck_close(CLIENT *cl) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + mtx_lock(&ct->ct_lock); + + if (ct->ct_closed) { + mtx_unlock(&ct->ct_lock); + return; + } + + if (ct->ct_closing) { + while (ct->ct_closing) + msleep(ct, &ct->ct_lock, 0, "rpcclose", 0); + KASSERT(ct->ct_closed, ("client should be closed")); + mtx_unlock(&ct->ct_lock); + return; + } + + ct->ct_closing = FALSE; + ct->ct_closed = TRUE; + mtx_unlock(&ct->ct_lock); + wakeup(ct); +} + +static void +clnt_bck_destroy(CLIENT *cl) +{ + struct ct_data *ct = (struct ct_data *) cl->cl_private; + + clnt_bck_close(cl); + + mtx_destroy(&ct->ct_lock); + mem_free(ct, sizeof(struct ct_data)); + if (cl->cl_netid && cl->cl_netid[0]) + mem_free(cl->cl_netid, strlen(cl->cl_netid) +1); + if (cl->cl_tp && cl->cl_tp[0]) + mem_free(cl->cl_tp, strlen(cl->cl_tp) +1); + mem_free(cl, sizeof(CLIENT)); +} + +/* + * This call is done by the svc code when a backchannel RPC reply is + * received. + */ +void +clnt_bck_svccall(void *arg, struct mbuf *mrep, uint32_t xid) +{ + struct ct_data *ct = (struct ct_data *)arg; + struct ct_request *cr; + int foundreq; + + mtx_lock(&ct->ct_lock); + ct->ct_upcallrefs++; + /* + * See if we can match this reply to a request. + */ + foundreq = 0; + TAILQ_FOREACH(cr, &ct->ct_pending, cr_link) { + if (cr->cr_xid == xid) { + /* + * This one matches. We leave the reply mbuf list in + * cr->cr_mrep. Set the XID to zero so that we will + * ignore any duplicated replies. + */ + cr->cr_xid = 0; + cr->cr_mrep = mrep; + cr->cr_error = 0; + foundreq = 1; + wakeup(cr); + break; + } + } + + ct->ct_upcallrefs--; + if (ct->ct_upcallrefs < 0) + panic("rpcvc svccall refcnt"); + if (ct->ct_upcallrefs == 0) + wakeup(&ct->ct_upcallrefs); + mtx_unlock(&ct->ct_lock); + if (foundreq == 0) + m_freem(mrep); +} + diff --git a/sys/rpc/krpc.h b/sys/rpc/krpc.h index 280860b..2e61b3c 100644 --- a/sys/rpc/krpc.h +++ b/sys/rpc/krpc.h @@ -37,6 +37,10 @@ */ #define MCALL_MSG_SIZE 24 +void clnt_bck_svccall(void *, struct mbuf *, uint32_t); +enum clnt_stat clnt_bck_call(CLIENT *, struct rpc_callextra *, rpcproc_t, + struct mbuf *, struct mbuf **, struct timeval, SVCXPRT *); + /* * A pending RPC request which awaits a reply. Requests which have * received their reply will have cr_xid set to zero and cr_mrep to diff --git a/sys/rpc/svc.h b/sys/rpc/svc.h index 4f2c853..1c7bbce 100644 --- a/sys/rpc/svc.h +++ b/sys/rpc/svc.h @@ -145,6 +145,7 @@ struct __rpc_svcthread; * Server side transport handle. In the kernel, transports have a * reference count which tracks the number of currently assigned * worker threads plus one for the service pool's reference. + * For NFSv4.1 sessions, a reference is also held for a backchannel. */ typedef struct __rpc_svcxprt { #ifdef _KERNEL @@ -774,6 +775,13 @@ extern SVCXPRT *svc_vc_create(SVCPOOL *, struct socket *, extern SVCXPRT *svc_vc_create_backchannel(SVCPOOL *); +extern void *clnt_bck_create(struct socket *, const rpcprog_t, const rpcvers_t); + /* + * struct socket *; -- server transport socket + * const rpcprog_t prog; -- RPC program number + * const rpcvers_t vers; -- RPC program version + */ + /* * Generic TLI create routine */ diff --git a/sys/rpc/svc_vc.c b/sys/rpc/svc_vc.c index 5fe6488..df1d86e 100644 --- a/sys/rpc/svc_vc.c +++ b/sys/rpc/svc_vc.c @@ -654,6 +654,7 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg, struct socket* so = xprt->xp_socket; XDR xdrs; int error, rcvflag; + uint32_t xid_plus_direction[2]; /* * Serialise access to the socket and our own record parsing @@ -672,6 +673,32 @@ svc_vc_recv(SVCXPRT *xprt, struct rpc_msg *msg, /* Process and return complete request in cd->mreq. */ if (cd->mreq != NULL && cd->resid == 0 && cd->eor) { + /* + * Now, check for a backchannel reply. + * The XID is in the first uint32_t of the reply + * and the message direction is the second one. + */ + if ((cd->mreq->m_len >= sizeof(xid_plus_direction) || + m_length(cd->mreq, NULL) >= + sizeof(xid_plus_direction)) && + xprt->xp_p2 != NULL) { + m_copydata(cd->mreq, 0, + sizeof(xid_plus_direction), + (char *)xid_plus_direction); + xid_plus_direction[0] = + ntohl(xid_plus_direction[0]); + xid_plus_direction[1] = + ntohl(xid_plus_direction[1]); + /* Check message direction. */ + if (xid_plus_direction[1] == REPLY) { + clnt_bck_svccall(xprt->xp_p2, + cd->mreq, + xid_plus_direction[0]); + cd->mreq = NULL; + continue; + } + } + xdrmbuf_create(&xdrs, cd->mreq, XDR_DECODE); cd->mreq = NULL; @@ -848,7 +875,6 @@ svc_vc_reply(SVCXPRT *xprt, struct rpc_msg *msg, } XDR_DESTROY(&xdrs); - xprt->xp_p2 = NULL; return (stat); } -- cgit v1.1