From owner-svn-src-projects@FreeBSD.ORG Wed Jul 17 23:03:11 2013 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) by hub.freebsd.org (Postfix) with ESMTP id 4E06330F; Wed, 17 Jul 2013 23:03:11 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 303FE1CC; Wed, 17 Jul 2013 23:03:11 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.7/8.14.7) with ESMTP id r6HN3BnF069631; Wed, 17 Jul 2013 23:03:11 GMT (envelope-from rmacklem@svn.freebsd.org) Received: (from rmacklem@localhost) by svn.freebsd.org (8.14.7/8.14.5/Submit) id r6HN3BEx069630; Wed, 17 Jul 2013 23:03:11 GMT (envelope-from rmacklem@svn.freebsd.org) Message-Id: <201307172303.r6HN3BEx069630@svn.freebsd.org> From: Rick Macklem Date: Wed, 17 Jul 2013 23:03:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r253435 - projects/nfsv4.1-server/sys/fs/nfsserver X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 17 Jul 2013 23:03:11 -0000 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); +} +