diff options
author | rmacklem <rmacklem@FreeBSD.org> | 2014-08-01 21:10:41 +0000 |
---|---|---|
committer | rmacklem <rmacklem@FreeBSD.org> | 2014-08-01 21:10:41 +0000 |
commit | 50867d802b534350d2bee81cf0858b8cd871b487 (patch) | |
tree | 7ce81fe06c3c67164e672e40de7e128c98669a0c /sys/fs/nfsserver | |
parent | ba879dc5f5950146f01bba4a949fe57cdd5628a2 (diff) | |
download | FreeBSD-src-50867d802b534350d2bee81cf0858b8cd871b487.zip FreeBSD-src-50867d802b534350d2bee81cf0858b8cd871b487.tar.gz |
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.
Diffstat (limited to 'sys/fs/nfsserver')
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdcache.c | 3 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdkrpc.c | 51 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdport.c | 21 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdserv.c | 666 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdsocket.c | 144 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdstate.c | 913 | ||||
-rw-r--r-- | sys/fs/nfsserver/nfs_nfsdsubs.c | 84 |
7 files changed, 1632 insertions, 250 deletions
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; +} + |