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