svn commit: r253435 - projects/nfsv4.1-server/sys/fs/nfsserver

Rick Macklem rmacklem at FreeBSD.org
Wed Jul 17 23:03:11 UTC 2013


Author: rmacklem
Date: Wed Jul 17 23:03:10 2013
New Revision: 253435
URL: http://svnweb.freebsd.org/changeset/base/253435

Log:
  Merge in the NFSv4.1 changes for the NFSv4 state handling code.

Modified:
  projects/nfsv4.1-server/sys/fs/nfsserver/nfs_nfsdstate.c

Modified: projects/nfsv4.1-server/sys/fs/nfsserver/nfs_nfsdstate.c
==============================================================================
--- projects/nfsv4.1-server/sys/fs/nfsserver/nfs_nfsdstate.c	Wed Jul 17 19:41:16 2013	(r253434)
+++ projects/nfsv4.1-server/sys/fs/nfsserver/nfs_nfsdstate.c	Wed Jul 17 23:03:10 2013	(r253435)
@@ -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,7 +90,8 @@ static void nfsrv_updatelock(struct nfss
 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);
@@ -123,6 +125,8 @@ static void nfsrv_locallock_commit(struc
     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);
 
 /*
  * Scan the client list for a match and either return the current one,
@@ -191,6 +195,18 @@ nfsrv_setclient(struct nfsrv_descript *n
 	}
 	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 +221,12 @@ nfsrv_setclient(struct nfsrv_descript *n
 		 * 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 +238,7 @@ nfsrv_setclient(struct nfsrv_descript *n
 		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 +311,12 @@ nfsrv_setclient(struct nfsrv_descript *n
 		 */
 		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 +369,68 @@ nfsrv_setclient(struct nfsrv_descript *n
 		*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 +442,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 +475,39 @@ nfsrv_getclient(nfsquad_t clientid, int 
 			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 +533,10 @@ nfsrv_getclient(nfsquad_t clientid, int 
 	 * 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 +551,7 @@ nfsrv_getclient(nfsquad_t clientid, int 
 			 */
 			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 +562,23 @@ nfsrv_getclient(nfsquad_t clientid, int 
 			    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) {
+			NFSBCOPY(sessid, nsep->sess_sessionid,
+			    NFSX_V4SESSIONID);
+			shp = NFSSESSIONHASH(nsep->sess_sessionid);
+			NFSLOCKSESSION(shp);
+			LIST_INSERT_HEAD(&shp->list, nsep, sess_hash);
+			LIST_INSERT_HEAD(&clp->lc_session, nsep, sess_list);
+			nsep->sess_clp = clp;
+			NFSUNLOCKSESSION(shp);
+		    }
 		}
 	} else if (clp->lc_flags & LCL_NEEDSCONFIRM) {
 		error = NFSERR_EXPIRED;
@@ -546,6 +625,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 +1130,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 +1576,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 +1589,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 +1679,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 +1692,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 +1939,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 +2048,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 +2149,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 +2176,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;
@@ -2252,8 +2410,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) {
 		/*
@@ -2506,7 +2664,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 +2685,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 +2750,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 +2789,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 +2844,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,6 +2855,9 @@ 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.
@@ -2708,7 +2869,7 @@ tryagain:
 			 LCL_CALLBACKSON &&
 			!NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) &&
 			NFSVNO_DELEGOK(vp)) {
-			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]
@@ -2733,7 +2894,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);
@@ -2762,7 +2923,7 @@ tryagain:
 			 LCL_CALLBACKSON &&
 			!NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) &&
 			NFSVNO_DELEGOK(vp)) {
-			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]
@@ -2800,7 +2961,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 +2975,9 @@ 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;
 		} else {
 			*rflagsp |= NFSV4OPEN_RESULTCONFIRM;
 			new_stp->ls_flags = NFSLCK_NEEDSCONFIRM;
@@ -2881,8 +3045,8 @@ nfsrv_openupdate(vnode_t vp, struct nfss
 	/*
 	 * 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 +3065,10 @@ nfsrv_openupdate(vnode_t vp, struct nfss
 		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 +3096,8 @@ nfsrv_openupdate(vnode_t vp, struct nfss
 	 * 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 +3113,9 @@ nfsrv_openupdate(vnode_t vp, struct nfss
 			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 +3152,9 @@ nfsrv_openupdate(vnode_t vp, struct nfss
 		}
 		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 +3176,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 +3208,8 @@ nfsrv_delegupdate(nfsquad_t clientid, nf
 	 * 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 +3218,8 @@ nfsrv_delegupdate(nfsquad_t clientid, nf
 	}
 	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 +3278,8 @@ nfsrv_releaselckown(struct nfsstate *new
 	/*
 	 * 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;
@@ -3420,6 +3597,9 @@ nfsrv_checkseqid(struct nfsrv_descript *
 {
 	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))
@@ -3638,7 +3818,7 @@ nfsrv_checkrestart(nfsquad_t clientid, u
 		goto out;
 
 	NFSLOCKSTATE();
-	ret = nfsrv_checkgrace(flags);
+	ret = nfsrv_checkgrace(NULL, NULL, flags);
 	NFSUNLOCKSTATE();
 
 out:
@@ -3650,11 +3830,14 @@ 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 ||
+	    (nd != NULL && clp != NULL && (nd->nd_flag & ND_NFSV41) != 0 &&
+	     (clp->lc_flags & LCL_RECLAIMCOMPLETE) != 0)) {
 		if (flags & NFSLCK_RECLAIM) {
 			error = NFSERR_NOGRACE;
 			goto out;
@@ -5282,3 +5465,216 @@ 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;
+
+	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();
+	NFSUNLOCKSESSION(shp);
+	*sflagsp = 0;
+	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) {
+		LIST_REMOVE(sep, sess_hash);
+		LIST_REMOVE(sep, sess_list);
+	}
+	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);
+	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);
+}
+


More information about the svn-src-projects mailing list