1191783Srmacklem/*- 2191783Srmacklem * Copyright (c) 2009 Rick Macklem, University of Guelph 3191783Srmacklem * All rights reserved. 4191783Srmacklem * 5191783Srmacklem * Redistribution and use in source and binary forms, with or without 6191783Srmacklem * modification, are permitted provided that the following conditions 7191783Srmacklem * are met: 8191783Srmacklem * 1. Redistributions of source code must retain the above copyright 9191783Srmacklem * notice, this list of conditions and the following disclaimer. 10191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright 11191783Srmacklem * notice, this list of conditions and the following disclaimer in the 12191783Srmacklem * documentation and/or other materials provided with the distribution. 13191783Srmacklem * 14191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17191783Srmacklem * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24191783Srmacklem * SUCH DAMAGE. 25191783Srmacklem * 26191783Srmacklem */ 27191783Srmacklem 28191783Srmacklem#include <sys/cdefs.h> 29191783Srmacklem__FBSDID("$FreeBSD$"); 30191783Srmacklem 31191783Srmacklem#ifndef APPLEKEXT 32191783Srmacklem#include <fs/nfs/nfsport.h> 33191783Srmacklem 34191783Srmacklemstruct nfsrv_stablefirst nfsrv_stablefirst; 35191783Srmacklemint nfsrv_issuedelegs = 0; 36203849Srmacklemint nfsrv_dolocallocks = 0; 37191783Srmacklemstruct nfsv4lock nfsv4rootfs_lock; 38191783Srmacklem 39191783Srmacklemextern int newnfs_numnfsd; 40191783Srmacklemextern struct nfsstats newnfsstats; 41191783Srmacklemextern int nfsrv_lease; 42191783Srmacklemextern struct timeval nfsboottime; 43191783Srmacklemextern u_int32_t newnfs_true, newnfs_false; 44191783SrmacklemNFSV4ROOTLOCKMUTEX; 45191783SrmacklemNFSSTATESPINLOCK; 46191783Srmacklem 47191783Srmacklem/* 48210030Srmacklem * Hash lists for nfs V4. 49191783Srmacklem * (Some would put them in the .h file, but I don't like declaring storage 50191783Srmacklem * in a .h) 51191783Srmacklem */ 52191783Srmacklemstruct nfsclienthashhead nfsclienthash[NFSCLIENTHASHSIZE]; 53191783Srmacklemstruct nfslockhashhead nfslockhash[NFSLOCKHASHSIZE]; 54191783Srmacklem#endif /* !APPLEKEXT */ 55191783Srmacklem 56191783Srmacklemstatic u_int32_t nfsrv_openpluslock = 0, nfsrv_delegatecnt = 0; 57191783Srmacklemstatic time_t nfsrvboottime; 58191783Srmacklemstatic int nfsrv_writedelegifpos = 1; 59191783Srmacklemstatic int nfsrv_returnoldstateid = 0, nfsrv_clients = 0; 60191783Srmacklemstatic int nfsrv_clienthighwater = NFSRV_CLIENTHIGHWATER; 61191783Srmacklemstatic int nfsrv_nogsscallback = 0; 62191783Srmacklem 63191783Srmacklem/* local functions */ 64191783Srmacklemstatic void nfsrv_dumpaclient(struct nfsclient *clp, 65191783Srmacklem struct nfsd_dumpclients *dumpp); 66191783Srmacklemstatic void nfsrv_freeopenowner(struct nfsstate *stp, int cansleep, 67191783Srmacklem NFSPROC_T *p); 68205941Srmacklemstatic int nfsrv_freeopen(struct nfsstate *stp, vnode_t vp, int cansleep, 69205941Srmacklem NFSPROC_T *p); 70205941Srmacklemstatic void nfsrv_freelockowner(struct nfsstate *stp, vnode_t vp, int cansleep, 71205941Srmacklem NFSPROC_T *p); 72205941Srmacklemstatic void nfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, 73191783Srmacklem int cansleep, NFSPROC_T *p); 74191783Srmacklemstatic void nfsrv_freenfslock(struct nfslock *lop); 75191783Srmacklemstatic void nfsrv_freenfslockfile(struct nfslockfile *lfp); 76191783Srmacklemstatic void nfsrv_freedeleg(struct nfsstate *); 77191783Srmacklemstatic int nfsrv_getstate(struct nfsclient *clp, nfsv4stateid_t *stateidp, 78191783Srmacklem u_int32_t flags, struct nfsstate **stpp); 79191783Srmacklemstatic void nfsrv_getowner(struct nfsstatehead *hp, struct nfsstate *new_stp, 80191783Srmacklem struct nfsstate **stpp); 81191783Srmacklemstatic int nfsrv_getlockfh(vnode_t vp, u_short flags, 82191783Srmacklem struct nfslockfile **new_lfpp, fhandle_t *nfhp, NFSPROC_T *p); 83205941Srmacklemstatic int nfsrv_getlockfile(u_short flags, struct nfslockfile **new_lfpp, 84205941Srmacklem struct nfslockfile **lfpp, fhandle_t *nfhp, int lockit); 85191783Srmacklemstatic void nfsrv_insertlock(struct nfslock *new_lop, 86191783Srmacklem struct nfslock *insert_lop, struct nfsstate *stp, struct nfslockfile *lfp); 87191783Srmacklemstatic void nfsrv_updatelock(struct nfsstate *stp, struct nfslock **new_lopp, 88191783Srmacklem struct nfslock **other_lopp, struct nfslockfile *lfp); 89191783Srmacklemstatic int nfsrv_getipnumber(u_char *cp); 90191783Srmacklemstatic int nfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, 91191783Srmacklem nfsv4stateid_t *stateidp, int specialid); 92191783Srmacklemstatic int nfsrv_checkgrace(u_int32_t flags); 93191783Srmacklemstatic int nfsrv_docallback(struct nfsclient *clp, int procnum, 94191783Srmacklem nfsv4stateid_t *stateidp, int trunc, fhandle_t *fhp, 95191783Srmacklem struct nfsvattr *nap, nfsattrbit_t *attrbitp, NFSPROC_T *p); 96191783Srmacklemstatic u_int32_t nfsrv_nextclientindex(void); 97191783Srmacklemstatic u_int32_t nfsrv_nextstateindex(struct nfsclient *clp); 98191783Srmacklemstatic void nfsrv_markstable(struct nfsclient *clp); 99191783Srmacklemstatic int nfsrv_checkstable(struct nfsclient *clp); 100191783Srmacklemstatic int nfsrv_clientconflict(struct nfsclient *clp, int *haslockp, struct 101191783Srmacklem vnode *vp, NFSPROC_T *p); 102191783Srmacklemstatic int nfsrv_delegconflict(struct nfsstate *stp, int *haslockp, 103191783Srmacklem NFSPROC_T *p, vnode_t vp); 104191783Srmacklemstatic int nfsrv_cleandeleg(vnode_t vp, struct nfslockfile *lfp, 105191783Srmacklem struct nfsclient *clp, int *haslockp, NFSPROC_T *p); 106191783Srmacklemstatic int nfsrv_notsamecredname(struct nfsrv_descript *nd, 107191783Srmacklem struct nfsclient *clp); 108191783Srmacklemstatic time_t nfsrv_leaseexpiry(void); 109191783Srmacklemstatic void nfsrv_delaydelegtimeout(struct nfsstate *stp); 110191783Srmacklemstatic int nfsrv_checkseqid(struct nfsrv_descript *nd, u_int32_t seqid, 111191783Srmacklem struct nfsstate *stp, struct nfsrvcache *op); 112205941Srmacklemstatic int nfsrv_nootherstate(struct nfsstate *stp); 113205941Srmacklemstatic int nfsrv_locallock(vnode_t vp, struct nfslockfile *lfp, int flags, 114205941Srmacklem uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p); 115205941Srmacklemstatic void nfsrv_localunlock(vnode_t vp, struct nfslockfile *lfp, 116205941Srmacklem uint64_t init_first, uint64_t init_end, NFSPROC_T *p); 117205941Srmacklemstatic int nfsrv_dolocal(vnode_t vp, struct nfslockfile *lfp, int flags, 118205941Srmacklem int oldflags, uint64_t first, uint64_t end, struct nfslockconflict *cfp, 119191783Srmacklem NFSPROC_T *p); 120205941Srmacklemstatic void nfsrv_locallock_rollback(vnode_t vp, struct nfslockfile *lfp, 121205941Srmacklem NFSPROC_T *p); 122205941Srmacklemstatic void nfsrv_locallock_commit(struct nfslockfile *lfp, int flags, 123205941Srmacklem uint64_t first, uint64_t end); 124205941Srmacklemstatic void nfsrv_locklf(struct nfslockfile *lfp); 125205941Srmacklemstatic void nfsrv_unlocklf(struct nfslockfile *lfp); 126191783Srmacklem 127191783Srmacklem/* 128191783Srmacklem * Scan the client list for a match and either return the current one, 129191783Srmacklem * create a new entry or return an error. 130191783Srmacklem * If returning a non-error, the clp structure must either be linked into 131191783Srmacklem * the client list or free'd. 132191783Srmacklem */ 133191783SrmacklemAPPLESTATIC int 134191783Srmacklemnfsrv_setclient(struct nfsrv_descript *nd, struct nfsclient **new_clpp, 135191783Srmacklem nfsquad_t *clientidp, nfsquad_t *confirmp, NFSPROC_T *p) 136191783Srmacklem{ 137191783Srmacklem struct nfsclient *clp = NULL, *new_clp = *new_clpp; 138224086Szack int i, error = 0; 139191783Srmacklem struct nfsstate *stp, *tstp; 140191783Srmacklem struct sockaddr_in *sad, *rad; 141191783Srmacklem int zapit = 0, gotit, hasstate = 0, igotlock; 142191783Srmacklem static u_int64_t confirm_index = 0; 143191783Srmacklem 144191783Srmacklem /* 145191783Srmacklem * Check for state resource limit exceeded. 146191783Srmacklem */ 147224086Szack if (nfsrv_openpluslock > NFSRV_V4STATELIMIT) { 148224086Szack error = NFSERR_RESOURCE; 149224086Szack goto out; 150224086Szack } 151191783Srmacklem 152216330Srmacklem if (nfsrv_issuedelegs == 0 || 153216330Srmacklem ((nd->nd_flag & ND_GSS) != 0 && nfsrv_nogsscallback != 0)) 154191783Srmacklem /* 155216330Srmacklem * Don't do callbacks when delegations are disabled or 156216330Srmacklem * for AUTH_GSS unless enabled via nfsrv_nogsscallback. 157216330Srmacklem * If establishing a callback connection is attempted 158216330Srmacklem * when a firewall is blocking the callback path, the 159216330Srmacklem * server may wait too long for the connect attempt to 160216330Srmacklem * succeed during the Open. Some clients, such as Linux, 161216330Srmacklem * may timeout and give up on the Open before the server 162216330Srmacklem * replies. Also, since AUTH_GSS callbacks are not 163216330Srmacklem * yet interoperability tested, they might cause the 164216330Srmacklem * server to crap out, if they get past the Init call to 165216330Srmacklem * the client. 166191783Srmacklem */ 167191783Srmacklem new_clp->lc_program = 0; 168191783Srmacklem 169191783Srmacklem /* Lock out other nfsd threads */ 170191783Srmacklem NFSLOCKV4ROOTMUTEX(); 171191783Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 172191783Srmacklem do { 173191783Srmacklem igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, 174222389Srmacklem NFSV4ROOTLOCKMUTEXPTR, NULL); 175191783Srmacklem } while (!igotlock); 176191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 177191783Srmacklem 178191783Srmacklem /* 179191783Srmacklem * Search for a match in the client list. 180191783Srmacklem */ 181191783Srmacklem gotit = i = 0; 182191783Srmacklem while (i < NFSCLIENTHASHSIZE && !gotit) { 183191783Srmacklem LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { 184191783Srmacklem if (new_clp->lc_idlen == clp->lc_idlen && 185191783Srmacklem !NFSBCMP(new_clp->lc_id, clp->lc_id, clp->lc_idlen)) { 186191783Srmacklem gotit = 1; 187191783Srmacklem break; 188191783Srmacklem } 189191783Srmacklem } 190191783Srmacklem i++; 191191783Srmacklem } 192191783Srmacklem if (!gotit || 193191783Srmacklem (clp->lc_flags & (LCL_NEEDSCONFIRM | LCL_ADMINREVOKED))) { 194191783Srmacklem /* 195191783Srmacklem * Get rid of the old one. 196191783Srmacklem */ 197191783Srmacklem if (i != NFSCLIENTHASHSIZE) { 198191783Srmacklem LIST_REMOVE(clp, lc_hash); 199191783Srmacklem nfsrv_cleanclient(clp, p); 200191783Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 201191783Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 202191783Srmacklem zapit = 1; 203191783Srmacklem } 204191783Srmacklem /* 205191783Srmacklem * Add it after assigning a client id to it. 206191783Srmacklem */ 207191783Srmacklem new_clp->lc_flags |= LCL_NEEDSCONFIRM; 208191783Srmacklem confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; 209191783Srmacklem clientidp->lval[0] = new_clp->lc_clientid.lval[0] = 210191783Srmacklem (u_int32_t)nfsrvboottime; 211191783Srmacklem clientidp->lval[1] = new_clp->lc_clientid.lval[1] = 212191783Srmacklem nfsrv_nextclientindex(); 213191783Srmacklem new_clp->lc_stateindex = 0; 214191783Srmacklem new_clp->lc_statemaxindex = 0; 215191783Srmacklem new_clp->lc_cbref = 0; 216191783Srmacklem new_clp->lc_expiry = nfsrv_leaseexpiry(); 217191783Srmacklem LIST_INIT(&new_clp->lc_open); 218191783Srmacklem LIST_INIT(&new_clp->lc_deleg); 219191783Srmacklem LIST_INIT(&new_clp->lc_olddeleg); 220191783Srmacklem for (i = 0; i < NFSSTATEHASHSIZE; i++) 221191783Srmacklem LIST_INIT(&new_clp->lc_stateid[i]); 222191783Srmacklem LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, 223191783Srmacklem lc_hash); 224191783Srmacklem newnfsstats.srvclients++; 225191783Srmacklem nfsrv_openpluslock++; 226191783Srmacklem nfsrv_clients++; 227191783Srmacklem NFSLOCKV4ROOTMUTEX(); 228191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 229191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 230191783Srmacklem if (zapit) 231191783Srmacklem nfsrv_zapclient(clp, p); 232191783Srmacklem *new_clpp = NULL; 233224086Szack goto out; 234191783Srmacklem } 235191783Srmacklem 236191783Srmacklem /* 237191783Srmacklem * Now, handle the cases where the id is already issued. 238191783Srmacklem */ 239191783Srmacklem if (nfsrv_notsamecredname(nd, clp)) { 240191783Srmacklem /* 241191783Srmacklem * Check to see if there is expired state that should go away. 242191783Srmacklem */ 243191783Srmacklem if (clp->lc_expiry < NFSD_MONOSEC && 244191783Srmacklem (!LIST_EMPTY(&clp->lc_open) || !LIST_EMPTY(&clp->lc_deleg))) { 245191783Srmacklem nfsrv_cleanclient(clp, p); 246191783Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 247191783Srmacklem } 248191783Srmacklem 249191783Srmacklem /* 250191783Srmacklem * If there is outstanding state, then reply NFSERR_CLIDINUSE per 251191783Srmacklem * RFC3530 Sec. 8.1.2 last para. 252191783Srmacklem */ 253191783Srmacklem if (!LIST_EMPTY(&clp->lc_deleg)) { 254191783Srmacklem hasstate = 1; 255191783Srmacklem } else if (LIST_EMPTY(&clp->lc_open)) { 256191783Srmacklem hasstate = 0; 257191783Srmacklem } else { 258191783Srmacklem hasstate = 0; 259191783Srmacklem /* Look for an Open on the OpenOwner */ 260191783Srmacklem LIST_FOREACH(stp, &clp->lc_open, ls_list) { 261191783Srmacklem if (!LIST_EMPTY(&stp->ls_open)) { 262191783Srmacklem hasstate = 1; 263191783Srmacklem break; 264191783Srmacklem } 265191783Srmacklem } 266191783Srmacklem } 267191783Srmacklem if (hasstate) { 268191783Srmacklem /* 269191783Srmacklem * If the uid doesn't match, return NFSERR_CLIDINUSE after 270191783Srmacklem * filling out the correct ipaddr and portnum. 271191783Srmacklem */ 272191783Srmacklem sad = NFSSOCKADDR(new_clp->lc_req.nr_nam, struct sockaddr_in *); 273191783Srmacklem rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *); 274191783Srmacklem sad->sin_addr.s_addr = rad->sin_addr.s_addr; 275191783Srmacklem sad->sin_port = rad->sin_port; 276191783Srmacklem NFSLOCKV4ROOTMUTEX(); 277191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 278191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 279224086Szack error = NFSERR_CLIDINUSE; 280224086Szack goto out; 281191783Srmacklem } 282191783Srmacklem } 283191783Srmacklem 284191783Srmacklem if (NFSBCMP(new_clp->lc_verf, clp->lc_verf, NFSX_VERF)) { 285191783Srmacklem /* 286191783Srmacklem * If the verifier has changed, the client has rebooted 287191783Srmacklem * and a new client id is issued. The old state info 288191783Srmacklem * can be thrown away once the SETCLIENTID_CONFIRM occurs. 289191783Srmacklem */ 290191783Srmacklem LIST_REMOVE(clp, lc_hash); 291191783Srmacklem new_clp->lc_flags |= LCL_NEEDSCONFIRM; 292191783Srmacklem confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; 293191783Srmacklem clientidp->lval[0] = new_clp->lc_clientid.lval[0] = 294191783Srmacklem nfsrvboottime; 295191783Srmacklem clientidp->lval[1] = new_clp->lc_clientid.lval[1] = 296191783Srmacklem nfsrv_nextclientindex(); 297191783Srmacklem new_clp->lc_stateindex = 0; 298191783Srmacklem new_clp->lc_statemaxindex = 0; 299191783Srmacklem new_clp->lc_cbref = 0; 300191783Srmacklem new_clp->lc_expiry = nfsrv_leaseexpiry(); 301191783Srmacklem 302191783Srmacklem /* 303191783Srmacklem * Save the state until confirmed. 304191783Srmacklem */ 305191783Srmacklem LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); 306191783Srmacklem LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) 307191783Srmacklem tstp->ls_clp = new_clp; 308191783Srmacklem LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); 309191783Srmacklem LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) 310191783Srmacklem tstp->ls_clp = new_clp; 311191783Srmacklem LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, 312191783Srmacklem ls_list); 313191783Srmacklem LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) 314191783Srmacklem tstp->ls_clp = new_clp; 315191783Srmacklem for (i = 0; i < NFSSTATEHASHSIZE; i++) { 316191783Srmacklem LIST_NEWHEAD(&new_clp->lc_stateid[i], 317191783Srmacklem &clp->lc_stateid[i], ls_hash); 318230100Srmacklem LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) 319191783Srmacklem tstp->ls_clp = new_clp; 320191783Srmacklem } 321191783Srmacklem LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, 322191783Srmacklem lc_hash); 323191783Srmacklem newnfsstats.srvclients++; 324191783Srmacklem nfsrv_openpluslock++; 325191783Srmacklem nfsrv_clients++; 326191783Srmacklem NFSLOCKV4ROOTMUTEX(); 327191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 328191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 329191783Srmacklem 330191783Srmacklem /* 331191783Srmacklem * Must wait until any outstanding callback on the old clp 332191783Srmacklem * completes. 333191783Srmacklem */ 334235381Srmacklem NFSLOCKSTATE(); 335191783Srmacklem while (clp->lc_cbref) { 336191783Srmacklem clp->lc_flags |= LCL_WAKEUPWANTED; 337235381Srmacklem (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, 338191783Srmacklem "nfsd clp", 10 * hz); 339191783Srmacklem } 340235381Srmacklem NFSUNLOCKSTATE(); 341191783Srmacklem nfsrv_zapclient(clp, p); 342191783Srmacklem *new_clpp = NULL; 343224086Szack goto out; 344191783Srmacklem } 345191783Srmacklem /* 346191783Srmacklem * id and verifier match, so update the net address info 347191783Srmacklem * and get rid of any existing callback authentication 348191783Srmacklem * handle, so a new one will be acquired. 349191783Srmacklem */ 350191783Srmacklem LIST_REMOVE(clp, lc_hash); 351191783Srmacklem new_clp->lc_flags |= (LCL_NEEDSCONFIRM | LCL_DONTCLEAN); 352191783Srmacklem new_clp->lc_expiry = nfsrv_leaseexpiry(); 353191783Srmacklem confirmp->qval = new_clp->lc_confirm.qval = ++confirm_index; 354191783Srmacklem clientidp->lval[0] = new_clp->lc_clientid.lval[0] = 355191783Srmacklem clp->lc_clientid.lval[0]; 356191783Srmacklem clientidp->lval[1] = new_clp->lc_clientid.lval[1] = 357191783Srmacklem clp->lc_clientid.lval[1]; 358191783Srmacklem new_clp->lc_delegtime = clp->lc_delegtime; 359191783Srmacklem new_clp->lc_stateindex = clp->lc_stateindex; 360191783Srmacklem new_clp->lc_statemaxindex = clp->lc_statemaxindex; 361191783Srmacklem new_clp->lc_cbref = 0; 362191783Srmacklem LIST_NEWHEAD(&new_clp->lc_open, &clp->lc_open, ls_list); 363191783Srmacklem LIST_FOREACH(tstp, &new_clp->lc_open, ls_list) 364191783Srmacklem tstp->ls_clp = new_clp; 365191783Srmacklem LIST_NEWHEAD(&new_clp->lc_deleg, &clp->lc_deleg, ls_list); 366191783Srmacklem LIST_FOREACH(tstp, &new_clp->lc_deleg, ls_list) 367191783Srmacklem tstp->ls_clp = new_clp; 368191783Srmacklem LIST_NEWHEAD(&new_clp->lc_olddeleg, &clp->lc_olddeleg, ls_list); 369191783Srmacklem LIST_FOREACH(tstp, &new_clp->lc_olddeleg, ls_list) 370191783Srmacklem tstp->ls_clp = new_clp; 371191783Srmacklem for (i = 0; i < NFSSTATEHASHSIZE; i++) { 372191783Srmacklem LIST_NEWHEAD(&new_clp->lc_stateid[i], &clp->lc_stateid[i], 373191783Srmacklem ls_hash); 374230100Srmacklem LIST_FOREACH(tstp, &new_clp->lc_stateid[i], ls_hash) 375191783Srmacklem tstp->ls_clp = new_clp; 376191783Srmacklem } 377191783Srmacklem LIST_INSERT_HEAD(NFSCLIENTHASH(new_clp->lc_clientid), new_clp, 378191783Srmacklem lc_hash); 379191783Srmacklem newnfsstats.srvclients++; 380191783Srmacklem nfsrv_openpluslock++; 381191783Srmacklem nfsrv_clients++; 382191783Srmacklem NFSLOCKV4ROOTMUTEX(); 383191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 384191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 385191783Srmacklem 386191783Srmacklem /* 387191783Srmacklem * Must wait until any outstanding callback on the old clp 388191783Srmacklem * completes. 389191783Srmacklem */ 390235381Srmacklem NFSLOCKSTATE(); 391191783Srmacklem while (clp->lc_cbref) { 392191783Srmacklem clp->lc_flags |= LCL_WAKEUPWANTED; 393235381Srmacklem (void)mtx_sleep(clp, NFSSTATEMUTEXPTR, PZERO - 1, "nfsd clp", 394235381Srmacklem 10 * hz); 395191783Srmacklem } 396235381Srmacklem NFSUNLOCKSTATE(); 397191783Srmacklem nfsrv_zapclient(clp, p); 398191783Srmacklem *new_clpp = NULL; 399224086Szack 400224086Szackout: 401224086Szack NFSEXITCODE2(error, nd); 402224086Szack return (error); 403191783Srmacklem} 404191783Srmacklem 405191783Srmacklem/* 406191783Srmacklem * Check to see if the client id exists and optionally confirm it. 407191783Srmacklem */ 408191783SrmacklemAPPLESTATIC int 409191783Srmacklemnfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp, 410191783Srmacklem nfsquad_t confirm, struct nfsrv_descript *nd, NFSPROC_T *p) 411191783Srmacklem{ 412191783Srmacklem struct nfsclient *clp; 413191783Srmacklem struct nfsstate *stp; 414191783Srmacklem int i; 415191783Srmacklem struct nfsclienthashhead *hp; 416191783Srmacklem int error = 0, igotlock, doneok; 417191783Srmacklem 418191783Srmacklem if (clpp) 419191783Srmacklem *clpp = NULL; 420224086Szack if (nfsrvboottime != clientid.lval[0]) { 421224086Szack error = NFSERR_STALECLIENTID; 422224086Szack goto out; 423224086Szack } 424191783Srmacklem 425191783Srmacklem /* 426191783Srmacklem * If called with opflags == CLOPS_RENEW, the State Lock is 427191783Srmacklem * already held. Otherwise, we need to get either that or, 428191783Srmacklem * for the case of Confirm, lock out the nfsd threads. 429191783Srmacklem */ 430191783Srmacklem if (opflags & CLOPS_CONFIRM) { 431191783Srmacklem NFSLOCKV4ROOTMUTEX(); 432191783Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 433191783Srmacklem do { 434191783Srmacklem igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, 435222389Srmacklem NFSV4ROOTLOCKMUTEXPTR, NULL); 436191783Srmacklem } while (!igotlock); 437191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 438191783Srmacklem } else if (opflags != CLOPS_RENEW) { 439191783Srmacklem NFSLOCKSTATE(); 440191783Srmacklem } 441191783Srmacklem 442191783Srmacklem hp = NFSCLIENTHASH(clientid); 443191783Srmacklem LIST_FOREACH(clp, hp, lc_hash) { 444191783Srmacklem if (clp->lc_clientid.lval[1] == clientid.lval[1]) 445191783Srmacklem break; 446191783Srmacklem } 447191783Srmacklem if (clp == LIST_END(hp)) { 448191783Srmacklem if (opflags & CLOPS_CONFIRM) 449191783Srmacklem error = NFSERR_STALECLIENTID; 450191783Srmacklem else 451191783Srmacklem error = NFSERR_EXPIRED; 452191783Srmacklem } else if (clp->lc_flags & LCL_ADMINREVOKED) { 453191783Srmacklem /* 454191783Srmacklem * If marked admin revoked, just return the error. 455191783Srmacklem */ 456191783Srmacklem error = NFSERR_ADMINREVOKED; 457191783Srmacklem } 458191783Srmacklem if (error) { 459191783Srmacklem if (opflags & CLOPS_CONFIRM) { 460191783Srmacklem NFSLOCKV4ROOTMUTEX(); 461191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 462191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 463191783Srmacklem } else if (opflags != CLOPS_RENEW) { 464191783Srmacklem NFSUNLOCKSTATE(); 465191783Srmacklem } 466224086Szack goto out; 467191783Srmacklem } 468191783Srmacklem 469191783Srmacklem /* 470191783Srmacklem * Perform any operations specified by the opflags. 471191783Srmacklem */ 472191783Srmacklem if (opflags & CLOPS_CONFIRM) { 473191783Srmacklem if (clp->lc_confirm.qval != confirm.qval) 474191783Srmacklem error = NFSERR_STALECLIENTID; 475191783Srmacklem else if (nfsrv_notsamecredname(nd, clp)) 476191783Srmacklem error = NFSERR_CLIDINUSE; 477191783Srmacklem 478191783Srmacklem if (!error) { 479191783Srmacklem if ((clp->lc_flags & (LCL_NEEDSCONFIRM | LCL_DONTCLEAN)) == 480191783Srmacklem LCL_NEEDSCONFIRM) { 481191783Srmacklem /* 482191783Srmacklem * Hang onto the delegations (as old delegations) 483191783Srmacklem * for an Open with CLAIM_DELEGATE_PREV unless in 484191783Srmacklem * grace, but get rid of the rest of the state. 485191783Srmacklem */ 486191783Srmacklem nfsrv_cleanclient(clp, p); 487191783Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 488191783Srmacklem if (nfsrv_checkgrace(0)) { 489191783Srmacklem /* In grace, so just delete delegations */ 490191783Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 491191783Srmacklem } else { 492191783Srmacklem LIST_FOREACH(stp, &clp->lc_deleg, ls_list) 493191783Srmacklem stp->ls_flags |= NFSLCK_OLDDELEG; 494191783Srmacklem clp->lc_delegtime = NFSD_MONOSEC + 495191783Srmacklem nfsrv_lease + NFSRV_LEASEDELTA; 496191783Srmacklem LIST_NEWHEAD(&clp->lc_olddeleg, &clp->lc_deleg, 497191783Srmacklem ls_list); 498191783Srmacklem } 499191783Srmacklem } 500191783Srmacklem clp->lc_flags &= ~(LCL_NEEDSCONFIRM | LCL_DONTCLEAN); 501191783Srmacklem if (clp->lc_program) 502191783Srmacklem clp->lc_flags |= LCL_NEEDSCBNULL; 503191783Srmacklem } 504191783Srmacklem } else if (clp->lc_flags & LCL_NEEDSCONFIRM) { 505191783Srmacklem error = NFSERR_EXPIRED; 506191783Srmacklem } 507191783Srmacklem 508191783Srmacklem /* 509191783Srmacklem * If called by the Renew Op, we must check the principal. 510191783Srmacklem */ 511191783Srmacklem if (!error && (opflags & CLOPS_RENEWOP)) { 512191783Srmacklem if (nfsrv_notsamecredname(nd, clp)) { 513191783Srmacklem doneok = 0; 514191783Srmacklem for (i = 0; i < NFSSTATEHASHSIZE && doneok == 0; i++) { 515191783Srmacklem LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { 516191783Srmacklem if ((stp->ls_flags & NFSLCK_OPEN) && 517191783Srmacklem stp->ls_uid == nd->nd_cred->cr_uid) { 518191783Srmacklem doneok = 1; 519191783Srmacklem break; 520191783Srmacklem } 521191783Srmacklem } 522191783Srmacklem } 523191783Srmacklem if (!doneok) 524191783Srmacklem error = NFSERR_ACCES; 525191783Srmacklem } 526191783Srmacklem if (!error && (clp->lc_flags & LCL_CBDOWN)) 527191783Srmacklem error = NFSERR_CBPATHDOWN; 528191783Srmacklem } 529191783Srmacklem if ((!error || error == NFSERR_CBPATHDOWN) && 530191783Srmacklem (opflags & CLOPS_RENEW)) { 531191783Srmacklem clp->lc_expiry = nfsrv_leaseexpiry(); 532191783Srmacklem } 533191783Srmacklem if (opflags & CLOPS_CONFIRM) { 534191783Srmacklem NFSLOCKV4ROOTMUTEX(); 535191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 536191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 537191783Srmacklem } else if (opflags != CLOPS_RENEW) { 538191783Srmacklem NFSUNLOCKSTATE(); 539191783Srmacklem } 540191783Srmacklem if (clpp) 541191783Srmacklem *clpp = clp; 542224086Szack 543224086Szackout: 544224086Szack NFSEXITCODE2(error, nd); 545191783Srmacklem return (error); 546191783Srmacklem} 547191783Srmacklem 548191783Srmacklem/* 549191783Srmacklem * Called from the new nfssvc syscall to admin revoke a clientid. 550191783Srmacklem * Returns 0 for success, error otherwise. 551191783Srmacklem */ 552191783SrmacklemAPPLESTATIC int 553191783Srmacklemnfsrv_adminrevoke(struct nfsd_clid *revokep, NFSPROC_T *p) 554191783Srmacklem{ 555191783Srmacklem struct nfsclient *clp = NULL; 556224086Szack int i, error = 0; 557191783Srmacklem int gotit, igotlock; 558191783Srmacklem 559191783Srmacklem /* 560191783Srmacklem * First, lock out the nfsd so that state won't change while the 561191783Srmacklem * revocation record is being written to the stable storage restart 562191783Srmacklem * file. 563191783Srmacklem */ 564191783Srmacklem NFSLOCKV4ROOTMUTEX(); 565191783Srmacklem do { 566191783Srmacklem igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, 567222389Srmacklem NFSV4ROOTLOCKMUTEXPTR, NULL); 568191783Srmacklem } while (!igotlock); 569191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 570191783Srmacklem 571191783Srmacklem /* 572191783Srmacklem * Search for a match in the client list. 573191783Srmacklem */ 574191783Srmacklem gotit = i = 0; 575191783Srmacklem while (i < NFSCLIENTHASHSIZE && !gotit) { 576191783Srmacklem LIST_FOREACH(clp, &nfsclienthash[i], lc_hash) { 577191783Srmacklem if (revokep->nclid_idlen == clp->lc_idlen && 578191783Srmacklem !NFSBCMP(revokep->nclid_id, clp->lc_id, clp->lc_idlen)) { 579191783Srmacklem gotit = 1; 580191783Srmacklem break; 581191783Srmacklem } 582191783Srmacklem } 583191783Srmacklem i++; 584191783Srmacklem } 585191783Srmacklem if (!gotit) { 586191783Srmacklem NFSLOCKV4ROOTMUTEX(); 587191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 0); 588191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 589224086Szack error = EPERM; 590224086Szack goto out; 591191783Srmacklem } 592191783Srmacklem 593191783Srmacklem /* 594191783Srmacklem * Now, write out the revocation record 595191783Srmacklem */ 596191783Srmacklem nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p); 597217432Srmacklem nfsrv_backupstable(); 598191783Srmacklem 599191783Srmacklem /* 600191783Srmacklem * and clear out the state, marking the clientid revoked. 601191783Srmacklem */ 602191783Srmacklem clp->lc_flags &= ~LCL_CALLBACKSON; 603191783Srmacklem clp->lc_flags |= LCL_ADMINREVOKED; 604191783Srmacklem nfsrv_cleanclient(clp, p); 605191783Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 606191783Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 607191783Srmacklem NFSLOCKV4ROOTMUTEX(); 608191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 0); 609191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 610224086Szack 611224086Szackout: 612224086Szack NFSEXITCODE(error); 613224086Szack return (error); 614191783Srmacklem} 615191783Srmacklem 616191783Srmacklem/* 617191783Srmacklem * Dump out stats for all clients. Called from nfssvc(2), that is used 618191783Srmacklem * newnfsstats. 619191783Srmacklem */ 620191783SrmacklemAPPLESTATIC void 621191783Srmacklemnfsrv_dumpclients(struct nfsd_dumpclients *dumpp, int maxcnt) 622191783Srmacklem{ 623191783Srmacklem struct nfsclient *clp; 624191783Srmacklem int i = 0, cnt = 0; 625191783Srmacklem 626210178Srmacklem /* 627210178Srmacklem * First, get a reference on the nfsv4rootfs_lock so that an 628210178Srmacklem * exclusive lock cannot be acquired while dumping the clients. 629210178Srmacklem */ 630210178Srmacklem NFSLOCKV4ROOTMUTEX(); 631222389Srmacklem nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); 632210178Srmacklem NFSUNLOCKV4ROOTMUTEX(); 633191783Srmacklem NFSLOCKSTATE(); 634191783Srmacklem /* 635191783Srmacklem * Rattle through the client lists until done. 636191783Srmacklem */ 637191783Srmacklem while (i < NFSCLIENTHASHSIZE && cnt < maxcnt) { 638191783Srmacklem clp = LIST_FIRST(&nfsclienthash[i]); 639191783Srmacklem while (clp != LIST_END(&nfsclienthash[i]) && cnt < maxcnt) { 640191783Srmacklem nfsrv_dumpaclient(clp, &dumpp[cnt]); 641191783Srmacklem cnt++; 642191783Srmacklem clp = LIST_NEXT(clp, lc_hash); 643191783Srmacklem } 644191783Srmacklem i++; 645191783Srmacklem } 646191783Srmacklem if (cnt < maxcnt) 647191783Srmacklem dumpp[cnt].ndcl_clid.nclid_idlen = 0; 648191783Srmacklem NFSUNLOCKSTATE(); 649210178Srmacklem NFSLOCKV4ROOTMUTEX(); 650210178Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 651210178Srmacklem NFSUNLOCKV4ROOTMUTEX(); 652191783Srmacklem} 653191783Srmacklem 654191783Srmacklem/* 655191783Srmacklem * Dump stats for a client. Must be called with the NFSSTATELOCK and spl'd. 656191783Srmacklem */ 657191783Srmacklemstatic void 658191783Srmacklemnfsrv_dumpaclient(struct nfsclient *clp, struct nfsd_dumpclients *dumpp) 659191783Srmacklem{ 660191783Srmacklem struct nfsstate *stp, *openstp, *lckownstp; 661191783Srmacklem struct nfslock *lop; 662191783Srmacklem struct sockaddr *sad; 663191783Srmacklem struct sockaddr_in *rad; 664191783Srmacklem struct sockaddr_in6 *rad6; 665191783Srmacklem 666191783Srmacklem dumpp->ndcl_nopenowners = dumpp->ndcl_nlockowners = 0; 667191783Srmacklem dumpp->ndcl_nopens = dumpp->ndcl_nlocks = 0; 668191783Srmacklem dumpp->ndcl_ndelegs = dumpp->ndcl_nolddelegs = 0; 669191783Srmacklem dumpp->ndcl_flags = clp->lc_flags; 670191783Srmacklem dumpp->ndcl_clid.nclid_idlen = clp->lc_idlen; 671191783Srmacklem NFSBCOPY(clp->lc_id, dumpp->ndcl_clid.nclid_id, clp->lc_idlen); 672191783Srmacklem sad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr *); 673191783Srmacklem dumpp->ndcl_addrfam = sad->sa_family; 674191783Srmacklem if (sad->sa_family == AF_INET) { 675191783Srmacklem rad = (struct sockaddr_in *)sad; 676191783Srmacklem dumpp->ndcl_cbaddr.sin_addr = rad->sin_addr; 677191783Srmacklem } else { 678191783Srmacklem rad6 = (struct sockaddr_in6 *)sad; 679191783Srmacklem dumpp->ndcl_cbaddr.sin6_addr = rad6->sin6_addr; 680191783Srmacklem } 681191783Srmacklem 682191783Srmacklem /* 683191783Srmacklem * Now, scan the state lists and total up the opens and locks. 684191783Srmacklem */ 685191783Srmacklem LIST_FOREACH(stp, &clp->lc_open, ls_list) { 686191783Srmacklem dumpp->ndcl_nopenowners++; 687191783Srmacklem LIST_FOREACH(openstp, &stp->ls_open, ls_list) { 688191783Srmacklem dumpp->ndcl_nopens++; 689191783Srmacklem LIST_FOREACH(lckownstp, &openstp->ls_open, ls_list) { 690191783Srmacklem dumpp->ndcl_nlockowners++; 691191783Srmacklem LIST_FOREACH(lop, &lckownstp->ls_lock, lo_lckowner) { 692191783Srmacklem dumpp->ndcl_nlocks++; 693191783Srmacklem } 694191783Srmacklem } 695191783Srmacklem } 696191783Srmacklem } 697191783Srmacklem 698191783Srmacklem /* 699191783Srmacklem * and the delegation lists. 700191783Srmacklem */ 701191783Srmacklem LIST_FOREACH(stp, &clp->lc_deleg, ls_list) { 702191783Srmacklem dumpp->ndcl_ndelegs++; 703191783Srmacklem } 704191783Srmacklem LIST_FOREACH(stp, &clp->lc_olddeleg, ls_list) { 705191783Srmacklem dumpp->ndcl_nolddelegs++; 706191783Srmacklem } 707191783Srmacklem} 708191783Srmacklem 709191783Srmacklem/* 710191783Srmacklem * Dump out lock stats for a file. 711191783Srmacklem */ 712191783SrmacklemAPPLESTATIC void 713191783Srmacklemnfsrv_dumplocks(vnode_t vp, struct nfsd_dumplocks *ldumpp, int maxcnt, 714191783Srmacklem NFSPROC_T *p) 715191783Srmacklem{ 716191783Srmacklem struct nfsstate *stp; 717191783Srmacklem struct nfslock *lop; 718191783Srmacklem int cnt = 0; 719191783Srmacklem struct nfslockfile *lfp; 720191783Srmacklem struct sockaddr *sad; 721191783Srmacklem struct sockaddr_in *rad; 722191783Srmacklem struct sockaddr_in6 *rad6; 723191783Srmacklem int ret; 724191783Srmacklem fhandle_t nfh; 725191783Srmacklem 726191783Srmacklem ret = nfsrv_getlockfh(vp, 0, NULL, &nfh, p); 727210178Srmacklem /* 728210178Srmacklem * First, get a reference on the nfsv4rootfs_lock so that an 729210178Srmacklem * exclusive lock on it cannot be acquired while dumping the locks. 730210178Srmacklem */ 731210178Srmacklem NFSLOCKV4ROOTMUTEX(); 732222389Srmacklem nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); 733210178Srmacklem NFSUNLOCKV4ROOTMUTEX(); 734191783Srmacklem NFSLOCKSTATE(); 735191783Srmacklem if (!ret) 736205941Srmacklem ret = nfsrv_getlockfile(0, NULL, &lfp, &nfh, 0); 737191783Srmacklem if (ret) { 738191783Srmacklem ldumpp[0].ndlck_clid.nclid_idlen = 0; 739191783Srmacklem NFSUNLOCKSTATE(); 740210178Srmacklem NFSLOCKV4ROOTMUTEX(); 741210178Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 742210178Srmacklem NFSUNLOCKV4ROOTMUTEX(); 743191783Srmacklem return; 744191783Srmacklem } 745191783Srmacklem 746191783Srmacklem /* 747191783Srmacklem * For each open share on file, dump it out. 748191783Srmacklem */ 749191783Srmacklem stp = LIST_FIRST(&lfp->lf_open); 750191783Srmacklem while (stp != LIST_END(&lfp->lf_open) && cnt < maxcnt) { 751191783Srmacklem ldumpp[cnt].ndlck_flags = stp->ls_flags; 752191783Srmacklem ldumpp[cnt].ndlck_stateid.seqid = stp->ls_stateid.seqid; 753191783Srmacklem ldumpp[cnt].ndlck_stateid.other[0] = stp->ls_stateid.other[0]; 754191783Srmacklem ldumpp[cnt].ndlck_stateid.other[1] = stp->ls_stateid.other[1]; 755191783Srmacklem ldumpp[cnt].ndlck_stateid.other[2] = stp->ls_stateid.other[2]; 756191783Srmacklem ldumpp[cnt].ndlck_owner.nclid_idlen = 757191783Srmacklem stp->ls_openowner->ls_ownerlen; 758191783Srmacklem NFSBCOPY(stp->ls_openowner->ls_owner, 759191783Srmacklem ldumpp[cnt].ndlck_owner.nclid_id, 760191783Srmacklem stp->ls_openowner->ls_ownerlen); 761191783Srmacklem ldumpp[cnt].ndlck_clid.nclid_idlen = stp->ls_clp->lc_idlen; 762191783Srmacklem NFSBCOPY(stp->ls_clp->lc_id, ldumpp[cnt].ndlck_clid.nclid_id, 763191783Srmacklem stp->ls_clp->lc_idlen); 764191783Srmacklem sad=NFSSOCKADDR(stp->ls_clp->lc_req.nr_nam, struct sockaddr *); 765191783Srmacklem ldumpp[cnt].ndlck_addrfam = sad->sa_family; 766191783Srmacklem if (sad->sa_family == AF_INET) { 767191783Srmacklem rad = (struct sockaddr_in *)sad; 768191783Srmacklem ldumpp[cnt].ndlck_cbaddr.sin_addr = rad->sin_addr; 769191783Srmacklem } else { 770191783Srmacklem rad6 = (struct sockaddr_in6 *)sad; 771191783Srmacklem ldumpp[cnt].ndlck_cbaddr.sin6_addr = rad6->sin6_addr; 772191783Srmacklem } 773191783Srmacklem stp = LIST_NEXT(stp, ls_file); 774191783Srmacklem cnt++; 775191783Srmacklem } 776191783Srmacklem 777191783Srmacklem /* 778191783Srmacklem * and all locks. 779191783Srmacklem */ 780191783Srmacklem lop = LIST_FIRST(&lfp->lf_lock); 781191783Srmacklem while (lop != LIST_END(&lfp->lf_lock) && cnt < maxcnt) { 782191783Srmacklem stp = lop->lo_stp; 783191783Srmacklem ldumpp[cnt].ndlck_flags = lop->lo_flags; 784191783Srmacklem ldumpp[cnt].ndlck_first = lop->lo_first; 785191783Srmacklem ldumpp[cnt].ndlck_end = lop->lo_end; 786191783Srmacklem ldumpp[cnt].ndlck_stateid.seqid = stp->ls_stateid.seqid; 787191783Srmacklem ldumpp[cnt].ndlck_stateid.other[0] = stp->ls_stateid.other[0]; 788191783Srmacklem ldumpp[cnt].ndlck_stateid.other[1] = stp->ls_stateid.other[1]; 789191783Srmacklem ldumpp[cnt].ndlck_stateid.other[2] = stp->ls_stateid.other[2]; 790191783Srmacklem ldumpp[cnt].ndlck_owner.nclid_idlen = stp->ls_ownerlen; 791191783Srmacklem NFSBCOPY(stp->ls_owner, ldumpp[cnt].ndlck_owner.nclid_id, 792191783Srmacklem stp->ls_ownerlen); 793191783Srmacklem ldumpp[cnt].ndlck_clid.nclid_idlen = stp->ls_clp->lc_idlen; 794191783Srmacklem NFSBCOPY(stp->ls_clp->lc_id, ldumpp[cnt].ndlck_clid.nclid_id, 795191783Srmacklem stp->ls_clp->lc_idlen); 796191783Srmacklem sad=NFSSOCKADDR(stp->ls_clp->lc_req.nr_nam, struct sockaddr *); 797191783Srmacklem ldumpp[cnt].ndlck_addrfam = sad->sa_family; 798191783Srmacklem if (sad->sa_family == AF_INET) { 799191783Srmacklem rad = (struct sockaddr_in *)sad; 800191783Srmacklem ldumpp[cnt].ndlck_cbaddr.sin_addr = rad->sin_addr; 801191783Srmacklem } else { 802191783Srmacklem rad6 = (struct sockaddr_in6 *)sad; 803191783Srmacklem ldumpp[cnt].ndlck_cbaddr.sin6_addr = rad6->sin6_addr; 804191783Srmacklem } 805191783Srmacklem lop = LIST_NEXT(lop, lo_lckfile); 806191783Srmacklem cnt++; 807191783Srmacklem } 808191783Srmacklem 809191783Srmacklem /* 810191783Srmacklem * and the delegations. 811191783Srmacklem */ 812191783Srmacklem stp = LIST_FIRST(&lfp->lf_deleg); 813191783Srmacklem while (stp != LIST_END(&lfp->lf_deleg) && cnt < maxcnt) { 814191783Srmacklem ldumpp[cnt].ndlck_flags = stp->ls_flags; 815191783Srmacklem ldumpp[cnt].ndlck_stateid.seqid = stp->ls_stateid.seqid; 816191783Srmacklem ldumpp[cnt].ndlck_stateid.other[0] = stp->ls_stateid.other[0]; 817191783Srmacklem ldumpp[cnt].ndlck_stateid.other[1] = stp->ls_stateid.other[1]; 818191783Srmacklem ldumpp[cnt].ndlck_stateid.other[2] = stp->ls_stateid.other[2]; 819191783Srmacklem ldumpp[cnt].ndlck_owner.nclid_idlen = 0; 820191783Srmacklem ldumpp[cnt].ndlck_clid.nclid_idlen = stp->ls_clp->lc_idlen; 821191783Srmacklem NFSBCOPY(stp->ls_clp->lc_id, ldumpp[cnt].ndlck_clid.nclid_id, 822191783Srmacklem stp->ls_clp->lc_idlen); 823191783Srmacklem sad=NFSSOCKADDR(stp->ls_clp->lc_req.nr_nam, struct sockaddr *); 824191783Srmacklem ldumpp[cnt].ndlck_addrfam = sad->sa_family; 825191783Srmacklem if (sad->sa_family == AF_INET) { 826191783Srmacklem rad = (struct sockaddr_in *)sad; 827191783Srmacklem ldumpp[cnt].ndlck_cbaddr.sin_addr = rad->sin_addr; 828191783Srmacklem } else { 829191783Srmacklem rad6 = (struct sockaddr_in6 *)sad; 830191783Srmacklem ldumpp[cnt].ndlck_cbaddr.sin6_addr = rad6->sin6_addr; 831191783Srmacklem } 832191783Srmacklem stp = LIST_NEXT(stp, ls_file); 833191783Srmacklem cnt++; 834191783Srmacklem } 835191783Srmacklem 836191783Srmacklem /* 837191783Srmacklem * If list isn't full, mark end of list by setting the client name 838191783Srmacklem * to zero length. 839191783Srmacklem */ 840191783Srmacklem if (cnt < maxcnt) 841191783Srmacklem ldumpp[cnt].ndlck_clid.nclid_idlen = 0; 842191783Srmacklem NFSUNLOCKSTATE(); 843210178Srmacklem NFSLOCKV4ROOTMUTEX(); 844210178Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 845210178Srmacklem NFSUNLOCKV4ROOTMUTEX(); 846191783Srmacklem} 847191783Srmacklem 848191783Srmacklem/* 849191783Srmacklem * Server timer routine. It can scan any linked list, so long 850211951Srmacklem * as it holds the spin/mutex lock and there is no exclusive lock on 851191783Srmacklem * nfsv4rootfs_lock. 852191783Srmacklem * (For OpenBSD, a kthread is ok. For FreeBSD, I think it is ok 853191783Srmacklem * to do this from a callout, since the spin locks work. For 854191783Srmacklem * Darwin, I'm not sure what will work correctly yet.) 855191783Srmacklem * Should be called once per second. 856191783Srmacklem */ 857191783SrmacklemAPPLESTATIC void 858191783Srmacklemnfsrv_servertimer(void) 859191783Srmacklem{ 860191783Srmacklem struct nfsclient *clp, *nclp; 861191783Srmacklem struct nfsstate *stp, *nstp; 862211951Srmacklem int got_ref, i; 863191783Srmacklem 864191783Srmacklem /* 865191783Srmacklem * Make sure nfsboottime is set. This is used by V3 as well 866191783Srmacklem * as V4. Note that nfsboottime is not nfsrvboottime, which is 867191783Srmacklem * only used by the V4 server for leases. 868191783Srmacklem */ 869191783Srmacklem if (nfsboottime.tv_sec == 0) 870191783Srmacklem NFSSETBOOTTIME(nfsboottime); 871191783Srmacklem 872191783Srmacklem /* 873191783Srmacklem * If server hasn't started yet, just return. 874191783Srmacklem */ 875191783Srmacklem NFSLOCKSTATE(); 876191783Srmacklem if (nfsrv_stablefirst.nsf_eograce == 0) { 877191783Srmacklem NFSUNLOCKSTATE(); 878191783Srmacklem return; 879191783Srmacklem } 880191783Srmacklem if (!(nfsrv_stablefirst.nsf_flags & NFSNSF_UPDATEDONE)) { 881191783Srmacklem if (!(nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) && 882191783Srmacklem NFSD_MONOSEC > nfsrv_stablefirst.nsf_eograce) 883191783Srmacklem nfsrv_stablefirst.nsf_flags |= 884191783Srmacklem (NFSNSF_GRACEOVER | NFSNSF_NEEDLOCK); 885191783Srmacklem NFSUNLOCKSTATE(); 886191783Srmacklem return; 887191783Srmacklem } 888191783Srmacklem 889191783Srmacklem /* 890211951Srmacklem * Try and get a reference count on the nfsv4rootfs_lock so that 891211951Srmacklem * no nfsd thread can acquire an exclusive lock on it before this 892211951Srmacklem * call is done. If it is already exclusively locked, just return. 893191783Srmacklem */ 894211951Srmacklem NFSLOCKV4ROOTMUTEX(); 895211951Srmacklem got_ref = nfsv4_getref_nonblock(&nfsv4rootfs_lock); 896211951Srmacklem NFSUNLOCKV4ROOTMUTEX(); 897211951Srmacklem if (got_ref == 0) { 898191783Srmacklem NFSUNLOCKSTATE(); 899191783Srmacklem return; 900191783Srmacklem } 901191783Srmacklem 902191783Srmacklem /* 903191783Srmacklem * For each client... 904191783Srmacklem */ 905191783Srmacklem for (i = 0; i < NFSCLIENTHASHSIZE; i++) { 906191783Srmacklem clp = LIST_FIRST(&nfsclienthash[i]); 907191783Srmacklem while (clp != LIST_END(&nfsclienthash[i])) { 908191783Srmacklem nclp = LIST_NEXT(clp, lc_hash); 909191783Srmacklem if (!(clp->lc_flags & LCL_EXPIREIT)) { 910191783Srmacklem if (((clp->lc_expiry + NFSRV_STALELEASE) < NFSD_MONOSEC 911191783Srmacklem && ((LIST_EMPTY(&clp->lc_deleg) 912191783Srmacklem && LIST_EMPTY(&clp->lc_open)) || 913191783Srmacklem nfsrv_clients > nfsrv_clienthighwater)) || 914191783Srmacklem (clp->lc_expiry + NFSRV_MOULDYLEASE) < NFSD_MONOSEC || 915191783Srmacklem (clp->lc_expiry < NFSD_MONOSEC && 916191783Srmacklem (nfsrv_openpluslock * 10 / 9) > NFSRV_V4STATELIMIT)) { 917191783Srmacklem /* 918191783Srmacklem * Lease has expired several nfsrv_lease times ago: 919191783Srmacklem * PLUS 920191783Srmacklem * - no state is associated with it 921191783Srmacklem * OR 922191783Srmacklem * - above high water mark for number of clients 923191783Srmacklem * (nfsrv_clienthighwater should be large enough 924191783Srmacklem * that this only occurs when clients fail to 925191783Srmacklem * use the same nfs_client_id4.id. Maybe somewhat 926191783Srmacklem * higher that the maximum number of clients that 927191783Srmacklem * will mount this server?) 928191783Srmacklem * OR 929191783Srmacklem * Lease has expired a very long time ago 930191783Srmacklem * OR 931191783Srmacklem * Lease has expired PLUS the number of opens + locks 932191783Srmacklem * has exceeded 90% of capacity 933191783Srmacklem * 934191783Srmacklem * --> Mark for expiry. The actual expiry will be done 935191783Srmacklem * by an nfsd sometime soon. 936191783Srmacklem */ 937191783Srmacklem clp->lc_flags |= LCL_EXPIREIT; 938191783Srmacklem nfsrv_stablefirst.nsf_flags |= 939191783Srmacklem (NFSNSF_NEEDLOCK | NFSNSF_EXPIREDCLIENT); 940191783Srmacklem } else { 941191783Srmacklem /* 942191783Srmacklem * If there are no opens, increment no open tick cnt 943191783Srmacklem * If time exceeds NFSNOOPEN, mark it to be thrown away 944191783Srmacklem * otherwise, if there is an open, reset no open time 945191783Srmacklem * Hopefully, this will avoid excessive re-creation 946191783Srmacklem * of open owners and subsequent open confirms. 947191783Srmacklem */ 948191783Srmacklem stp = LIST_FIRST(&clp->lc_open); 949191783Srmacklem while (stp != LIST_END(&clp->lc_open)) { 950191783Srmacklem nstp = LIST_NEXT(stp, ls_list); 951191783Srmacklem if (LIST_EMPTY(&stp->ls_open)) { 952191783Srmacklem stp->ls_noopens++; 953191783Srmacklem if (stp->ls_noopens > NFSNOOPEN || 954191783Srmacklem (nfsrv_openpluslock * 2) > 955191783Srmacklem NFSRV_V4STATELIMIT) 956191783Srmacklem nfsrv_stablefirst.nsf_flags |= 957191783Srmacklem NFSNSF_NOOPENS; 958191783Srmacklem } else { 959191783Srmacklem stp->ls_noopens = 0; 960191783Srmacklem } 961191783Srmacklem stp = nstp; 962191783Srmacklem } 963191783Srmacklem } 964191783Srmacklem } 965191783Srmacklem clp = nclp; 966191783Srmacklem } 967191783Srmacklem } 968191783Srmacklem NFSUNLOCKSTATE(); 969211951Srmacklem NFSLOCKV4ROOTMUTEX(); 970211951Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 971211951Srmacklem NFSUNLOCKV4ROOTMUTEX(); 972191783Srmacklem} 973191783Srmacklem 974191783Srmacklem/* 975191783Srmacklem * The following set of functions free up the various data structures. 976191783Srmacklem */ 977191783Srmacklem/* 978191783Srmacklem * Clear out all open/lock state related to this nfsclient. 979191783Srmacklem * Caller must hold an exclusive lock on nfsv4rootfs_lock, so that 980191783Srmacklem * there are no other active nfsd threads. 981191783Srmacklem */ 982191783SrmacklemAPPLESTATIC void 983191783Srmacklemnfsrv_cleanclient(struct nfsclient *clp, NFSPROC_T *p) 984191783Srmacklem{ 985191783Srmacklem struct nfsstate *stp, *nstp; 986191783Srmacklem 987205941Srmacklem LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) 988191783Srmacklem nfsrv_freeopenowner(stp, 1, p); 989191783Srmacklem} 990191783Srmacklem 991191783Srmacklem/* 992191783Srmacklem * Free a client that has been cleaned. It should also already have been 993191783Srmacklem * removed from the lists. 994191783Srmacklem * (Just to be safe w.r.t. newnfs_disconnect(), call this function when 995191783Srmacklem * softclock interrupts are enabled.) 996191783Srmacklem */ 997191783SrmacklemAPPLESTATIC void 998191783Srmacklemnfsrv_zapclient(struct nfsclient *clp, NFSPROC_T *p) 999191783Srmacklem{ 1000191783Srmacklem 1001191783Srmacklem#ifdef notyet 1002191783Srmacklem if ((clp->lc_flags & (LCL_GSS | LCL_CALLBACKSON)) == 1003191783Srmacklem (LCL_GSS | LCL_CALLBACKSON) && 1004191783Srmacklem (clp->lc_hand.nfsh_flag & NFSG_COMPLETE) && 1005191783Srmacklem clp->lc_handlelen > 0) { 1006191783Srmacklem clp->lc_hand.nfsh_flag &= ~NFSG_COMPLETE; 1007191783Srmacklem clp->lc_hand.nfsh_flag |= NFSG_DESTROYED; 1008191783Srmacklem (void) nfsrv_docallback(clp, NFSV4PROC_CBNULL, 1009191783Srmacklem NULL, 0, NULL, NULL, NULL, p); 1010191783Srmacklem } 1011191783Srmacklem#endif 1012191783Srmacklem newnfs_disconnect(&clp->lc_req); 1013191783Srmacklem NFSSOCKADDRFREE(clp->lc_req.nr_nam); 1014191783Srmacklem NFSFREEMUTEX(&clp->lc_req.nr_mtx); 1015191783Srmacklem free((caddr_t)clp, M_NFSDCLIENT); 1016191783Srmacklem NFSLOCKSTATE(); 1017191783Srmacklem newnfsstats.srvclients--; 1018191783Srmacklem nfsrv_openpluslock--; 1019191783Srmacklem nfsrv_clients--; 1020191783Srmacklem NFSUNLOCKSTATE(); 1021191783Srmacklem} 1022191783Srmacklem 1023191783Srmacklem/* 1024191783Srmacklem * Free a list of delegation state structures. 1025191783Srmacklem * (This function will also free all nfslockfile structures that no 1026191783Srmacklem * longer have associated state.) 1027191783Srmacklem */ 1028191783SrmacklemAPPLESTATIC void 1029191783Srmacklemnfsrv_freedeleglist(struct nfsstatehead *sthp) 1030191783Srmacklem{ 1031191783Srmacklem struct nfsstate *stp, *nstp; 1032191783Srmacklem 1033191783Srmacklem LIST_FOREACH_SAFE(stp, sthp, ls_list, nstp) { 1034191783Srmacklem nfsrv_freedeleg(stp); 1035191783Srmacklem } 1036191783Srmacklem LIST_INIT(sthp); 1037191783Srmacklem} 1038191783Srmacklem 1039191783Srmacklem/* 1040191783Srmacklem * Free up a delegation. 1041191783Srmacklem */ 1042191783Srmacklemstatic void 1043191783Srmacklemnfsrv_freedeleg(struct nfsstate *stp) 1044191783Srmacklem{ 1045191783Srmacklem struct nfslockfile *lfp; 1046191783Srmacklem 1047191783Srmacklem LIST_REMOVE(stp, ls_hash); 1048191783Srmacklem LIST_REMOVE(stp, ls_list); 1049191783Srmacklem LIST_REMOVE(stp, ls_file); 1050191783Srmacklem lfp = stp->ls_lfp; 1051191783Srmacklem if (LIST_EMPTY(&lfp->lf_open) && 1052205941Srmacklem LIST_EMPTY(&lfp->lf_lock) && LIST_EMPTY(&lfp->lf_deleg) && 1053205941Srmacklem LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && 1054205941Srmacklem lfp->lf_usecount == 0 && 1055205941Srmacklem nfsv4_testlock(&lfp->lf_locallock_lck) == 0) 1056191783Srmacklem nfsrv_freenfslockfile(lfp); 1057191783Srmacklem FREE((caddr_t)stp, M_NFSDSTATE); 1058191783Srmacklem newnfsstats.srvdelegates--; 1059191783Srmacklem nfsrv_openpluslock--; 1060191783Srmacklem nfsrv_delegatecnt--; 1061191783Srmacklem} 1062191783Srmacklem 1063191783Srmacklem/* 1064191783Srmacklem * This function frees an open owner and all associated opens. 1065191783Srmacklem */ 1066191783Srmacklemstatic void 1067191783Srmacklemnfsrv_freeopenowner(struct nfsstate *stp, int cansleep, NFSPROC_T *p) 1068191783Srmacklem{ 1069191783Srmacklem struct nfsstate *nstp, *tstp; 1070191783Srmacklem 1071191783Srmacklem LIST_REMOVE(stp, ls_list); 1072191783Srmacklem /* 1073191783Srmacklem * Now, free all associated opens. 1074191783Srmacklem */ 1075191783Srmacklem nstp = LIST_FIRST(&stp->ls_open); 1076191783Srmacklem while (nstp != LIST_END(&stp->ls_open)) { 1077191783Srmacklem tstp = nstp; 1078191783Srmacklem nstp = LIST_NEXT(nstp, ls_list); 1079191783Srmacklem (void) nfsrv_freeopen(tstp, NULL, cansleep, p); 1080191783Srmacklem } 1081191783Srmacklem if (stp->ls_op) 1082191783Srmacklem nfsrvd_derefcache(stp->ls_op); 1083191783Srmacklem FREE((caddr_t)stp, M_NFSDSTATE); 1084191783Srmacklem newnfsstats.srvopenowners--; 1085191783Srmacklem nfsrv_openpluslock--; 1086191783Srmacklem} 1087191783Srmacklem 1088191783Srmacklem/* 1089191783Srmacklem * This function frees an open (nfsstate open structure) with all associated 1090191783Srmacklem * lock_owners and locks. It also frees the nfslockfile structure iff there 1091191783Srmacklem * are no other opens on the file. 1092191783Srmacklem * Returns 1 if it free'd the nfslockfile, 0 otherwise. 1093191783Srmacklem */ 1094191783Srmacklemstatic int 1095205941Srmacklemnfsrv_freeopen(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p) 1096191783Srmacklem{ 1097191783Srmacklem struct nfsstate *nstp, *tstp; 1098191783Srmacklem struct nfslockfile *lfp; 1099205941Srmacklem int ret; 1100191783Srmacklem 1101191783Srmacklem LIST_REMOVE(stp, ls_hash); 1102191783Srmacklem LIST_REMOVE(stp, ls_list); 1103191783Srmacklem LIST_REMOVE(stp, ls_file); 1104191783Srmacklem 1105191783Srmacklem lfp = stp->ls_lfp; 1106191783Srmacklem /* 1107205941Srmacklem * Now, free all lockowners associated with this open. 1108205941Srmacklem */ 1109205941Srmacklem LIST_FOREACH_SAFE(tstp, &stp->ls_open, ls_list, nstp) 1110205941Srmacklem nfsrv_freelockowner(tstp, vp, cansleep, p); 1111205941Srmacklem 1112205941Srmacklem /* 1113191783Srmacklem * The nfslockfile is freed here if there are no locks 1114191783Srmacklem * associated with the open. 1115191783Srmacklem * If there are locks associated with the open, the 1116191783Srmacklem * nfslockfile structure can be freed via nfsrv_freelockowner(). 1117212443Srmacklem * Acquire the state mutex to avoid races with calls to 1118212443Srmacklem * nfsrv_getlockfile(). 1119191783Srmacklem */ 1120212443Srmacklem if (cansleep != 0) 1121212443Srmacklem NFSLOCKSTATE(); 1122205941Srmacklem if (lfp != NULL && LIST_EMPTY(&lfp->lf_open) && 1123205941Srmacklem LIST_EMPTY(&lfp->lf_deleg) && LIST_EMPTY(&lfp->lf_lock) && 1124205941Srmacklem LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && 1125205941Srmacklem lfp->lf_usecount == 0 && 1126205941Srmacklem (cansleep != 0 || nfsv4_testlock(&lfp->lf_locallock_lck) == 0)) { 1127191783Srmacklem nfsrv_freenfslockfile(lfp); 1128191783Srmacklem ret = 1; 1129205941Srmacklem } else 1130205941Srmacklem ret = 0; 1131212443Srmacklem if (cansleep != 0) 1132212443Srmacklem NFSUNLOCKSTATE(); 1133191783Srmacklem FREE((caddr_t)stp, M_NFSDSTATE); 1134191783Srmacklem newnfsstats.srvopens--; 1135191783Srmacklem nfsrv_openpluslock--; 1136191783Srmacklem return (ret); 1137191783Srmacklem} 1138191783Srmacklem 1139191783Srmacklem/* 1140191783Srmacklem * Frees a lockowner and all associated locks. 1141191783Srmacklem */ 1142205941Srmacklemstatic void 1143205941Srmacklemnfsrv_freelockowner(struct nfsstate *stp, vnode_t vp, int cansleep, 1144191783Srmacklem NFSPROC_T *p) 1145191783Srmacklem{ 1146191783Srmacklem 1147191783Srmacklem LIST_REMOVE(stp, ls_hash); 1148191783Srmacklem LIST_REMOVE(stp, ls_list); 1149205941Srmacklem nfsrv_freeallnfslocks(stp, vp, cansleep, p); 1150191783Srmacklem if (stp->ls_op) 1151191783Srmacklem nfsrvd_derefcache(stp->ls_op); 1152191783Srmacklem FREE((caddr_t)stp, M_NFSDSTATE); 1153191783Srmacklem newnfsstats.srvlockowners--; 1154191783Srmacklem nfsrv_openpluslock--; 1155191783Srmacklem} 1156191783Srmacklem 1157191783Srmacklem/* 1158191783Srmacklem * Free all the nfs locks on a lockowner. 1159191783Srmacklem */ 1160205941Srmacklemstatic void 1161205941Srmacklemnfsrv_freeallnfslocks(struct nfsstate *stp, vnode_t vp, int cansleep, 1162191783Srmacklem NFSPROC_T *p) 1163191783Srmacklem{ 1164191783Srmacklem struct nfslock *lop, *nlop; 1165205941Srmacklem struct nfsrollback *rlp, *nrlp; 1166205941Srmacklem struct nfslockfile *lfp = NULL; 1167205941Srmacklem int gottvp = 0; 1168205941Srmacklem vnode_t tvp = NULL; 1169212834Srmacklem uint64_t first, end; 1170191783Srmacklem 1171191783Srmacklem lop = LIST_FIRST(&stp->ls_lock); 1172191783Srmacklem while (lop != LIST_END(&stp->ls_lock)) { 1173191783Srmacklem nlop = LIST_NEXT(lop, lo_lckowner); 1174191783Srmacklem /* 1175205941Srmacklem * Since all locks should be for the same file, lfp should 1176205941Srmacklem * not change. 1177191783Srmacklem */ 1178205941Srmacklem if (lfp == NULL) 1179205941Srmacklem lfp = lop->lo_lfp; 1180205941Srmacklem else if (lfp != lop->lo_lfp) 1181205941Srmacklem panic("allnfslocks"); 1182205941Srmacklem /* 1183205941Srmacklem * If vp is NULL and cansleep != 0, a vnode must be acquired 1184205941Srmacklem * from the file handle. This only occurs when called from 1185205941Srmacklem * nfsrv_cleanclient(). 1186205941Srmacklem */ 1187205941Srmacklem if (gottvp == 0) { 1188205941Srmacklem if (nfsrv_dolocallocks == 0) 1189205941Srmacklem tvp = NULL; 1190205941Srmacklem else if (vp == NULL && cansleep != 0) 1191205941Srmacklem tvp = nfsvno_getvp(&lfp->lf_fh); 1192205941Srmacklem else 1193205941Srmacklem tvp = vp; 1194205941Srmacklem gottvp = 1; 1195205941Srmacklem } 1196205941Srmacklem 1197205941Srmacklem if (tvp != NULL) { 1198205941Srmacklem if (cansleep == 0) 1199205941Srmacklem panic("allnfs2"); 1200212834Srmacklem first = lop->lo_first; 1201212834Srmacklem end = lop->lo_end; 1202212834Srmacklem nfsrv_freenfslock(lop); 1203212834Srmacklem nfsrv_localunlock(tvp, lfp, first, end, p); 1204205941Srmacklem LIST_FOREACH_SAFE(rlp, &lfp->lf_rollback, rlck_list, 1205205941Srmacklem nrlp) 1206205941Srmacklem free(rlp, M_NFSDROLLBACK); 1207205941Srmacklem LIST_INIT(&lfp->lf_rollback); 1208212834Srmacklem } else 1209212834Srmacklem nfsrv_freenfslock(lop); 1210191783Srmacklem lop = nlop; 1211191783Srmacklem } 1212205941Srmacklem if (vp == NULL && tvp != NULL) 1213205941Srmacklem vput(tvp); 1214191783Srmacklem} 1215191783Srmacklem 1216191783Srmacklem/* 1217191783Srmacklem * Free an nfslock structure. 1218191783Srmacklem */ 1219191783Srmacklemstatic void 1220191783Srmacklemnfsrv_freenfslock(struct nfslock *lop) 1221191783Srmacklem{ 1222191783Srmacklem 1223205941Srmacklem if (lop->lo_lckfile.le_prev != NULL) { 1224205941Srmacklem LIST_REMOVE(lop, lo_lckfile); 1225205941Srmacklem newnfsstats.srvlocks--; 1226205941Srmacklem nfsrv_openpluslock--; 1227205941Srmacklem } 1228191783Srmacklem LIST_REMOVE(lop, lo_lckowner); 1229191783Srmacklem FREE((caddr_t)lop, M_NFSDLOCK); 1230191783Srmacklem} 1231191783Srmacklem 1232191783Srmacklem/* 1233191783Srmacklem * This function frees an nfslockfile structure. 1234191783Srmacklem */ 1235191783Srmacklemstatic void 1236191783Srmacklemnfsrv_freenfslockfile(struct nfslockfile *lfp) 1237191783Srmacklem{ 1238191783Srmacklem 1239191783Srmacklem LIST_REMOVE(lfp, lf_hash); 1240191783Srmacklem FREE((caddr_t)lfp, M_NFSDLOCKFILE); 1241191783Srmacklem} 1242191783Srmacklem 1243191783Srmacklem/* 1244191783Srmacklem * This function looks up an nfsstate structure via stateid. 1245191783Srmacklem */ 1246191783Srmacklemstatic int 1247191783Srmacklemnfsrv_getstate(struct nfsclient *clp, nfsv4stateid_t *stateidp, __unused u_int32_t flags, 1248191783Srmacklem struct nfsstate **stpp) 1249191783Srmacklem{ 1250191783Srmacklem struct nfsstate *stp; 1251191783Srmacklem struct nfsstatehead *hp; 1252224086Szack int error = 0; 1253191783Srmacklem 1254191783Srmacklem *stpp = NULL; 1255191783Srmacklem hp = NFSSTATEHASH(clp, *stateidp); 1256191783Srmacklem LIST_FOREACH(stp, hp, ls_hash) { 1257191783Srmacklem if (!NFSBCMP(stp->ls_stateid.other, stateidp->other, 1258191783Srmacklem NFSX_STATEIDOTHER)) 1259191783Srmacklem break; 1260191783Srmacklem } 1261191783Srmacklem 1262191783Srmacklem /* 1263191783Srmacklem * If no state id in list, return NFSERR_BADSTATEID. 1264191783Srmacklem */ 1265224086Szack if (stp == LIST_END(hp)) { 1266224086Szack error = NFSERR_BADSTATEID; 1267224086Szack goto out; 1268224086Szack } 1269191783Srmacklem *stpp = stp; 1270224086Szack 1271224086Szackout: 1272224086Szack NFSEXITCODE(error); 1273224086Szack return (error); 1274191783Srmacklem} 1275191783Srmacklem 1276191783Srmacklem/* 1277191783Srmacklem * This function gets an nfsstate structure via owner string. 1278191783Srmacklem */ 1279191783Srmacklemstatic void 1280191783Srmacklemnfsrv_getowner(struct nfsstatehead *hp, struct nfsstate *new_stp, 1281191783Srmacklem struct nfsstate **stpp) 1282191783Srmacklem{ 1283191783Srmacklem struct nfsstate *stp; 1284191783Srmacklem 1285191783Srmacklem *stpp = NULL; 1286191783Srmacklem LIST_FOREACH(stp, hp, ls_list) { 1287191783Srmacklem if (new_stp->ls_ownerlen == stp->ls_ownerlen && 1288191783Srmacklem !NFSBCMP(new_stp->ls_owner,stp->ls_owner,stp->ls_ownerlen)) { 1289191783Srmacklem *stpp = stp; 1290191783Srmacklem return; 1291191783Srmacklem } 1292191783Srmacklem } 1293191783Srmacklem} 1294191783Srmacklem 1295191783Srmacklem/* 1296191783Srmacklem * Lock control function called to update lock status. 1297191783Srmacklem * Returns 0 upon success, -1 if there is no lock and the flags indicate 1298191783Srmacklem * that one isn't to be created and an NFSERR_xxx for other errors. 1299191783Srmacklem * The structures new_stp and new_lop are passed in as pointers that should 1300191783Srmacklem * be set to NULL if the structure is used and shouldn't be free'd. 1301191783Srmacklem * For the NFSLCK_TEST and NFSLCK_CHECK cases, the structures are 1302191783Srmacklem * never used and can safely be allocated on the stack. For all other 1303191783Srmacklem * cases, *new_stpp and *new_lopp should be malloc'd before the call, 1304191783Srmacklem * in case they are used. 1305191783Srmacklem */ 1306191783SrmacklemAPPLESTATIC int 1307191783Srmacklemnfsrv_lockctrl(vnode_t vp, struct nfsstate **new_stpp, 1308191783Srmacklem struct nfslock **new_lopp, struct nfslockconflict *cfp, 1309205941Srmacklem nfsquad_t clientid, nfsv4stateid_t *stateidp, 1310205941Srmacklem __unused struct nfsexstuff *exp, 1311191783Srmacklem struct nfsrv_descript *nd, NFSPROC_T *p) 1312191783Srmacklem{ 1313191783Srmacklem struct nfslock *lop; 1314191783Srmacklem struct nfsstate *new_stp = *new_stpp; 1315191783Srmacklem struct nfslock *new_lop = *new_lopp; 1316191783Srmacklem struct nfsstate *tstp, *mystp, *nstp; 1317191783Srmacklem int specialid = 0; 1318191783Srmacklem struct nfslockfile *lfp; 1319191783Srmacklem struct nfslock *other_lop = NULL; 1320191783Srmacklem struct nfsstate *stp, *lckstp = NULL; 1321191783Srmacklem struct nfsclient *clp = NULL; 1322191783Srmacklem u_int32_t bits; 1323205941Srmacklem int error = 0, haslock = 0, ret, reterr; 1324205941Srmacklem int getlckret, delegation = 0, filestruct_locked; 1325191783Srmacklem fhandle_t nfh; 1326205941Srmacklem uint64_t first, end; 1327205941Srmacklem uint32_t lock_flags; 1328191783Srmacklem 1329191783Srmacklem if (new_stp->ls_flags & (NFSLCK_CHECK | NFSLCK_SETATTR)) { 1330191783Srmacklem /* 1331191783Srmacklem * Note the special cases of "all 1s" or "all 0s" stateids and 1332191783Srmacklem * let reads with all 1s go ahead. 1333191783Srmacklem */ 1334191783Srmacklem if (new_stp->ls_stateid.seqid == 0x0 && 1335191783Srmacklem new_stp->ls_stateid.other[0] == 0x0 && 1336191783Srmacklem new_stp->ls_stateid.other[1] == 0x0 && 1337191783Srmacklem new_stp->ls_stateid.other[2] == 0x0) 1338191783Srmacklem specialid = 1; 1339191783Srmacklem else if (new_stp->ls_stateid.seqid == 0xffffffff && 1340191783Srmacklem new_stp->ls_stateid.other[0] == 0xffffffff && 1341191783Srmacklem new_stp->ls_stateid.other[1] == 0xffffffff && 1342191783Srmacklem new_stp->ls_stateid.other[2] == 0xffffffff) 1343191783Srmacklem specialid = 2; 1344191783Srmacklem } 1345191783Srmacklem 1346191783Srmacklem /* 1347191783Srmacklem * Check for restart conditions (client and server). 1348191783Srmacklem */ 1349191783Srmacklem error = nfsrv_checkrestart(clientid, new_stp->ls_flags, 1350191783Srmacklem &new_stp->ls_stateid, specialid); 1351191783Srmacklem if (error) 1352224086Szack goto out; 1353191783Srmacklem 1354191783Srmacklem /* 1355191783Srmacklem * Check for state resource limit exceeded. 1356191783Srmacklem */ 1357191783Srmacklem if ((new_stp->ls_flags & NFSLCK_LOCK) && 1358224086Szack nfsrv_openpluslock > NFSRV_V4STATELIMIT) { 1359224086Szack error = NFSERR_RESOURCE; 1360224086Szack goto out; 1361224086Szack } 1362191783Srmacklem 1363191783Srmacklem /* 1364191783Srmacklem * For the lock case, get another nfslock structure, 1365191783Srmacklem * just in case we need it. 1366191783Srmacklem * Malloc now, before we start sifting through the linked lists, 1367191783Srmacklem * in case we have to wait for memory. 1368191783Srmacklem */ 1369191783Srmacklemtryagain: 1370191783Srmacklem if (new_stp->ls_flags & NFSLCK_LOCK) 1371191783Srmacklem MALLOC(other_lop, struct nfslock *, sizeof (struct nfslock), 1372191783Srmacklem M_NFSDLOCK, M_WAITOK); 1373205941Srmacklem filestruct_locked = 0; 1374205941Srmacklem reterr = 0; 1375205941Srmacklem lfp = NULL; 1376191783Srmacklem 1377191783Srmacklem /* 1378191783Srmacklem * Get the lockfile structure for CFH now, so we can do a sanity 1379191783Srmacklem * check against the stateid, before incrementing the seqid#, since 1380191783Srmacklem * we want to return NFSERR_BADSTATEID on failure and the seqid# 1381191783Srmacklem * shouldn't be incremented for this case. 1382191783Srmacklem * If nfsrv_getlockfile() returns -1, it means "not found", which 1383191783Srmacklem * will be handled later. 1384205941Srmacklem * If we are doing Lock/LockU and local locking is enabled, sleep 1385205941Srmacklem * lock the nfslockfile structure. 1386191783Srmacklem */ 1387191783Srmacklem getlckret = nfsrv_getlockfh(vp, new_stp->ls_flags, NULL, &nfh, p); 1388191783Srmacklem NFSLOCKSTATE(); 1389205941Srmacklem if (getlckret == 0) { 1390205941Srmacklem if ((new_stp->ls_flags & (NFSLCK_LOCK | NFSLCK_UNLOCK)) != 0 && 1391205941Srmacklem nfsrv_dolocallocks != 0 && nd->nd_repstat == 0) { 1392205941Srmacklem getlckret = nfsrv_getlockfile(new_stp->ls_flags, NULL, 1393205941Srmacklem &lfp, &nfh, 1); 1394205941Srmacklem if (getlckret == 0) 1395205941Srmacklem filestruct_locked = 1; 1396205941Srmacklem } else 1397205941Srmacklem getlckret = nfsrv_getlockfile(new_stp->ls_flags, NULL, 1398205941Srmacklem &lfp, &nfh, 0); 1399205941Srmacklem } 1400205941Srmacklem if (getlckret != 0 && getlckret != -1) 1401205941Srmacklem reterr = getlckret; 1402205941Srmacklem 1403205941Srmacklem if (filestruct_locked != 0) { 1404205941Srmacklem LIST_INIT(&lfp->lf_rollback); 1405205941Srmacklem if ((new_stp->ls_flags & NFSLCK_LOCK)) { 1406205941Srmacklem /* 1407205941Srmacklem * For local locking, do the advisory locking now, so 1408205941Srmacklem * that any conflict can be detected. A failure later 1409205941Srmacklem * can be rolled back locally. If an error is returned, 1410205941Srmacklem * struct nfslockfile has been unlocked and any local 1411205941Srmacklem * locking rolled back. 1412205941Srmacklem */ 1413205941Srmacklem NFSUNLOCKSTATE(); 1414205941Srmacklem reterr = nfsrv_locallock(vp, lfp, 1415205941Srmacklem (new_lop->lo_flags & (NFSLCK_READ | NFSLCK_WRITE)), 1416205941Srmacklem new_lop->lo_first, new_lop->lo_end, cfp, p); 1417205941Srmacklem NFSLOCKSTATE(); 1418191783Srmacklem } 1419191783Srmacklem } 1420191783Srmacklem 1421191783Srmacklem if (specialid == 0) { 1422191783Srmacklem if (new_stp->ls_flags & NFSLCK_TEST) { 1423191783Srmacklem /* 1424191783Srmacklem * RFC 3530 does not list LockT as an op that renews a 1425191783Srmacklem * lease, but the concensus seems to be that it is ok 1426191783Srmacklem * for a server to do so. 1427191783Srmacklem */ 1428191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 1429191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 1430191783Srmacklem 1431191783Srmacklem /* 1432191783Srmacklem * Since NFSERR_EXPIRED, NFSERR_ADMINREVOKED are not valid 1433191783Srmacklem * error returns for LockT, just go ahead and test for a lock, 1434191783Srmacklem * since there are no locks for this client, but other locks 1435191783Srmacklem * can conflict. (ie. same client will always be false) 1436191783Srmacklem */ 1437191783Srmacklem if (error == NFSERR_EXPIRED || error == NFSERR_ADMINREVOKED) 1438191783Srmacklem error = 0; 1439191783Srmacklem lckstp = new_stp; 1440191783Srmacklem } else { 1441191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 1442191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 1443191783Srmacklem if (error == 0) 1444191783Srmacklem /* 1445191783Srmacklem * Look up the stateid 1446191783Srmacklem */ 1447191783Srmacklem error = nfsrv_getstate(clp, &new_stp->ls_stateid, 1448191783Srmacklem new_stp->ls_flags, &stp); 1449191783Srmacklem /* 1450191783Srmacklem * do some sanity checks for an unconfirmed open or a 1451191783Srmacklem * stateid that refers to the wrong file, for an open stateid 1452191783Srmacklem */ 1453191783Srmacklem if (error == 0 && (stp->ls_flags & NFSLCK_OPEN) && 1454191783Srmacklem ((stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM) || 1455205941Srmacklem (getlckret == 0 && stp->ls_lfp != lfp))) 1456191783Srmacklem error = NFSERR_BADSTATEID; 1457191783Srmacklem if (error == 0 && 1458191783Srmacklem (stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) && 1459205941Srmacklem getlckret == 0 && stp->ls_lfp != lfp) 1460191783Srmacklem error = NFSERR_BADSTATEID; 1461191783Srmacklem 1462191783Srmacklem /* 1463191783Srmacklem * If the lockowner stateid doesn't refer to the same file, 1464191783Srmacklem * I believe that is considered ok, since some clients will 1465191783Srmacklem * only create a single lockowner and use that for all locks 1466191783Srmacklem * on all files. 1467191783Srmacklem * For now, log it as a diagnostic, instead of considering it 1468191783Srmacklem * a BadStateid. 1469191783Srmacklem */ 1470191783Srmacklem if (error == 0 && (stp->ls_flags & 1471191783Srmacklem (NFSLCK_OPEN | NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) == 0 && 1472205941Srmacklem getlckret == 0 && stp->ls_lfp != lfp) { 1473191783Srmacklem#ifdef DIAGNOSTIC 1474191783Srmacklem printf("Got a lock statid for different file open\n"); 1475191783Srmacklem#endif 1476191783Srmacklem /* 1477191783Srmacklem error = NFSERR_BADSTATEID; 1478191783Srmacklem */ 1479191783Srmacklem } 1480191783Srmacklem 1481191783Srmacklem if (error == 0) { 1482191783Srmacklem if (new_stp->ls_flags & NFSLCK_OPENTOLOCK) { 1483191783Srmacklem /* 1484191783Srmacklem * If haslock set, we've already checked the seqid. 1485191783Srmacklem */ 1486191783Srmacklem if (!haslock) { 1487191783Srmacklem if (stp->ls_flags & NFSLCK_OPEN) 1488191783Srmacklem error = nfsrv_checkseqid(nd, new_stp->ls_seq, 1489191783Srmacklem stp->ls_openowner, new_stp->ls_op); 1490191783Srmacklem else 1491191783Srmacklem error = NFSERR_BADSTATEID; 1492191783Srmacklem } 1493191783Srmacklem if (!error) 1494191783Srmacklem nfsrv_getowner(&stp->ls_open, new_stp, &lckstp); 1495191783Srmacklem if (lckstp) 1496191783Srmacklem /* 1497191783Srmacklem * I believe this should be an error, but it 1498191783Srmacklem * isn't obvious what NFSERR_xxx would be 1499191783Srmacklem * appropriate, so I'll use NFSERR_INVAL for now. 1500191783Srmacklem */ 1501191783Srmacklem error = NFSERR_INVAL; 1502191783Srmacklem else 1503191783Srmacklem lckstp = new_stp; 1504191783Srmacklem } else if (new_stp->ls_flags&(NFSLCK_LOCK|NFSLCK_UNLOCK)) { 1505191783Srmacklem /* 1506191783Srmacklem * If haslock set, ditto above. 1507191783Srmacklem */ 1508191783Srmacklem if (!haslock) { 1509191783Srmacklem if (stp->ls_flags & NFSLCK_OPEN) 1510191783Srmacklem error = NFSERR_BADSTATEID; 1511191783Srmacklem else 1512191783Srmacklem error = nfsrv_checkseqid(nd, new_stp->ls_seq, 1513191783Srmacklem stp, new_stp->ls_op); 1514191783Srmacklem } 1515191783Srmacklem lckstp = stp; 1516191783Srmacklem } else { 1517191783Srmacklem lckstp = stp; 1518191783Srmacklem } 1519191783Srmacklem } 1520191783Srmacklem /* 1521191783Srmacklem * If the seqid part of the stateid isn't the same, return 1522191783Srmacklem * NFSERR_OLDSTATEID for cases other than I/O Ops. 1523191783Srmacklem * For I/O Ops, only return NFSERR_OLDSTATEID if 1524191783Srmacklem * nfsrv_returnoldstateid is set. (The concensus on the email 1525191783Srmacklem * list was that most clients would prefer to not receive 1526191783Srmacklem * NFSERR_OLDSTATEID for I/O Ops, but the RFC suggests that that 1527191783Srmacklem * is what will happen, so I use the nfsrv_returnoldstateid to 1528191783Srmacklem * allow for either server configuration.) 1529191783Srmacklem */ 1530191783Srmacklem if (!error && stp->ls_stateid.seqid!=new_stp->ls_stateid.seqid && 1531191783Srmacklem (!(new_stp->ls_flags & NFSLCK_CHECK) || 1532191783Srmacklem nfsrv_returnoldstateid)) 1533191783Srmacklem error = NFSERR_OLDSTATEID; 1534191783Srmacklem } 1535191783Srmacklem } 1536191783Srmacklem 1537191783Srmacklem /* 1538191783Srmacklem * Now we can check for grace. 1539191783Srmacklem */ 1540191783Srmacklem if (!error) 1541191783Srmacklem error = nfsrv_checkgrace(new_stp->ls_flags); 1542191783Srmacklem if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && 1543191783Srmacklem nfsrv_checkstable(clp)) 1544191783Srmacklem error = NFSERR_NOGRACE; 1545191783Srmacklem /* 1546191783Srmacklem * If we successfully Reclaimed state, note that. 1547191783Srmacklem */ 1548191783Srmacklem if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error) 1549191783Srmacklem nfsrv_markstable(clp); 1550191783Srmacklem 1551191783Srmacklem /* 1552205941Srmacklem * At this point, either error == NFSERR_BADSTATEID or the 1553205941Srmacklem * seqid# has been updated, so we can return any error. 1554205941Srmacklem * If error == 0, there may be an error in: 1555205941Srmacklem * nd_repstat - Set by the calling function. 1556205941Srmacklem * reterr - Set above, if getting the nfslockfile structure 1557205941Srmacklem * or acquiring the local lock failed. 1558205941Srmacklem * (If both of these are set, nd_repstat should probably be 1559205941Srmacklem * returned, since that error was detected before this 1560205941Srmacklem * function call.) 1561191783Srmacklem */ 1562205941Srmacklem if (error != 0 || nd->nd_repstat != 0 || reterr != 0) { 1563205941Srmacklem if (error == 0) { 1564205941Srmacklem if (nd->nd_repstat != 0) 1565205941Srmacklem error = nd->nd_repstat; 1566205941Srmacklem else 1567205941Srmacklem error = reterr; 1568205941Srmacklem } 1569205941Srmacklem if (filestruct_locked != 0) { 1570205941Srmacklem /* Roll back local locks. */ 1571205941Srmacklem NFSUNLOCKSTATE(); 1572205941Srmacklem nfsrv_locallock_rollback(vp, lfp, p); 1573205941Srmacklem NFSLOCKSTATE(); 1574205941Srmacklem nfsrv_unlocklf(lfp); 1575205941Srmacklem } 1576191783Srmacklem NFSUNLOCKSTATE(); 1577224086Szack goto out; 1578191783Srmacklem } 1579191783Srmacklem 1580191783Srmacklem /* 1581191783Srmacklem * Check the nfsrv_getlockfile return. 1582191783Srmacklem * Returned -1 if no structure found. 1583191783Srmacklem */ 1584191783Srmacklem if (getlckret == -1) { 1585191783Srmacklem error = NFSERR_EXPIRED; 1586191783Srmacklem /* 1587191783Srmacklem * Called from lockt, so no lock is OK. 1588191783Srmacklem */ 1589191783Srmacklem if (new_stp->ls_flags & NFSLCK_TEST) { 1590191783Srmacklem error = 0; 1591191783Srmacklem } else if (new_stp->ls_flags & 1592191783Srmacklem (NFSLCK_CHECK | NFSLCK_SETATTR)) { 1593191783Srmacklem /* 1594191783Srmacklem * Called to check for a lock, OK if the stateid is all 1595191783Srmacklem * 1s or all 0s, but there should be an nfsstate 1596191783Srmacklem * otherwise. 1597191783Srmacklem * (ie. If there is no open, I'll assume no share 1598191783Srmacklem * deny bits.) 1599191783Srmacklem */ 1600191783Srmacklem if (specialid) 1601191783Srmacklem error = 0; 1602191783Srmacklem else 1603191783Srmacklem error = NFSERR_BADSTATEID; 1604191783Srmacklem } 1605191783Srmacklem NFSUNLOCKSTATE(); 1606224086Szack goto out; 1607191783Srmacklem } 1608191783Srmacklem 1609191783Srmacklem /* 1610191783Srmacklem * For NFSLCK_CHECK and NFSLCK_LOCK, test for a share conflict. 1611191783Srmacklem * For NFSLCK_CHECK, allow a read if write access is granted, 1612191783Srmacklem * but check for a deny. For NFSLCK_LOCK, require correct access, 1613191783Srmacklem * which implies a conflicting deny can't exist. 1614191783Srmacklem */ 1615191783Srmacklem if (new_stp->ls_flags & (NFSLCK_CHECK | NFSLCK_LOCK)) { 1616191783Srmacklem /* 1617191783Srmacklem * Four kinds of state id: 1618191783Srmacklem * - specialid (all 0s or all 1s), only for NFSLCK_CHECK 1619191783Srmacklem * - stateid for an open 1620191783Srmacklem * - stateid for a delegation 1621191783Srmacklem * - stateid for a lock owner 1622191783Srmacklem */ 1623191783Srmacklem if (!specialid) { 1624191783Srmacklem if (stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) { 1625191783Srmacklem delegation = 1; 1626191783Srmacklem mystp = stp; 1627191783Srmacklem nfsrv_delaydelegtimeout(stp); 1628191783Srmacklem } else if (stp->ls_flags & NFSLCK_OPEN) { 1629191783Srmacklem mystp = stp; 1630191783Srmacklem } else { 1631191783Srmacklem mystp = stp->ls_openstp; 1632191783Srmacklem } 1633191783Srmacklem /* 1634191783Srmacklem * If locking or checking, require correct access 1635191783Srmacklem * bit set. 1636191783Srmacklem */ 1637191783Srmacklem if (((new_stp->ls_flags & NFSLCK_LOCK) && 1638191783Srmacklem !((new_lop->lo_flags >> NFSLCK_LOCKSHIFT) & 1639191783Srmacklem mystp->ls_flags & NFSLCK_ACCESSBITS)) || 1640191783Srmacklem ((new_stp->ls_flags & (NFSLCK_CHECK|NFSLCK_READACCESS)) == 1641191783Srmacklem (NFSLCK_CHECK | NFSLCK_READACCESS) && 1642191783Srmacklem !(mystp->ls_flags & NFSLCK_READACCESS)) || 1643191783Srmacklem ((new_stp->ls_flags & (NFSLCK_CHECK|NFSLCK_WRITEACCESS)) == 1644191783Srmacklem (NFSLCK_CHECK | NFSLCK_WRITEACCESS) && 1645191783Srmacklem !(mystp->ls_flags & NFSLCK_WRITEACCESS))) { 1646205941Srmacklem if (filestruct_locked != 0) { 1647205941Srmacklem /* Roll back local locks. */ 1648205941Srmacklem NFSUNLOCKSTATE(); 1649205941Srmacklem nfsrv_locallock_rollback(vp, lfp, p); 1650205941Srmacklem NFSLOCKSTATE(); 1651205941Srmacklem nfsrv_unlocklf(lfp); 1652205941Srmacklem } 1653191783Srmacklem NFSUNLOCKSTATE(); 1654224086Szack error = NFSERR_OPENMODE; 1655224086Szack goto out; 1656191783Srmacklem } 1657191783Srmacklem } else 1658191783Srmacklem mystp = NULL; 1659191783Srmacklem if ((new_stp->ls_flags & NFSLCK_CHECK) && !delegation) { 1660191783Srmacklem /* 1661191783Srmacklem * Check for a conflicting deny bit. 1662191783Srmacklem */ 1663191783Srmacklem LIST_FOREACH(tstp, &lfp->lf_open, ls_file) { 1664191783Srmacklem if (tstp != mystp) { 1665191783Srmacklem bits = tstp->ls_flags; 1666191783Srmacklem bits >>= NFSLCK_SHIFT; 1667191783Srmacklem if (new_stp->ls_flags & bits & NFSLCK_ACCESSBITS) { 1668191783Srmacklem ret = nfsrv_clientconflict(tstp->ls_clp, &haslock, 1669191783Srmacklem vp, p); 1670216893Srmacklem if (ret == 1) { 1671191783Srmacklem /* 1672191783Srmacklem * nfsrv_clientconflict unlocks state 1673191783Srmacklem * when it returns non-zero. 1674191783Srmacklem */ 1675191783Srmacklem lckstp = NULL; 1676191783Srmacklem goto tryagain; 1677191783Srmacklem } 1678216893Srmacklem if (ret == 0) 1679216893Srmacklem NFSUNLOCKSTATE(); 1680216893Srmacklem if (ret == 2) 1681224086Szack error = NFSERR_PERM; 1682216893Srmacklem else 1683224086Szack error = NFSERR_OPENMODE; 1684224086Szack goto out; 1685191783Srmacklem } 1686191783Srmacklem } 1687191783Srmacklem } 1688191783Srmacklem 1689191783Srmacklem /* We're outta here */ 1690191783Srmacklem NFSUNLOCKSTATE(); 1691224086Szack goto out; 1692191783Srmacklem } 1693191783Srmacklem } 1694191783Srmacklem 1695191783Srmacklem /* 1696191783Srmacklem * For setattr, just get rid of all the Delegations for other clients. 1697191783Srmacklem */ 1698191783Srmacklem if (new_stp->ls_flags & NFSLCK_SETATTR) { 1699191783Srmacklem ret = nfsrv_cleandeleg(vp, lfp, clp, &haslock, p); 1700191783Srmacklem if (ret) { 1701191783Srmacklem /* 1702191783Srmacklem * nfsrv_cleandeleg() unlocks state when it 1703191783Srmacklem * returns non-zero. 1704191783Srmacklem */ 1705191783Srmacklem if (ret == -1) { 1706191783Srmacklem lckstp = NULL; 1707191783Srmacklem goto tryagain; 1708191783Srmacklem } 1709224086Szack error = ret; 1710224086Szack goto out; 1711191783Srmacklem } 1712191783Srmacklem if (!(new_stp->ls_flags & NFSLCK_CHECK) || 1713191783Srmacklem (LIST_EMPTY(&lfp->lf_open) && LIST_EMPTY(&lfp->lf_lock) && 1714191783Srmacklem LIST_EMPTY(&lfp->lf_deleg))) { 1715191783Srmacklem NFSUNLOCKSTATE(); 1716224086Szack goto out; 1717191783Srmacklem } 1718191783Srmacklem } 1719191783Srmacklem 1720191783Srmacklem /* 1721191783Srmacklem * Check for a conflicting delegation. If one is found, call 1722191783Srmacklem * nfsrv_delegconflict() to handle it. If the v4root lock hasn't 1723191783Srmacklem * been set yet, it will get the lock. Otherwise, it will recall 1724191783Srmacklem * the delegation. Then, we try try again... 1725191783Srmacklem * I currently believe the conflict algorithm to be: 1726191783Srmacklem * For Lock Ops (Lock/LockT/LockU) 1727191783Srmacklem * - there is a conflict iff a different client has a write delegation 1728191783Srmacklem * For Reading (Read Op) 1729191783Srmacklem * - there is a conflict iff a different client has a write delegation 1730191783Srmacklem * (the specialids are always a different client) 1731191783Srmacklem * For Writing (Write/Setattr of size) 1732191783Srmacklem * - there is a conflict if a different client has any delegation 1733191783Srmacklem * - there is a conflict if the same client has a read delegation 1734191783Srmacklem * (I don't understand why this isn't allowed, but that seems to be 1735191783Srmacklem * the current concensus?) 1736191783Srmacklem */ 1737191783Srmacklem tstp = LIST_FIRST(&lfp->lf_deleg); 1738191783Srmacklem while (tstp != LIST_END(&lfp->lf_deleg)) { 1739191783Srmacklem nstp = LIST_NEXT(tstp, ls_file); 1740191783Srmacklem if ((((new_stp->ls_flags&(NFSLCK_LOCK|NFSLCK_UNLOCK|NFSLCK_TEST))|| 1741191783Srmacklem ((new_stp->ls_flags & NFSLCK_CHECK) && 1742191783Srmacklem (new_lop->lo_flags & NFSLCK_READ))) && 1743191783Srmacklem clp != tstp->ls_clp && 1744191783Srmacklem (tstp->ls_flags & NFSLCK_DELEGWRITE)) || 1745191783Srmacklem ((new_stp->ls_flags & NFSLCK_CHECK) && 1746191783Srmacklem (new_lop->lo_flags & NFSLCK_WRITE) && 1747191783Srmacklem (clp != tstp->ls_clp || 1748191783Srmacklem (tstp->ls_flags & NFSLCK_DELEGREAD)))) { 1749205941Srmacklem if (filestruct_locked != 0) { 1750205941Srmacklem /* Roll back local locks. */ 1751205941Srmacklem NFSUNLOCKSTATE(); 1752205941Srmacklem nfsrv_locallock_rollback(vp, lfp, p); 1753205941Srmacklem NFSLOCKSTATE(); 1754205941Srmacklem nfsrv_unlocklf(lfp); 1755205941Srmacklem } 1756191783Srmacklem ret = nfsrv_delegconflict(tstp, &haslock, p, vp); 1757191783Srmacklem if (ret) { 1758191783Srmacklem /* 1759191783Srmacklem * nfsrv_delegconflict unlocks state when it 1760205941Srmacklem * returns non-zero, which it always does. 1761191783Srmacklem */ 1762191783Srmacklem if (other_lop) { 1763191783Srmacklem FREE((caddr_t)other_lop, M_NFSDLOCK); 1764191783Srmacklem other_lop = NULL; 1765191783Srmacklem } 1766191783Srmacklem if (ret == -1) { 1767191783Srmacklem lckstp = NULL; 1768191783Srmacklem goto tryagain; 1769191783Srmacklem } 1770224086Szack error = ret; 1771224086Szack goto out; 1772191783Srmacklem } 1773205941Srmacklem /* Never gets here. */ 1774191783Srmacklem } 1775191783Srmacklem tstp = nstp; 1776191783Srmacklem } 1777191783Srmacklem 1778191783Srmacklem /* 1779191783Srmacklem * Handle the unlock case by calling nfsrv_updatelock(). 1780191783Srmacklem * (Should I have done some access checking above for unlock? For now, 1781191783Srmacklem * just let it happen.) 1782191783Srmacklem */ 1783191783Srmacklem if (new_stp->ls_flags & NFSLCK_UNLOCK) { 1784205941Srmacklem first = new_lop->lo_first; 1785205941Srmacklem end = new_lop->lo_end; 1786191783Srmacklem nfsrv_updatelock(stp, new_lopp, &other_lop, lfp); 1787191783Srmacklem stateidp->seqid = ++(stp->ls_stateid.seqid); 1788191783Srmacklem stateidp->other[0] = stp->ls_stateid.other[0]; 1789191783Srmacklem stateidp->other[1] = stp->ls_stateid.other[1]; 1790191783Srmacklem stateidp->other[2] = stp->ls_stateid.other[2]; 1791205941Srmacklem if (filestruct_locked != 0) { 1792205941Srmacklem NFSUNLOCKSTATE(); 1793205941Srmacklem /* Update the local locks. */ 1794205941Srmacklem nfsrv_localunlock(vp, lfp, first, end, p); 1795205941Srmacklem NFSLOCKSTATE(); 1796205941Srmacklem nfsrv_unlocklf(lfp); 1797205941Srmacklem } 1798191783Srmacklem NFSUNLOCKSTATE(); 1799224086Szack goto out; 1800191783Srmacklem } 1801191783Srmacklem 1802191783Srmacklem /* 1803191783Srmacklem * Search for a conflicting lock. A lock conflicts if: 1804191783Srmacklem * - the lock range overlaps and 1805191783Srmacklem * - at least one lock is a write lock and 1806191783Srmacklem * - it is not owned by the same lock owner 1807191783Srmacklem */ 1808191783Srmacklem if (!delegation) { 1809191783Srmacklem LIST_FOREACH(lop, &lfp->lf_lock, lo_lckfile) { 1810191783Srmacklem if (new_lop->lo_end > lop->lo_first && 1811191783Srmacklem new_lop->lo_first < lop->lo_end && 1812191783Srmacklem (new_lop->lo_flags == NFSLCK_WRITE || 1813191783Srmacklem lop->lo_flags == NFSLCK_WRITE) && 1814191783Srmacklem lckstp != lop->lo_stp && 1815201442Srmacklem (clp != lop->lo_stp->ls_clp || 1816191783Srmacklem lckstp->ls_ownerlen != lop->lo_stp->ls_ownerlen || 1817191783Srmacklem NFSBCMP(lckstp->ls_owner, lop->lo_stp->ls_owner, 1818191783Srmacklem lckstp->ls_ownerlen))) { 1819191783Srmacklem if (other_lop) { 1820191783Srmacklem FREE((caddr_t)other_lop, M_NFSDLOCK); 1821191783Srmacklem other_lop = NULL; 1822191783Srmacklem } 1823191783Srmacklem ret = nfsrv_clientconflict(lop->lo_stp->ls_clp,&haslock,vp,p); 1824216893Srmacklem if (ret == 1) { 1825205941Srmacklem if (filestruct_locked != 0) { 1826205941Srmacklem /* Roll back local locks. */ 1827205941Srmacklem nfsrv_locallock_rollback(vp, lfp, p); 1828205941Srmacklem NFSLOCKSTATE(); 1829205941Srmacklem nfsrv_unlocklf(lfp); 1830205941Srmacklem NFSUNLOCKSTATE(); 1831205941Srmacklem } 1832191783Srmacklem /* 1833191783Srmacklem * nfsrv_clientconflict() unlocks state when it 1834191783Srmacklem * returns non-zero. 1835191783Srmacklem */ 1836191783Srmacklem lckstp = NULL; 1837191783Srmacklem goto tryagain; 1838191783Srmacklem } 1839191783Srmacklem /* 1840191783Srmacklem * Found a conflicting lock, so record the conflict and 1841191783Srmacklem * return the error. 1842191783Srmacklem */ 1843216893Srmacklem if (cfp != NULL && ret == 0) { 1844191783Srmacklem cfp->cl_clientid.lval[0]=lop->lo_stp->ls_stateid.other[0]; 1845191783Srmacklem cfp->cl_clientid.lval[1]=lop->lo_stp->ls_stateid.other[1]; 1846191783Srmacklem cfp->cl_first = lop->lo_first; 1847191783Srmacklem cfp->cl_end = lop->lo_end; 1848191783Srmacklem cfp->cl_flags = lop->lo_flags; 1849191783Srmacklem cfp->cl_ownerlen = lop->lo_stp->ls_ownerlen; 1850191783Srmacklem NFSBCOPY(lop->lo_stp->ls_owner, cfp->cl_owner, 1851191783Srmacklem cfp->cl_ownerlen); 1852191783Srmacklem } 1853216893Srmacklem if (ret == 2) 1854216893Srmacklem error = NFSERR_PERM; 1855216893Srmacklem else if (new_stp->ls_flags & NFSLCK_RECLAIM) 1856191783Srmacklem error = NFSERR_RECLAIMCONFLICT; 1857191783Srmacklem else if (new_stp->ls_flags & NFSLCK_CHECK) 1858191783Srmacklem error = NFSERR_LOCKED; 1859191783Srmacklem else 1860191783Srmacklem error = NFSERR_DENIED; 1861216893Srmacklem if (filestruct_locked != 0 && ret == 0) { 1862205941Srmacklem /* Roll back local locks. */ 1863205941Srmacklem NFSUNLOCKSTATE(); 1864205941Srmacklem nfsrv_locallock_rollback(vp, lfp, p); 1865205941Srmacklem NFSLOCKSTATE(); 1866205941Srmacklem nfsrv_unlocklf(lfp); 1867205941Srmacklem } 1868216893Srmacklem if (ret == 0) 1869216893Srmacklem NFSUNLOCKSTATE(); 1870224086Szack goto out; 1871191783Srmacklem } 1872191783Srmacklem } 1873191783Srmacklem } 1874191783Srmacklem 1875191783Srmacklem /* 1876191783Srmacklem * We only get here if there was no lock that conflicted. 1877191783Srmacklem */ 1878191783Srmacklem if (new_stp->ls_flags & (NFSLCK_TEST | NFSLCK_CHECK)) { 1879191783Srmacklem NFSUNLOCKSTATE(); 1880224086Szack goto out; 1881191783Srmacklem } 1882191783Srmacklem 1883191783Srmacklem /* 1884191783Srmacklem * We only get here when we are creating or modifying a lock. 1885191783Srmacklem * There are two variants: 1886191783Srmacklem * - exist_lock_owner where lock_owner exists 1887191783Srmacklem * - open_to_lock_owner with new lock_owner 1888191783Srmacklem */ 1889205941Srmacklem first = new_lop->lo_first; 1890205941Srmacklem end = new_lop->lo_end; 1891205941Srmacklem lock_flags = new_lop->lo_flags; 1892191783Srmacklem if (!(new_stp->ls_flags & NFSLCK_OPENTOLOCK)) { 1893191783Srmacklem nfsrv_updatelock(lckstp, new_lopp, &other_lop, lfp); 1894191783Srmacklem stateidp->seqid = ++(lckstp->ls_stateid.seqid); 1895191783Srmacklem stateidp->other[0] = lckstp->ls_stateid.other[0]; 1896191783Srmacklem stateidp->other[1] = lckstp->ls_stateid.other[1]; 1897191783Srmacklem stateidp->other[2] = lckstp->ls_stateid.other[2]; 1898191783Srmacklem } else { 1899191783Srmacklem /* 1900191783Srmacklem * The new open_to_lock_owner case. 1901191783Srmacklem * Link the new nfsstate into the lists. 1902191783Srmacklem */ 1903191783Srmacklem new_stp->ls_seq = new_stp->ls_opentolockseq; 1904191783Srmacklem nfsrvd_refcache(new_stp->ls_op); 1905217336Szack stateidp->seqid = new_stp->ls_stateid.seqid = 1; 1906191783Srmacklem stateidp->other[0] = new_stp->ls_stateid.other[0] = 1907191783Srmacklem clp->lc_clientid.lval[0]; 1908191783Srmacklem stateidp->other[1] = new_stp->ls_stateid.other[1] = 1909191783Srmacklem clp->lc_clientid.lval[1]; 1910191783Srmacklem stateidp->other[2] = new_stp->ls_stateid.other[2] = 1911191783Srmacklem nfsrv_nextstateindex(clp); 1912191783Srmacklem new_stp->ls_clp = clp; 1913191783Srmacklem LIST_INIT(&new_stp->ls_lock); 1914191783Srmacklem new_stp->ls_openstp = stp; 1915191783Srmacklem new_stp->ls_lfp = lfp; 1916191783Srmacklem nfsrv_insertlock(new_lop, (struct nfslock *)new_stp, new_stp, 1917191783Srmacklem lfp); 1918191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_stp->ls_stateid), 1919191783Srmacklem new_stp, ls_hash); 1920191783Srmacklem LIST_INSERT_HEAD(&stp->ls_open, new_stp, ls_list); 1921191783Srmacklem *new_lopp = NULL; 1922191783Srmacklem *new_stpp = NULL; 1923191783Srmacklem newnfsstats.srvlockowners++; 1924191783Srmacklem nfsrv_openpluslock++; 1925191783Srmacklem } 1926205941Srmacklem if (filestruct_locked != 0) { 1927205941Srmacklem NFSUNLOCKSTATE(); 1928205941Srmacklem nfsrv_locallock_commit(lfp, lock_flags, first, end); 1929205941Srmacklem NFSLOCKSTATE(); 1930205941Srmacklem nfsrv_unlocklf(lfp); 1931205941Srmacklem } 1932191783Srmacklem NFSUNLOCKSTATE(); 1933224086Szack 1934224086Szackout: 1935191783Srmacklem if (haslock) { 1936191783Srmacklem NFSLOCKV4ROOTMUTEX(); 1937191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 1938191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 1939191783Srmacklem } 1940191783Srmacklem if (other_lop) 1941191783Srmacklem FREE((caddr_t)other_lop, M_NFSDLOCK); 1942224086Szack NFSEXITCODE2(error, nd); 1943224086Szack return (error); 1944191783Srmacklem} 1945191783Srmacklem 1946191783Srmacklem/* 1947191783Srmacklem * Check for state errors for Open. 1948191783Srmacklem * repstat is passed back out as an error if more critical errors 1949191783Srmacklem * are not detected. 1950191783Srmacklem */ 1951191783SrmacklemAPPLESTATIC int 1952191783Srmacklemnfsrv_opencheck(nfsquad_t clientid, nfsv4stateid_t *stateidp, 1953191783Srmacklem struct nfsstate *new_stp, vnode_t vp, struct nfsrv_descript *nd, 1954191783Srmacklem NFSPROC_T *p, int repstat) 1955191783Srmacklem{ 1956191783Srmacklem struct nfsstate *stp, *nstp; 1957191783Srmacklem struct nfsclient *clp; 1958191783Srmacklem struct nfsstate *ownerstp; 1959191783Srmacklem struct nfslockfile *lfp, *new_lfp; 1960224086Szack int error = 0, haslock = 0, ret, readonly = 0, getfhret = 0; 1961191783Srmacklem 1962191783Srmacklem if ((new_stp->ls_flags & NFSLCK_SHAREBITS) == NFSLCK_READACCESS) 1963191783Srmacklem readonly = 1; 1964191783Srmacklem /* 1965191783Srmacklem * Check for restart conditions (client and server). 1966191783Srmacklem */ 1967191783Srmacklem error = nfsrv_checkrestart(clientid, new_stp->ls_flags, 1968191783Srmacklem &new_stp->ls_stateid, 0); 1969191783Srmacklem if (error) 1970224086Szack goto out; 1971191783Srmacklem 1972191783Srmacklem /* 1973191783Srmacklem * Check for state resource limit exceeded. 1974191783Srmacklem * Technically this should be SMP protected, but the worst 1975191783Srmacklem * case error is "out by one or two" on the count when it 1976191783Srmacklem * returns NFSERR_RESOURCE and the limit is just a rather 1977191783Srmacklem * arbitrary high water mark, so no harm is done. 1978191783Srmacklem */ 1979224086Szack if (nfsrv_openpluslock > NFSRV_V4STATELIMIT) { 1980224086Szack error = NFSERR_RESOURCE; 1981224086Szack goto out; 1982224086Szack } 1983191783Srmacklem 1984191783Srmacklemtryagain: 1985191783Srmacklem MALLOC(new_lfp, struct nfslockfile *, sizeof (struct nfslockfile), 1986191783Srmacklem M_NFSDLOCKFILE, M_WAITOK); 1987191783Srmacklem if (vp) 1988191783Srmacklem getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, &new_lfp, 1989191783Srmacklem NULL, p); 1990191783Srmacklem NFSLOCKSTATE(); 1991191783Srmacklem /* 1992191783Srmacklem * Get the nfsclient structure. 1993191783Srmacklem */ 1994191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 1995191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 1996191783Srmacklem 1997191783Srmacklem /* 1998191783Srmacklem * Look up the open owner. See if it needs confirmation and 1999191783Srmacklem * check the seq#, as required. 2000191783Srmacklem */ 2001191783Srmacklem if (!error) 2002191783Srmacklem nfsrv_getowner(&clp->lc_open, new_stp, &ownerstp); 2003191783Srmacklem 2004191783Srmacklem if (!error && ownerstp) { 2005191783Srmacklem error = nfsrv_checkseqid(nd, new_stp->ls_seq, ownerstp, 2006191783Srmacklem new_stp->ls_op); 2007191783Srmacklem /* 2008191783Srmacklem * If the OpenOwner hasn't been confirmed, assume the 2009191783Srmacklem * old one was a replay and this one is ok. 2010191783Srmacklem * See: RFC3530 Sec. 14.2.18. 2011191783Srmacklem */ 2012191783Srmacklem if (error == NFSERR_BADSEQID && 2013191783Srmacklem (ownerstp->ls_flags & NFSLCK_NEEDSCONFIRM)) 2014191783Srmacklem error = 0; 2015191783Srmacklem } 2016191783Srmacklem 2017191783Srmacklem /* 2018191783Srmacklem * Check for grace. 2019191783Srmacklem */ 2020191783Srmacklem if (!error) 2021191783Srmacklem error = nfsrv_checkgrace(new_stp->ls_flags); 2022191783Srmacklem if ((new_stp->ls_flags & NFSLCK_RECLAIM) && !error && 2023191783Srmacklem nfsrv_checkstable(clp)) 2024191783Srmacklem error = NFSERR_NOGRACE; 2025191783Srmacklem 2026191783Srmacklem /* 2027191783Srmacklem * If none of the above errors occurred, let repstat be 2028191783Srmacklem * returned. 2029191783Srmacklem */ 2030191783Srmacklem if (repstat && !error) 2031191783Srmacklem error = repstat; 2032191783Srmacklem if (error) { 2033191783Srmacklem NFSUNLOCKSTATE(); 2034191783Srmacklem if (haslock) { 2035191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2036191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2037191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2038191783Srmacklem } 2039191783Srmacklem free((caddr_t)new_lfp, M_NFSDLOCKFILE); 2040224086Szack goto out; 2041191783Srmacklem } 2042191783Srmacklem 2043191783Srmacklem /* 2044191783Srmacklem * If vp == NULL, the file doesn't exist yet, so return ok. 2045191783Srmacklem * (This always happens on the first pass, so haslock must be 0.) 2046191783Srmacklem */ 2047191783Srmacklem if (vp == NULL) { 2048191783Srmacklem NFSUNLOCKSTATE(); 2049191783Srmacklem FREE((caddr_t)new_lfp, M_NFSDLOCKFILE); 2050224086Szack goto out; 2051191783Srmacklem } 2052191783Srmacklem 2053191783Srmacklem /* 2054191783Srmacklem * Get the structure for the underlying file. 2055191783Srmacklem */ 2056191783Srmacklem if (getfhret) 2057191783Srmacklem error = getfhret; 2058191783Srmacklem else 2059191783Srmacklem error = nfsrv_getlockfile(new_stp->ls_flags, &new_lfp, &lfp, 2060205941Srmacklem NULL, 0); 2061191783Srmacklem if (new_lfp) 2062191783Srmacklem FREE((caddr_t)new_lfp, M_NFSDLOCKFILE); 2063191783Srmacklem if (error) { 2064191783Srmacklem NFSUNLOCKSTATE(); 2065191783Srmacklem if (haslock) { 2066191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2067191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2068191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2069191783Srmacklem } 2070224086Szack goto out; 2071191783Srmacklem } 2072191783Srmacklem 2073191783Srmacklem /* 2074191783Srmacklem * Search for a conflicting open/share. 2075191783Srmacklem */ 2076191783Srmacklem if (new_stp->ls_flags & NFSLCK_DELEGCUR) { 2077191783Srmacklem /* 2078191783Srmacklem * For Delegate_Cur, search for the matching Delegation, 2079191783Srmacklem * which indicates no conflict. 2080191783Srmacklem * An old delegation should have been recovered by the 2081191783Srmacklem * client doing a Claim_DELEGATE_Prev, so I won't let 2082191783Srmacklem * it match and return NFSERR_EXPIRED. Should I let it 2083191783Srmacklem * match? 2084191783Srmacklem */ 2085191783Srmacklem LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { 2086191783Srmacklem if (!(stp->ls_flags & NFSLCK_OLDDELEG) && 2087191783Srmacklem stateidp->seqid == stp->ls_stateid.seqid && 2088191783Srmacklem !NFSBCMP(stateidp->other, stp->ls_stateid.other, 2089191783Srmacklem NFSX_STATEIDOTHER)) 2090191783Srmacklem break; 2091191783Srmacklem } 2092191783Srmacklem if (stp == LIST_END(&lfp->lf_deleg) || 2093191783Srmacklem ((new_stp->ls_flags & NFSLCK_WRITEACCESS) && 2094191783Srmacklem (stp->ls_flags & NFSLCK_DELEGREAD))) { 2095191783Srmacklem NFSUNLOCKSTATE(); 2096191783Srmacklem if (haslock) { 2097191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2098191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2099191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2100191783Srmacklem } 2101224086Szack error = NFSERR_EXPIRED; 2102224086Szack goto out; 2103191783Srmacklem } 2104191783Srmacklem } 2105191783Srmacklem 2106191783Srmacklem /* 2107191783Srmacklem * Check for access/deny bit conflicts. I check for the same 2108191783Srmacklem * owner as well, in case the client didn't bother. 2109191783Srmacklem */ 2110191783Srmacklem LIST_FOREACH(stp, &lfp->lf_open, ls_file) { 2111191783Srmacklem if (!(new_stp->ls_flags & NFSLCK_DELEGCUR) && 2112191783Srmacklem (((new_stp->ls_flags & NFSLCK_ACCESSBITS) & 2113191783Srmacklem ((stp->ls_flags>>NFSLCK_SHIFT) & NFSLCK_ACCESSBITS))|| 2114191783Srmacklem ((stp->ls_flags & NFSLCK_ACCESSBITS) & 2115191783Srmacklem ((new_stp->ls_flags>>NFSLCK_SHIFT)&NFSLCK_ACCESSBITS)))){ 2116191783Srmacklem ret = nfsrv_clientconflict(stp->ls_clp,&haslock,vp,p); 2117216893Srmacklem if (ret == 1) { 2118191783Srmacklem /* 2119191783Srmacklem * nfsrv_clientconflict() unlocks 2120191783Srmacklem * state when it returns non-zero. 2121191783Srmacklem */ 2122191783Srmacklem goto tryagain; 2123191783Srmacklem } 2124216893Srmacklem if (ret == 2) 2125216893Srmacklem error = NFSERR_PERM; 2126216893Srmacklem else if (new_stp->ls_flags & NFSLCK_RECLAIM) 2127191783Srmacklem error = NFSERR_RECLAIMCONFLICT; 2128191783Srmacklem else 2129191783Srmacklem error = NFSERR_SHAREDENIED; 2130216893Srmacklem if (ret == 0) 2131216893Srmacklem NFSUNLOCKSTATE(); 2132191783Srmacklem if (haslock) { 2133191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2134191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2135191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2136191783Srmacklem } 2137224086Szack goto out; 2138191783Srmacklem } 2139191783Srmacklem } 2140191783Srmacklem 2141191783Srmacklem /* 2142191783Srmacklem * Check for a conflicting delegation. If one is found, call 2143191783Srmacklem * nfsrv_delegconflict() to handle it. If the v4root lock hasn't 2144191783Srmacklem * been set yet, it will get the lock. Otherwise, it will recall 2145191783Srmacklem * the delegation. Then, we try try again... 2146191783Srmacklem * (If NFSLCK_DELEGCUR is set, it has a delegation, so there 2147191783Srmacklem * isn't a conflict.) 2148191783Srmacklem * I currently believe the conflict algorithm to be: 2149191783Srmacklem * For Open with Read Access and Deny None 2150191783Srmacklem * - there is a conflict iff a different client has a write delegation 2151191783Srmacklem * For Open with other Write Access or any Deny except None 2152191783Srmacklem * - there is a conflict if a different client has any delegation 2153191783Srmacklem * - there is a conflict if the same client has a read delegation 2154191783Srmacklem * (The current concensus is that this last case should be 2155191783Srmacklem * considered a conflict since the client with a read delegation 2156191783Srmacklem * could have done an Open with ReadAccess and WriteDeny 2157191783Srmacklem * locally and then not have checked for the WriteDeny.) 2158191783Srmacklem * Don't check for a Reclaim, since that will be dealt with 2159191783Srmacklem * by nfsrv_openctrl(). 2160191783Srmacklem */ 2161191783Srmacklem if (!(new_stp->ls_flags & 2162191783Srmacklem (NFSLCK_DELEGPREV | NFSLCK_DELEGCUR | NFSLCK_RECLAIM))) { 2163191783Srmacklem stp = LIST_FIRST(&lfp->lf_deleg); 2164191783Srmacklem while (stp != LIST_END(&lfp->lf_deleg)) { 2165191783Srmacklem nstp = LIST_NEXT(stp, ls_file); 2166191783Srmacklem if ((readonly && stp->ls_clp != clp && 2167191783Srmacklem (stp->ls_flags & NFSLCK_DELEGWRITE)) || 2168191783Srmacklem (!readonly && (stp->ls_clp != clp || 2169191783Srmacklem (stp->ls_flags & NFSLCK_DELEGREAD)))) { 2170191783Srmacklem ret = nfsrv_delegconflict(stp, &haslock, p, vp); 2171191783Srmacklem if (ret) { 2172191783Srmacklem /* 2173191783Srmacklem * nfsrv_delegconflict() unlocks state 2174191783Srmacklem * when it returns non-zero. 2175191783Srmacklem */ 2176191783Srmacklem if (ret == -1) 2177191783Srmacklem goto tryagain; 2178224086Szack error = ret; 2179224086Szack goto out; 2180191783Srmacklem } 2181191783Srmacklem } 2182191783Srmacklem stp = nstp; 2183191783Srmacklem } 2184191783Srmacklem } 2185191783Srmacklem NFSUNLOCKSTATE(); 2186191783Srmacklem if (haslock) { 2187191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2188191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2189191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2190191783Srmacklem } 2191224086Szack 2192224086Szackout: 2193224086Szack NFSEXITCODE2(error, nd); 2194224086Szack return (error); 2195191783Srmacklem} 2196191783Srmacklem 2197191783Srmacklem/* 2198191783Srmacklem * Open control function to create/update open state for an open. 2199191783Srmacklem */ 2200191783SrmacklemAPPLESTATIC int 2201191783Srmacklemnfsrv_openctrl(struct nfsrv_descript *nd, vnode_t vp, 2202191783Srmacklem struct nfsstate **new_stpp, nfsquad_t clientid, nfsv4stateid_t *stateidp, 2203191783Srmacklem nfsv4stateid_t *delegstateidp, u_int32_t *rflagsp, struct nfsexstuff *exp, 2204191783Srmacklem NFSPROC_T *p, u_quad_t filerev) 2205191783Srmacklem{ 2206191783Srmacklem struct nfsstate *new_stp = *new_stpp; 2207191783Srmacklem struct nfsstate *stp, *nstp; 2208191783Srmacklem struct nfsstate *openstp = NULL, *new_open, *ownerstp, *new_deleg; 2209191783Srmacklem struct nfslockfile *lfp, *new_lfp; 2210191783Srmacklem struct nfsclient *clp; 2211224086Szack int error = 0, haslock = 0, ret, delegate = 1, writedeleg = 1; 2212191783Srmacklem int readonly = 0, cbret = 1, getfhret = 0; 2213191783Srmacklem 2214191783Srmacklem if ((new_stp->ls_flags & NFSLCK_SHAREBITS) == NFSLCK_READACCESS) 2215191783Srmacklem readonly = 1; 2216191783Srmacklem /* 2217191783Srmacklem * Check for restart conditions (client and server). 2218191783Srmacklem * (Paranoia, should have been detected by nfsrv_opencheck().) 2219191783Srmacklem * If an error does show up, return NFSERR_EXPIRED, since the 2220191783Srmacklem * the seqid# has already been incremented. 2221191783Srmacklem */ 2222191783Srmacklem error = nfsrv_checkrestart(clientid, new_stp->ls_flags, 2223191783Srmacklem &new_stp->ls_stateid, 0); 2224191783Srmacklem if (error) { 2225191783Srmacklem printf("Nfsd: openctrl unexpected restart err=%d\n", 2226191783Srmacklem error); 2227224086Szack error = NFSERR_EXPIRED; 2228224086Szack goto out; 2229191783Srmacklem } 2230191783Srmacklem 2231191783Srmacklemtryagain: 2232191783Srmacklem MALLOC(new_lfp, struct nfslockfile *, sizeof (struct nfslockfile), 2233191783Srmacklem M_NFSDLOCKFILE, M_WAITOK); 2234191783Srmacklem MALLOC(new_open, struct nfsstate *, sizeof (struct nfsstate), 2235191783Srmacklem M_NFSDSTATE, M_WAITOK); 2236191783Srmacklem MALLOC(new_deleg, struct nfsstate *, sizeof (struct nfsstate), 2237191783Srmacklem M_NFSDSTATE, M_WAITOK); 2238191783Srmacklem getfhret = nfsrv_getlockfh(vp, new_stp->ls_flags, &new_lfp, 2239191783Srmacklem NULL, p); 2240191783Srmacklem NFSLOCKSTATE(); 2241191783Srmacklem /* 2242191783Srmacklem * Get the client structure. Since the linked lists could be changed 2243191783Srmacklem * by other nfsd processes if this process does a tsleep(), one of 2244191783Srmacklem * two things must be done. 2245191783Srmacklem * 1 - don't tsleep() 2246191783Srmacklem * or 2247191783Srmacklem * 2 - get the nfsv4_lock() { indicated by haslock == 1 } 2248191783Srmacklem * before using the lists, since this lock stops the other 2249191783Srmacklem * nfsd. This should only be used for rare cases, since it 2250191783Srmacklem * essentially single threads the nfsd. 2251191783Srmacklem * At this time, it is only done for cases where the stable 2252191783Srmacklem * storage file must be written prior to completion of state 2253191783Srmacklem * expiration. 2254191783Srmacklem */ 2255191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 2256191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 2257191783Srmacklem if (!error && (clp->lc_flags & LCL_NEEDSCBNULL) && 2258191783Srmacklem clp->lc_program) { 2259191783Srmacklem /* 2260191783Srmacklem * This happens on the first open for a client 2261191783Srmacklem * that supports callbacks. 2262191783Srmacklem */ 2263191783Srmacklem NFSUNLOCKSTATE(); 2264191783Srmacklem /* 2265191783Srmacklem * Although nfsrv_docallback() will sleep, clp won't 2266191783Srmacklem * go away, since they are only removed when the 2267191783Srmacklem * nfsv4_lock() has blocked the nfsd threads. The 2268191783Srmacklem * fields in clp can change, but having multiple 2269191783Srmacklem * threads do this Null callback RPC should be 2270191783Srmacklem * harmless. 2271191783Srmacklem */ 2272191783Srmacklem cbret = nfsrv_docallback(clp, NFSV4PROC_CBNULL, 2273191783Srmacklem NULL, 0, NULL, NULL, NULL, p); 2274191783Srmacklem NFSLOCKSTATE(); 2275191783Srmacklem clp->lc_flags &= ~LCL_NEEDSCBNULL; 2276191783Srmacklem if (!cbret) 2277191783Srmacklem clp->lc_flags |= LCL_CALLBACKSON; 2278191783Srmacklem } 2279191783Srmacklem 2280191783Srmacklem /* 2281191783Srmacklem * Look up the open owner. See if it needs confirmation and 2282191783Srmacklem * check the seq#, as required. 2283191783Srmacklem */ 2284191783Srmacklem if (!error) 2285191783Srmacklem nfsrv_getowner(&clp->lc_open, new_stp, &ownerstp); 2286191783Srmacklem 2287191783Srmacklem if (error) { 2288191783Srmacklem NFSUNLOCKSTATE(); 2289191783Srmacklem printf("Nfsd: openctrl unexpected state err=%d\n", 2290191783Srmacklem error); 2291191783Srmacklem free((caddr_t)new_lfp, M_NFSDLOCKFILE); 2292191783Srmacklem free((caddr_t)new_open, M_NFSDSTATE); 2293191783Srmacklem free((caddr_t)new_deleg, M_NFSDSTATE); 2294191783Srmacklem if (haslock) { 2295191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2296191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2297191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2298191783Srmacklem } 2299224086Szack error = NFSERR_EXPIRED; 2300224086Szack goto out; 2301191783Srmacklem } 2302191783Srmacklem 2303191783Srmacklem if (new_stp->ls_flags & NFSLCK_RECLAIM) 2304191783Srmacklem nfsrv_markstable(clp); 2305191783Srmacklem 2306191783Srmacklem /* 2307191783Srmacklem * Get the structure for the underlying file. 2308191783Srmacklem */ 2309191783Srmacklem if (getfhret) 2310191783Srmacklem error = getfhret; 2311191783Srmacklem else 2312191783Srmacklem error = nfsrv_getlockfile(new_stp->ls_flags, &new_lfp, &lfp, 2313205941Srmacklem NULL, 0); 2314191783Srmacklem if (new_lfp) 2315191783Srmacklem FREE((caddr_t)new_lfp, M_NFSDLOCKFILE); 2316191783Srmacklem if (error) { 2317191783Srmacklem NFSUNLOCKSTATE(); 2318191783Srmacklem printf("Nfsd openctrl unexpected getlockfile err=%d\n", 2319191783Srmacklem error); 2320191783Srmacklem free((caddr_t)new_open, M_NFSDSTATE); 2321191783Srmacklem free((caddr_t)new_deleg, M_NFSDSTATE); 2322191783Srmacklem if (haslock) { 2323191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2324191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2325191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2326191783Srmacklem } 2327224086Szack goto out; 2328191783Srmacklem } 2329191783Srmacklem 2330191783Srmacklem /* 2331191783Srmacklem * Search for a conflicting open/share. 2332191783Srmacklem */ 2333191783Srmacklem if (new_stp->ls_flags & NFSLCK_DELEGCUR) { 2334191783Srmacklem /* 2335191783Srmacklem * For Delegate_Cur, search for the matching Delegation, 2336191783Srmacklem * which indicates no conflict. 2337191783Srmacklem * An old delegation should have been recovered by the 2338191783Srmacklem * client doing a Claim_DELEGATE_Prev, so I won't let 2339191783Srmacklem * it match and return NFSERR_EXPIRED. Should I let it 2340191783Srmacklem * match? 2341191783Srmacklem */ 2342191783Srmacklem LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { 2343191783Srmacklem if (!(stp->ls_flags & NFSLCK_OLDDELEG) && 2344191783Srmacklem stateidp->seqid == stp->ls_stateid.seqid && 2345191783Srmacklem !NFSBCMP(stateidp->other, stp->ls_stateid.other, 2346191783Srmacklem NFSX_STATEIDOTHER)) 2347191783Srmacklem break; 2348191783Srmacklem } 2349191783Srmacklem if (stp == LIST_END(&lfp->lf_deleg) || 2350191783Srmacklem ((new_stp->ls_flags & NFSLCK_WRITEACCESS) && 2351191783Srmacklem (stp->ls_flags & NFSLCK_DELEGREAD))) { 2352191783Srmacklem NFSUNLOCKSTATE(); 2353191783Srmacklem printf("Nfsd openctrl unexpected expiry\n"); 2354191783Srmacklem free((caddr_t)new_open, M_NFSDSTATE); 2355191783Srmacklem free((caddr_t)new_deleg, M_NFSDSTATE); 2356191783Srmacklem if (haslock) { 2357191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2358191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2359191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2360191783Srmacklem } 2361224086Szack error = NFSERR_EXPIRED; 2362224086Szack goto out; 2363191783Srmacklem } 2364191783Srmacklem 2365191783Srmacklem /* 2366191783Srmacklem * Don't issue a Delegation, since one already exists and 2367191783Srmacklem * delay delegation timeout, as required. 2368191783Srmacklem */ 2369191783Srmacklem delegate = 0; 2370191783Srmacklem nfsrv_delaydelegtimeout(stp); 2371191783Srmacklem } 2372191783Srmacklem 2373191783Srmacklem /* 2374191783Srmacklem * Check for access/deny bit conflicts. I also check for the 2375191783Srmacklem * same owner, since the client might not have bothered to check. 2376191783Srmacklem * Also, note an open for the same file and owner, if found, 2377191783Srmacklem * which is all we do here for Delegate_Cur, since conflict 2378191783Srmacklem * checking is already done. 2379191783Srmacklem */ 2380191783Srmacklem LIST_FOREACH(stp, &lfp->lf_open, ls_file) { 2381191783Srmacklem if (ownerstp && stp->ls_openowner == ownerstp) 2382191783Srmacklem openstp = stp; 2383191783Srmacklem if (!(new_stp->ls_flags & NFSLCK_DELEGCUR)) { 2384191783Srmacklem /* 2385191783Srmacklem * If another client has the file open, the only 2386191783Srmacklem * delegation that can be issued is a Read delegation 2387191783Srmacklem * and only if it is a Read open with Deny none. 2388191783Srmacklem */ 2389191783Srmacklem if (clp != stp->ls_clp) { 2390191783Srmacklem if ((stp->ls_flags & NFSLCK_SHAREBITS) == 2391191783Srmacklem NFSLCK_READACCESS) 2392191783Srmacklem writedeleg = 0; 2393191783Srmacklem else 2394191783Srmacklem delegate = 0; 2395191783Srmacklem } 2396191783Srmacklem if(((new_stp->ls_flags & NFSLCK_ACCESSBITS) & 2397191783Srmacklem ((stp->ls_flags>>NFSLCK_SHIFT) & NFSLCK_ACCESSBITS))|| 2398191783Srmacklem ((stp->ls_flags & NFSLCK_ACCESSBITS) & 2399191783Srmacklem ((new_stp->ls_flags>>NFSLCK_SHIFT)&NFSLCK_ACCESSBITS))){ 2400191783Srmacklem ret = nfsrv_clientconflict(stp->ls_clp,&haslock,vp,p); 2401216893Srmacklem if (ret == 1) { 2402191783Srmacklem /* 2403191783Srmacklem * nfsrv_clientconflict() unlocks state 2404191783Srmacklem * when it returns non-zero. 2405191783Srmacklem */ 2406191783Srmacklem free((caddr_t)new_open, M_NFSDSTATE); 2407191783Srmacklem free((caddr_t)new_deleg, M_NFSDSTATE); 2408191783Srmacklem openstp = NULL; 2409191783Srmacklem goto tryagain; 2410191783Srmacklem } 2411216893Srmacklem if (ret == 2) 2412216893Srmacklem error = NFSERR_PERM; 2413216893Srmacklem else if (new_stp->ls_flags & NFSLCK_RECLAIM) 2414191783Srmacklem error = NFSERR_RECLAIMCONFLICT; 2415191783Srmacklem else 2416191783Srmacklem error = NFSERR_SHAREDENIED; 2417216893Srmacklem if (ret == 0) 2418216893Srmacklem NFSUNLOCKSTATE(); 2419191783Srmacklem if (haslock) { 2420191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2421191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2422191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2423191783Srmacklem } 2424191783Srmacklem free((caddr_t)new_open, M_NFSDSTATE); 2425191783Srmacklem free((caddr_t)new_deleg, M_NFSDSTATE); 2426191783Srmacklem printf("nfsd openctrl unexpected client cnfl\n"); 2427224086Szack goto out; 2428191783Srmacklem } 2429191783Srmacklem } 2430191783Srmacklem } 2431191783Srmacklem 2432191783Srmacklem /* 2433191783Srmacklem * Check for a conflicting delegation. If one is found, call 2434191783Srmacklem * nfsrv_delegconflict() to handle it. If the v4root lock hasn't 2435191783Srmacklem * been set yet, it will get the lock. Otherwise, it will recall 2436191783Srmacklem * the delegation. Then, we try try again... 2437191783Srmacklem * (If NFSLCK_DELEGCUR is set, it has a delegation, so there 2438191783Srmacklem * isn't a conflict.) 2439191783Srmacklem * I currently believe the conflict algorithm to be: 2440191783Srmacklem * For Open with Read Access and Deny None 2441191783Srmacklem * - there is a conflict iff a different client has a write delegation 2442191783Srmacklem * For Open with other Write Access or any Deny except None 2443191783Srmacklem * - there is a conflict if a different client has any delegation 2444191783Srmacklem * - there is a conflict if the same client has a read delegation 2445191783Srmacklem * (The current concensus is that this last case should be 2446191783Srmacklem * considered a conflict since the client with a read delegation 2447191783Srmacklem * could have done an Open with ReadAccess and WriteDeny 2448191783Srmacklem * locally and then not have checked for the WriteDeny.) 2449191783Srmacklem */ 2450191783Srmacklem if (!(new_stp->ls_flags & (NFSLCK_DELEGPREV | NFSLCK_DELEGCUR))) { 2451191783Srmacklem stp = LIST_FIRST(&lfp->lf_deleg); 2452191783Srmacklem while (stp != LIST_END(&lfp->lf_deleg)) { 2453191783Srmacklem nstp = LIST_NEXT(stp, ls_file); 2454191783Srmacklem if (stp->ls_clp != clp && (stp->ls_flags & NFSLCK_DELEGREAD)) 2455191783Srmacklem writedeleg = 0; 2456191783Srmacklem else 2457191783Srmacklem delegate = 0; 2458191783Srmacklem if ((readonly && stp->ls_clp != clp && 2459191783Srmacklem (stp->ls_flags & NFSLCK_DELEGWRITE)) || 2460191783Srmacklem (!readonly && (stp->ls_clp != clp || 2461191783Srmacklem (stp->ls_flags & NFSLCK_DELEGREAD)))) { 2462191783Srmacklem if (new_stp->ls_flags & NFSLCK_RECLAIM) { 2463191783Srmacklem delegate = 2; 2464191783Srmacklem } else { 2465191783Srmacklem ret = nfsrv_delegconflict(stp, &haslock, p, vp); 2466191783Srmacklem if (ret) { 2467191783Srmacklem /* 2468191783Srmacklem * nfsrv_delegconflict() unlocks state 2469191783Srmacklem * when it returns non-zero. 2470191783Srmacklem */ 2471191783Srmacklem printf("Nfsd openctrl unexpected deleg cnfl\n"); 2472191783Srmacklem free((caddr_t)new_open, M_NFSDSTATE); 2473191783Srmacklem free((caddr_t)new_deleg, M_NFSDSTATE); 2474191783Srmacklem if (ret == -1) { 2475191783Srmacklem openstp = NULL; 2476191783Srmacklem goto tryagain; 2477191783Srmacklem } 2478224086Szack error = ret; 2479224086Szack goto out; 2480191783Srmacklem } 2481191783Srmacklem } 2482191783Srmacklem } 2483191783Srmacklem stp = nstp; 2484191783Srmacklem } 2485191783Srmacklem } 2486191783Srmacklem 2487191783Srmacklem /* 2488191783Srmacklem * We only get here if there was no open that conflicted. 2489191783Srmacklem * If an open for the owner exists, or in the access/deny bits. 2490191783Srmacklem * Otherwise it is a new open. If the open_owner hasn't been 2491191783Srmacklem * confirmed, replace the open with the new one needing confirmation, 2492191783Srmacklem * otherwise add the open. 2493191783Srmacklem */ 2494191783Srmacklem if (new_stp->ls_flags & NFSLCK_DELEGPREV) { 2495191783Srmacklem /* 2496191783Srmacklem * Handle NFSLCK_DELEGPREV by searching the old delegations for 2497191783Srmacklem * a match. If found, just move the old delegation to the current 2498191783Srmacklem * delegation list and issue open. If not found, return 2499191783Srmacklem * NFSERR_EXPIRED. 2500191783Srmacklem */ 2501191783Srmacklem LIST_FOREACH(stp, &clp->lc_olddeleg, ls_list) { 2502191783Srmacklem if (stp->ls_lfp == lfp) { 2503191783Srmacklem /* Found it */ 2504191783Srmacklem if (stp->ls_clp != clp) 2505191783Srmacklem panic("olddeleg clp"); 2506191783Srmacklem LIST_REMOVE(stp, ls_list); 2507191783Srmacklem LIST_REMOVE(stp, ls_hash); 2508191783Srmacklem stp->ls_flags &= ~NFSLCK_OLDDELEG; 2509191783Srmacklem stp->ls_stateid.seqid = delegstateidp->seqid = 0; 2510191783Srmacklem stp->ls_stateid.other[0] = delegstateidp->other[0] = 2511191783Srmacklem clp->lc_clientid.lval[0]; 2512191783Srmacklem stp->ls_stateid.other[1] = delegstateidp->other[1] = 2513191783Srmacklem clp->lc_clientid.lval[1]; 2514191783Srmacklem stp->ls_stateid.other[2] = delegstateidp->other[2] = 2515191783Srmacklem nfsrv_nextstateindex(clp); 2516191783Srmacklem stp->ls_compref = nd->nd_compref; 2517191783Srmacklem LIST_INSERT_HEAD(&clp->lc_deleg, stp, ls_list); 2518191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, 2519191783Srmacklem stp->ls_stateid), stp, ls_hash); 2520191783Srmacklem if (stp->ls_flags & NFSLCK_DELEGWRITE) 2521191783Srmacklem *rflagsp |= NFSV4OPEN_WRITEDELEGATE; 2522191783Srmacklem else 2523191783Srmacklem *rflagsp |= NFSV4OPEN_READDELEGATE; 2524191783Srmacklem clp->lc_delegtime = NFSD_MONOSEC + 2525191783Srmacklem nfsrv_lease + NFSRV_LEASEDELTA; 2526191783Srmacklem 2527191783Srmacklem /* 2528191783Srmacklem * Now, do the associated open. 2529191783Srmacklem */ 2530191783Srmacklem new_open->ls_stateid.seqid = 0; 2531191783Srmacklem new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; 2532191783Srmacklem new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; 2533191783Srmacklem new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); 2534191783Srmacklem new_open->ls_flags = (new_stp->ls_flags&NFSLCK_DENYBITS)| 2535191783Srmacklem NFSLCK_OPEN; 2536191783Srmacklem if (stp->ls_flags & NFSLCK_DELEGWRITE) 2537191783Srmacklem new_open->ls_flags |= (NFSLCK_READACCESS | 2538191783Srmacklem NFSLCK_WRITEACCESS); 2539191783Srmacklem else 2540191783Srmacklem new_open->ls_flags |= NFSLCK_READACCESS; 2541191783Srmacklem new_open->ls_uid = new_stp->ls_uid; 2542191783Srmacklem new_open->ls_lfp = lfp; 2543191783Srmacklem new_open->ls_clp = clp; 2544191783Srmacklem LIST_INIT(&new_open->ls_open); 2545191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); 2546191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), 2547191783Srmacklem new_open, ls_hash); 2548191783Srmacklem /* 2549191783Srmacklem * and handle the open owner 2550191783Srmacklem */ 2551191783Srmacklem if (ownerstp) { 2552191783Srmacklem new_open->ls_openowner = ownerstp; 2553191783Srmacklem LIST_INSERT_HEAD(&ownerstp->ls_open,new_open,ls_list); 2554191783Srmacklem } else { 2555191783Srmacklem new_open->ls_openowner = new_stp; 2556191783Srmacklem new_stp->ls_flags = 0; 2557191783Srmacklem nfsrvd_refcache(new_stp->ls_op); 2558191783Srmacklem new_stp->ls_noopens = 0; 2559191783Srmacklem LIST_INIT(&new_stp->ls_open); 2560191783Srmacklem LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); 2561191783Srmacklem LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); 2562191783Srmacklem *new_stpp = NULL; 2563191783Srmacklem newnfsstats.srvopenowners++; 2564191783Srmacklem nfsrv_openpluslock++; 2565191783Srmacklem } 2566191783Srmacklem openstp = new_open; 2567191783Srmacklem new_open = NULL; 2568191783Srmacklem newnfsstats.srvopens++; 2569191783Srmacklem nfsrv_openpluslock++; 2570191783Srmacklem break; 2571191783Srmacklem } 2572191783Srmacklem } 2573191783Srmacklem if (stp == LIST_END(&clp->lc_olddeleg)) 2574191783Srmacklem error = NFSERR_EXPIRED; 2575191783Srmacklem } else if (new_stp->ls_flags & (NFSLCK_DELEGREAD | NFSLCK_DELEGWRITE)) { 2576191783Srmacklem /* 2577191783Srmacklem * Scan to see that no delegation for this client and file 2578191783Srmacklem * doesn't already exist. 2579191783Srmacklem * There also shouldn't yet be an Open for this file and 2580191783Srmacklem * openowner. 2581191783Srmacklem */ 2582191783Srmacklem LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { 2583191783Srmacklem if (stp->ls_clp == clp) 2584191783Srmacklem break; 2585191783Srmacklem } 2586191783Srmacklem if (stp == LIST_END(&lfp->lf_deleg) && openstp == NULL) { 2587191783Srmacklem /* 2588191783Srmacklem * This is the Claim_Previous case with a delegation 2589191783Srmacklem * type != Delegate_None. 2590191783Srmacklem */ 2591191783Srmacklem /* 2592191783Srmacklem * First, add the delegation. (Although we must issue the 2593191783Srmacklem * delegation, we can also ask for an immediate return.) 2594191783Srmacklem */ 2595191783Srmacklem new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; 2596191783Srmacklem new_deleg->ls_stateid.other[0] = delegstateidp->other[0] = 2597191783Srmacklem clp->lc_clientid.lval[0]; 2598191783Srmacklem new_deleg->ls_stateid.other[1] = delegstateidp->other[1] = 2599191783Srmacklem clp->lc_clientid.lval[1]; 2600191783Srmacklem new_deleg->ls_stateid.other[2] = delegstateidp->other[2] = 2601191783Srmacklem nfsrv_nextstateindex(clp); 2602191783Srmacklem if (new_stp->ls_flags & NFSLCK_DELEGWRITE) { 2603191783Srmacklem new_deleg->ls_flags = (NFSLCK_DELEGWRITE | 2604191783Srmacklem NFSLCK_READACCESS | NFSLCK_WRITEACCESS); 2605191783Srmacklem *rflagsp |= NFSV4OPEN_WRITEDELEGATE; 2606191783Srmacklem } else { 2607191783Srmacklem new_deleg->ls_flags = (NFSLCK_DELEGREAD | 2608191783Srmacklem NFSLCK_READACCESS); 2609191783Srmacklem *rflagsp |= NFSV4OPEN_READDELEGATE; 2610191783Srmacklem } 2611191783Srmacklem new_deleg->ls_uid = new_stp->ls_uid; 2612191783Srmacklem new_deleg->ls_lfp = lfp; 2613191783Srmacklem new_deleg->ls_clp = clp; 2614191783Srmacklem new_deleg->ls_filerev = filerev; 2615191783Srmacklem new_deleg->ls_compref = nd->nd_compref; 2616191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); 2617191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, 2618191783Srmacklem new_deleg->ls_stateid), new_deleg, ls_hash); 2619191783Srmacklem LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); 2620191783Srmacklem new_deleg = NULL; 2621191783Srmacklem if (delegate == 2 || nfsrv_issuedelegs == 0 || 2622191783Srmacklem (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) != 2623191783Srmacklem LCL_CALLBACKSON || 2624191783Srmacklem NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) || 2625191783Srmacklem !NFSVNO_DELEGOK(vp)) 2626191783Srmacklem *rflagsp |= NFSV4OPEN_RECALL; 2627191783Srmacklem newnfsstats.srvdelegates++; 2628191783Srmacklem nfsrv_openpluslock++; 2629191783Srmacklem nfsrv_delegatecnt++; 2630191783Srmacklem 2631191783Srmacklem /* 2632191783Srmacklem * Now, do the associated open. 2633191783Srmacklem */ 2634191783Srmacklem new_open->ls_stateid.seqid = 0; 2635191783Srmacklem new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; 2636191783Srmacklem new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; 2637191783Srmacklem new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); 2638191783Srmacklem new_open->ls_flags = (new_stp->ls_flags & NFSLCK_DENYBITS) | 2639191783Srmacklem NFSLCK_OPEN; 2640191783Srmacklem if (new_stp->ls_flags & NFSLCK_DELEGWRITE) 2641191783Srmacklem new_open->ls_flags |= (NFSLCK_READACCESS | 2642191783Srmacklem NFSLCK_WRITEACCESS); 2643191783Srmacklem else 2644191783Srmacklem new_open->ls_flags |= NFSLCK_READACCESS; 2645191783Srmacklem new_open->ls_uid = new_stp->ls_uid; 2646191783Srmacklem new_open->ls_lfp = lfp; 2647191783Srmacklem new_open->ls_clp = clp; 2648191783Srmacklem LIST_INIT(&new_open->ls_open); 2649191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); 2650191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), 2651191783Srmacklem new_open, ls_hash); 2652191783Srmacklem /* 2653191783Srmacklem * and handle the open owner 2654191783Srmacklem */ 2655191783Srmacklem if (ownerstp) { 2656191783Srmacklem new_open->ls_openowner = ownerstp; 2657191783Srmacklem LIST_INSERT_HEAD(&ownerstp->ls_open, new_open, ls_list); 2658191783Srmacklem } else { 2659191783Srmacklem new_open->ls_openowner = new_stp; 2660191783Srmacklem new_stp->ls_flags = 0; 2661191783Srmacklem nfsrvd_refcache(new_stp->ls_op); 2662191783Srmacklem new_stp->ls_noopens = 0; 2663191783Srmacklem LIST_INIT(&new_stp->ls_open); 2664191783Srmacklem LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); 2665191783Srmacklem LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); 2666191783Srmacklem *new_stpp = NULL; 2667191783Srmacklem newnfsstats.srvopenowners++; 2668191783Srmacklem nfsrv_openpluslock++; 2669191783Srmacklem } 2670191783Srmacklem openstp = new_open; 2671191783Srmacklem new_open = NULL; 2672191783Srmacklem newnfsstats.srvopens++; 2673191783Srmacklem nfsrv_openpluslock++; 2674191783Srmacklem } else { 2675191783Srmacklem error = NFSERR_RECLAIMCONFLICT; 2676191783Srmacklem } 2677191783Srmacklem } else if (ownerstp) { 2678191783Srmacklem if (ownerstp->ls_flags & NFSLCK_NEEDSCONFIRM) { 2679191783Srmacklem /* Replace the open */ 2680191783Srmacklem if (ownerstp->ls_op) 2681191783Srmacklem nfsrvd_derefcache(ownerstp->ls_op); 2682191783Srmacklem ownerstp->ls_op = new_stp->ls_op; 2683191783Srmacklem nfsrvd_refcache(ownerstp->ls_op); 2684191783Srmacklem ownerstp->ls_seq = new_stp->ls_seq; 2685191783Srmacklem *rflagsp |= NFSV4OPEN_RESULTCONFIRM; 2686191783Srmacklem stp = LIST_FIRST(&ownerstp->ls_open); 2687191783Srmacklem stp->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS) | 2688191783Srmacklem NFSLCK_OPEN; 2689191783Srmacklem stp->ls_stateid.seqid = 0; 2690191783Srmacklem stp->ls_uid = new_stp->ls_uid; 2691191783Srmacklem if (lfp != stp->ls_lfp) { 2692191783Srmacklem LIST_REMOVE(stp, ls_file); 2693191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_open, stp, ls_file); 2694191783Srmacklem stp->ls_lfp = lfp; 2695191783Srmacklem } 2696191783Srmacklem openstp = stp; 2697191783Srmacklem } else if (openstp) { 2698191783Srmacklem openstp->ls_flags |= (new_stp->ls_flags & NFSLCK_SHAREBITS); 2699191783Srmacklem openstp->ls_stateid.seqid++; 2700191783Srmacklem 2701191783Srmacklem /* 2702191783Srmacklem * This is where we can choose to issue a delegation. 2703191783Srmacklem */ 2704191783Srmacklem if (delegate && nfsrv_issuedelegs && 2705191783Srmacklem writedeleg && !NFSVNO_EXRDONLY(exp) && 2706191783Srmacklem (nfsrv_writedelegifpos || !readonly) && 2707191783Srmacklem (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == 2708191783Srmacklem LCL_CALLBACKSON && 2709191783Srmacklem !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && 2710191783Srmacklem NFSVNO_DELEGOK(vp)) { 2711191783Srmacklem new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; 2712191783Srmacklem new_deleg->ls_stateid.other[0] = delegstateidp->other[0] 2713191783Srmacklem = clp->lc_clientid.lval[0]; 2714191783Srmacklem new_deleg->ls_stateid.other[1] = delegstateidp->other[1] 2715191783Srmacklem = clp->lc_clientid.lval[1]; 2716191783Srmacklem new_deleg->ls_stateid.other[2] = delegstateidp->other[2] 2717191783Srmacklem = nfsrv_nextstateindex(clp); 2718191783Srmacklem new_deleg->ls_flags = (NFSLCK_DELEGWRITE | 2719191783Srmacklem NFSLCK_READACCESS | NFSLCK_WRITEACCESS); 2720191783Srmacklem *rflagsp |= NFSV4OPEN_WRITEDELEGATE; 2721191783Srmacklem new_deleg->ls_uid = new_stp->ls_uid; 2722191783Srmacklem new_deleg->ls_lfp = lfp; 2723191783Srmacklem new_deleg->ls_clp = clp; 2724191783Srmacklem new_deleg->ls_filerev = filerev; 2725191783Srmacklem new_deleg->ls_compref = nd->nd_compref; 2726191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); 2727191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, 2728191783Srmacklem new_deleg->ls_stateid), new_deleg, ls_hash); 2729191783Srmacklem LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); 2730191783Srmacklem new_deleg = NULL; 2731191783Srmacklem newnfsstats.srvdelegates++; 2732191783Srmacklem nfsrv_openpluslock++; 2733191783Srmacklem nfsrv_delegatecnt++; 2734191783Srmacklem } 2735191783Srmacklem } else { 2736191783Srmacklem new_open->ls_stateid.seqid = 0; 2737191783Srmacklem new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; 2738191783Srmacklem new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; 2739191783Srmacklem new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); 2740191783Srmacklem new_open->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS)| 2741191783Srmacklem NFSLCK_OPEN; 2742191783Srmacklem new_open->ls_uid = new_stp->ls_uid; 2743191783Srmacklem new_open->ls_openowner = ownerstp; 2744191783Srmacklem new_open->ls_lfp = lfp; 2745191783Srmacklem new_open->ls_clp = clp; 2746191783Srmacklem LIST_INIT(&new_open->ls_open); 2747191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); 2748191783Srmacklem LIST_INSERT_HEAD(&ownerstp->ls_open, new_open, ls_list); 2749191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), 2750191783Srmacklem new_open, ls_hash); 2751191783Srmacklem openstp = new_open; 2752191783Srmacklem new_open = NULL; 2753191783Srmacklem newnfsstats.srvopens++; 2754191783Srmacklem nfsrv_openpluslock++; 2755191783Srmacklem 2756191783Srmacklem /* 2757191783Srmacklem * This is where we can choose to issue a delegation. 2758191783Srmacklem */ 2759191783Srmacklem if (delegate && nfsrv_issuedelegs && 2760191783Srmacklem (writedeleg || readonly) && 2761191783Srmacklem (clp->lc_flags & (LCL_CALLBACKSON | LCL_CBDOWN)) == 2762191783Srmacklem LCL_CALLBACKSON && 2763191783Srmacklem !NFSRV_V4DELEGLIMIT(nfsrv_delegatecnt) && 2764191783Srmacklem NFSVNO_DELEGOK(vp)) { 2765191783Srmacklem new_deleg->ls_stateid.seqid = delegstateidp->seqid = 0; 2766191783Srmacklem new_deleg->ls_stateid.other[0] = delegstateidp->other[0] 2767191783Srmacklem = clp->lc_clientid.lval[0]; 2768191783Srmacklem new_deleg->ls_stateid.other[1] = delegstateidp->other[1] 2769191783Srmacklem = clp->lc_clientid.lval[1]; 2770191783Srmacklem new_deleg->ls_stateid.other[2] = delegstateidp->other[2] 2771191783Srmacklem = nfsrv_nextstateindex(clp); 2772191783Srmacklem if (writedeleg && !NFSVNO_EXRDONLY(exp) && 2773191783Srmacklem (nfsrv_writedelegifpos || !readonly)) { 2774191783Srmacklem new_deleg->ls_flags = (NFSLCK_DELEGWRITE | 2775191783Srmacklem NFSLCK_READACCESS | NFSLCK_WRITEACCESS); 2776191783Srmacklem *rflagsp |= NFSV4OPEN_WRITEDELEGATE; 2777191783Srmacklem } else { 2778191783Srmacklem new_deleg->ls_flags = (NFSLCK_DELEGREAD | 2779191783Srmacklem NFSLCK_READACCESS); 2780191783Srmacklem *rflagsp |= NFSV4OPEN_READDELEGATE; 2781191783Srmacklem } 2782191783Srmacklem new_deleg->ls_uid = new_stp->ls_uid; 2783191783Srmacklem new_deleg->ls_lfp = lfp; 2784191783Srmacklem new_deleg->ls_clp = clp; 2785191783Srmacklem new_deleg->ls_filerev = filerev; 2786191783Srmacklem new_deleg->ls_compref = nd->nd_compref; 2787191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_deleg, new_deleg, ls_file); 2788191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, 2789191783Srmacklem new_deleg->ls_stateid), new_deleg, ls_hash); 2790191783Srmacklem LIST_INSERT_HEAD(&clp->lc_deleg, new_deleg, ls_list); 2791191783Srmacklem new_deleg = NULL; 2792191783Srmacklem newnfsstats.srvdelegates++; 2793191783Srmacklem nfsrv_openpluslock++; 2794191783Srmacklem nfsrv_delegatecnt++; 2795191783Srmacklem } 2796191783Srmacklem } 2797191783Srmacklem } else { 2798191783Srmacklem /* 2799191783Srmacklem * New owner case. Start the open_owner sequence with a 2800191783Srmacklem * Needs confirmation (unless a reclaim) and hang the 2801191783Srmacklem * new open off it. 2802191783Srmacklem */ 2803191783Srmacklem new_open->ls_stateid.seqid = 0; 2804191783Srmacklem new_open->ls_stateid.other[0] = clp->lc_clientid.lval[0]; 2805191783Srmacklem new_open->ls_stateid.other[1] = clp->lc_clientid.lval[1]; 2806191783Srmacklem new_open->ls_stateid.other[2] = nfsrv_nextstateindex(clp); 2807191783Srmacklem new_open->ls_flags = (new_stp->ls_flags & NFSLCK_SHAREBITS) | 2808191783Srmacklem NFSLCK_OPEN; 2809191783Srmacklem new_open->ls_uid = new_stp->ls_uid; 2810191783Srmacklem LIST_INIT(&new_open->ls_open); 2811191783Srmacklem new_open->ls_openowner = new_stp; 2812191783Srmacklem new_open->ls_lfp = lfp; 2813191783Srmacklem new_open->ls_clp = clp; 2814191783Srmacklem LIST_INSERT_HEAD(&lfp->lf_open, new_open, ls_file); 2815191783Srmacklem if (new_stp->ls_flags & NFSLCK_RECLAIM) { 2816191783Srmacklem new_stp->ls_flags = 0; 2817191783Srmacklem } else { 2818191783Srmacklem *rflagsp |= NFSV4OPEN_RESULTCONFIRM; 2819191783Srmacklem new_stp->ls_flags = NFSLCK_NEEDSCONFIRM; 2820191783Srmacklem } 2821191783Srmacklem nfsrvd_refcache(new_stp->ls_op); 2822191783Srmacklem new_stp->ls_noopens = 0; 2823191783Srmacklem LIST_INIT(&new_stp->ls_open); 2824191783Srmacklem LIST_INSERT_HEAD(&new_stp->ls_open, new_open, ls_list); 2825191783Srmacklem LIST_INSERT_HEAD(&clp->lc_open, new_stp, ls_list); 2826191783Srmacklem LIST_INSERT_HEAD(NFSSTATEHASH(clp, new_open->ls_stateid), 2827191783Srmacklem new_open, ls_hash); 2828191783Srmacklem openstp = new_open; 2829191783Srmacklem new_open = NULL; 2830191783Srmacklem *new_stpp = NULL; 2831191783Srmacklem newnfsstats.srvopens++; 2832191783Srmacklem nfsrv_openpluslock++; 2833191783Srmacklem newnfsstats.srvopenowners++; 2834191783Srmacklem nfsrv_openpluslock++; 2835191783Srmacklem } 2836191783Srmacklem if (!error) { 2837191783Srmacklem stateidp->seqid = openstp->ls_stateid.seqid; 2838191783Srmacklem stateidp->other[0] = openstp->ls_stateid.other[0]; 2839191783Srmacklem stateidp->other[1] = openstp->ls_stateid.other[1]; 2840191783Srmacklem stateidp->other[2] = openstp->ls_stateid.other[2]; 2841191783Srmacklem } 2842191783Srmacklem NFSUNLOCKSTATE(); 2843191783Srmacklem if (haslock) { 2844191783Srmacklem NFSLOCKV4ROOTMUTEX(); 2845191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 2846191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 2847191783Srmacklem } 2848191783Srmacklem if (new_open) 2849191783Srmacklem FREE((caddr_t)new_open, M_NFSDSTATE); 2850191783Srmacklem if (new_deleg) 2851191783Srmacklem FREE((caddr_t)new_deleg, M_NFSDSTATE); 2852224086Szack 2853224086Szackout: 2854224086Szack NFSEXITCODE2(error, nd); 2855191783Srmacklem return (error); 2856191783Srmacklem} 2857191783Srmacklem 2858191783Srmacklem/* 2859191783Srmacklem * Open update. Does the confirm, downgrade and close. 2860191783Srmacklem */ 2861191783SrmacklemAPPLESTATIC int 2862191783Srmacklemnfsrv_openupdate(vnode_t vp, struct nfsstate *new_stp, nfsquad_t clientid, 2863191783Srmacklem nfsv4stateid_t *stateidp, struct nfsrv_descript *nd, NFSPROC_T *p) 2864191783Srmacklem{ 2865191783Srmacklem struct nfsstate *stp, *ownerstp; 2866191783Srmacklem struct nfsclient *clp; 2867191783Srmacklem struct nfslockfile *lfp; 2868191783Srmacklem u_int32_t bits; 2869224086Szack int error = 0, gotstate = 0, len = 0; 2870191783Srmacklem u_char client[NFSV4_OPAQUELIMIT]; 2871191783Srmacklem 2872191783Srmacklem /* 2873191783Srmacklem * Check for restart conditions (client and server). 2874191783Srmacklem */ 2875191783Srmacklem error = nfsrv_checkrestart(clientid, new_stp->ls_flags, 2876191783Srmacklem &new_stp->ls_stateid, 0); 2877191783Srmacklem if (error) 2878224086Szack goto out; 2879191783Srmacklem 2880191783Srmacklem NFSLOCKSTATE(); 2881191783Srmacklem /* 2882191783Srmacklem * Get the open structure via clientid and stateid. 2883191783Srmacklem */ 2884191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 2885191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 2886191783Srmacklem if (!error) 2887191783Srmacklem error = nfsrv_getstate(clp, &new_stp->ls_stateid, 2888191783Srmacklem new_stp->ls_flags, &stp); 2889191783Srmacklem 2890191783Srmacklem /* 2891191783Srmacklem * Sanity check the open. 2892191783Srmacklem */ 2893191783Srmacklem if (!error && (!(stp->ls_flags & NFSLCK_OPEN) || 2894191783Srmacklem (!(new_stp->ls_flags & NFSLCK_CONFIRM) && 2895191783Srmacklem (stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM)) || 2896191783Srmacklem ((new_stp->ls_flags & NFSLCK_CONFIRM) && 2897191783Srmacklem (!(stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM))))) 2898191783Srmacklem error = NFSERR_BADSTATEID; 2899191783Srmacklem 2900191783Srmacklem if (!error) 2901191783Srmacklem error = nfsrv_checkseqid(nd, new_stp->ls_seq, 2902191783Srmacklem stp->ls_openowner, new_stp->ls_op); 2903191783Srmacklem if (!error && stp->ls_stateid.seqid != new_stp->ls_stateid.seqid && 2904191783Srmacklem !(new_stp->ls_flags & NFSLCK_CONFIRM)) 2905191783Srmacklem error = NFSERR_OLDSTATEID; 2906191783Srmacklem if (!error && vnode_vtype(vp) != VREG) { 2907191783Srmacklem if (vnode_vtype(vp) == VDIR) 2908191783Srmacklem error = NFSERR_ISDIR; 2909191783Srmacklem else 2910191783Srmacklem error = NFSERR_INVAL; 2911191783Srmacklem } 2912191783Srmacklem 2913191783Srmacklem if (error) { 2914191783Srmacklem /* 2915191783Srmacklem * If a client tries to confirm an Open with a bad 2916191783Srmacklem * seqid# and there are no byte range locks or other Opens 2917191783Srmacklem * on the openowner, just throw it away, so the next use of the 2918191783Srmacklem * openowner will start a fresh seq#. 2919191783Srmacklem */ 2920191783Srmacklem if (error == NFSERR_BADSEQID && 2921191783Srmacklem (new_stp->ls_flags & NFSLCK_CONFIRM) && 2922191783Srmacklem nfsrv_nootherstate(stp)) 2923191783Srmacklem nfsrv_freeopenowner(stp->ls_openowner, 0, p); 2924191783Srmacklem NFSUNLOCKSTATE(); 2925224086Szack goto out; 2926191783Srmacklem } 2927191783Srmacklem 2928191783Srmacklem /* 2929191783Srmacklem * Set the return stateid. 2930191783Srmacklem */ 2931191783Srmacklem stateidp->seqid = stp->ls_stateid.seqid + 1; 2932191783Srmacklem stateidp->other[0] = stp->ls_stateid.other[0]; 2933191783Srmacklem stateidp->other[1] = stp->ls_stateid.other[1]; 2934191783Srmacklem stateidp->other[2] = stp->ls_stateid.other[2]; 2935191783Srmacklem /* 2936191783Srmacklem * Now, handle the three cases. 2937191783Srmacklem */ 2938191783Srmacklem if (new_stp->ls_flags & NFSLCK_CONFIRM) { 2939191783Srmacklem /* 2940191783Srmacklem * If the open doesn't need confirmation, it seems to me that 2941191783Srmacklem * there is a client error, but I'll just log it and keep going? 2942191783Srmacklem */ 2943191783Srmacklem if (!(stp->ls_openowner->ls_flags & NFSLCK_NEEDSCONFIRM)) 2944191783Srmacklem printf("Nfsv4d: stray open confirm\n"); 2945191783Srmacklem stp->ls_openowner->ls_flags = 0; 2946191783Srmacklem stp->ls_stateid.seqid++; 2947191783Srmacklem if (!(clp->lc_flags & LCL_STAMPEDSTABLE)) { 2948191783Srmacklem clp->lc_flags |= LCL_STAMPEDSTABLE; 2949191783Srmacklem len = clp->lc_idlen; 2950191783Srmacklem NFSBCOPY(clp->lc_id, client, len); 2951191783Srmacklem gotstate = 1; 2952191783Srmacklem } 2953191783Srmacklem NFSUNLOCKSTATE(); 2954191783Srmacklem } else if (new_stp->ls_flags & NFSLCK_CLOSE) { 2955191783Srmacklem ownerstp = stp->ls_openowner; 2956191783Srmacklem lfp = stp->ls_lfp; 2957205941Srmacklem if (nfsrv_dolocallocks != 0 && !LIST_EMPTY(&stp->ls_open)) { 2958205941Srmacklem /* Get the lf lock */ 2959205941Srmacklem nfsrv_locklf(lfp); 2960205941Srmacklem NFSUNLOCKSTATE(); 2961205941Srmacklem if (nfsrv_freeopen(stp, vp, 1, p) == 0) { 2962205941Srmacklem NFSLOCKSTATE(); 2963205941Srmacklem nfsrv_unlocklf(lfp); 2964205941Srmacklem NFSUNLOCKSTATE(); 2965205941Srmacklem } 2966191783Srmacklem } else { 2967205941Srmacklem (void) nfsrv_freeopen(stp, NULL, 0, p); 2968205941Srmacklem NFSUNLOCKSTATE(); 2969191783Srmacklem } 2970191783Srmacklem } else { 2971191783Srmacklem /* 2972191783Srmacklem * Update the share bits, making sure that the new set are a 2973191783Srmacklem * subset of the old ones. 2974191783Srmacklem */ 2975191783Srmacklem bits = (new_stp->ls_flags & NFSLCK_SHAREBITS); 2976191783Srmacklem if (~(stp->ls_flags) & bits) { 2977191783Srmacklem NFSUNLOCKSTATE(); 2978224086Szack error = NFSERR_INVAL; 2979224086Szack goto out; 2980191783Srmacklem } 2981191783Srmacklem stp->ls_flags = (bits | NFSLCK_OPEN); 2982191783Srmacklem stp->ls_stateid.seqid++; 2983191783Srmacklem NFSUNLOCKSTATE(); 2984191783Srmacklem } 2985191783Srmacklem 2986191783Srmacklem /* 2987191783Srmacklem * If the client just confirmed its first open, write a timestamp 2988191783Srmacklem * to the stable storage file. 2989191783Srmacklem */ 2990217432Srmacklem if (gotstate != 0) { 2991191783Srmacklem nfsrv_writestable(client, len, NFSNST_NEWSTATE, p); 2992217432Srmacklem nfsrv_backupstable(); 2993217432Srmacklem } 2994224086Szack 2995224086Szackout: 2996224086Szack NFSEXITCODE2(error, nd); 2997191783Srmacklem return (error); 2998191783Srmacklem} 2999191783Srmacklem 3000191783Srmacklem/* 3001191783Srmacklem * Delegation update. Does the purge and return. 3002191783Srmacklem */ 3003191783SrmacklemAPPLESTATIC int 3004191783Srmacklemnfsrv_delegupdate(nfsquad_t clientid, nfsv4stateid_t *stateidp, 3005191783Srmacklem vnode_t vp, int op, struct ucred *cred, NFSPROC_T *p) 3006191783Srmacklem{ 3007191783Srmacklem struct nfsstate *stp; 3008191783Srmacklem struct nfsclient *clp; 3009224086Szack int error = 0; 3010191783Srmacklem fhandle_t fh; 3011191783Srmacklem 3012191783Srmacklem /* 3013191783Srmacklem * Do a sanity check against the file handle for DelegReturn. 3014191783Srmacklem */ 3015191783Srmacklem if (vp) { 3016191783Srmacklem error = nfsvno_getfh(vp, &fh, p); 3017191783Srmacklem if (error) 3018224086Szack goto out; 3019191783Srmacklem } 3020191783Srmacklem /* 3021191783Srmacklem * Check for restart conditions (client and server). 3022191783Srmacklem */ 3023191783Srmacklem if (op == NFSV4OP_DELEGRETURN) 3024191783Srmacklem error = nfsrv_checkrestart(clientid, NFSLCK_DELEGRETURN, 3025191783Srmacklem stateidp, 0); 3026191783Srmacklem else 3027191783Srmacklem error = nfsrv_checkrestart(clientid, NFSLCK_DELEGPURGE, 3028191783Srmacklem stateidp, 0); 3029191783Srmacklem 3030191783Srmacklem NFSLOCKSTATE(); 3031191783Srmacklem /* 3032191783Srmacklem * Get the open structure via clientid and stateid. 3033191783Srmacklem */ 3034191783Srmacklem if (!error) 3035191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 3036191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 3037191783Srmacklem if (error) { 3038191783Srmacklem if (error == NFSERR_CBPATHDOWN) 3039191783Srmacklem error = 0; 3040191783Srmacklem if (error == NFSERR_STALECLIENTID && op == NFSV4OP_DELEGRETURN) 3041191783Srmacklem error = NFSERR_STALESTATEID; 3042191783Srmacklem } 3043191783Srmacklem if (!error && op == NFSV4OP_DELEGRETURN) { 3044191783Srmacklem error = nfsrv_getstate(clp, stateidp, NFSLCK_DELEGRETURN, &stp); 3045191783Srmacklem if (!error && stp->ls_stateid.seqid != stateidp->seqid) 3046191783Srmacklem error = NFSERR_OLDSTATEID; 3047191783Srmacklem } 3048191783Srmacklem /* 3049191783Srmacklem * NFSERR_EXPIRED means that the state has gone away, 3050191783Srmacklem * so Delegations have been purged. Just return ok. 3051191783Srmacklem */ 3052191783Srmacklem if (error == NFSERR_EXPIRED && op == NFSV4OP_DELEGPURGE) { 3053191783Srmacklem NFSUNLOCKSTATE(); 3054224086Szack error = 0; 3055224086Szack goto out; 3056191783Srmacklem } 3057191783Srmacklem if (error) { 3058191783Srmacklem NFSUNLOCKSTATE(); 3059224086Szack goto out; 3060191783Srmacklem } 3061191783Srmacklem 3062191783Srmacklem if (op == NFSV4OP_DELEGRETURN) { 3063191783Srmacklem if (NFSBCMP((caddr_t)&fh, (caddr_t)&stp->ls_lfp->lf_fh, 3064191783Srmacklem sizeof (fhandle_t))) { 3065191783Srmacklem NFSUNLOCKSTATE(); 3066224086Szack error = NFSERR_BADSTATEID; 3067224086Szack goto out; 3068191783Srmacklem } 3069191783Srmacklem nfsrv_freedeleg(stp); 3070191783Srmacklem } else { 3071191783Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 3072191783Srmacklem } 3073191783Srmacklem NFSUNLOCKSTATE(); 3074224086Szack error = 0; 3075224086Szack 3076224086Szackout: 3077224086Szack NFSEXITCODE(error); 3078224086Szack return (error); 3079191783Srmacklem} 3080191783Srmacklem 3081191783Srmacklem/* 3082191783Srmacklem * Release lock owner. 3083191783Srmacklem */ 3084191783SrmacklemAPPLESTATIC int 3085191783Srmacklemnfsrv_releaselckown(struct nfsstate *new_stp, nfsquad_t clientid, 3086191783Srmacklem NFSPROC_T *p) 3087191783Srmacklem{ 3088191783Srmacklem struct nfsstate *stp, *nstp, *openstp, *ownstp; 3089191783Srmacklem struct nfsclient *clp; 3090224086Szack int error = 0; 3091191783Srmacklem 3092191783Srmacklem /* 3093191783Srmacklem * Check for restart conditions (client and server). 3094191783Srmacklem */ 3095191783Srmacklem error = nfsrv_checkrestart(clientid, new_stp->ls_flags, 3096191783Srmacklem &new_stp->ls_stateid, 0); 3097191783Srmacklem if (error) 3098224086Szack goto out; 3099191783Srmacklem 3100191783Srmacklem NFSLOCKSTATE(); 3101191783Srmacklem /* 3102191783Srmacklem * Get the lock owner by name. 3103191783Srmacklem */ 3104191783Srmacklem error = nfsrv_getclient(clientid, CLOPS_RENEW, &clp, 3105191783Srmacklem (nfsquad_t)((u_quad_t)0), NULL, p); 3106191783Srmacklem if (error) { 3107191783Srmacklem NFSUNLOCKSTATE(); 3108224086Szack goto out; 3109191783Srmacklem } 3110191783Srmacklem LIST_FOREACH(ownstp, &clp->lc_open, ls_list) { 3111191783Srmacklem LIST_FOREACH(openstp, &ownstp->ls_open, ls_list) { 3112191783Srmacklem stp = LIST_FIRST(&openstp->ls_open); 3113191783Srmacklem while (stp != LIST_END(&openstp->ls_open)) { 3114191783Srmacklem nstp = LIST_NEXT(stp, ls_list); 3115191783Srmacklem /* 3116191783Srmacklem * If the owner matches, check for locks and 3117191783Srmacklem * then free or return an error. 3118191783Srmacklem */ 3119191783Srmacklem if (stp->ls_ownerlen == new_stp->ls_ownerlen && 3120191783Srmacklem !NFSBCMP(stp->ls_owner, new_stp->ls_owner, 3121191783Srmacklem stp->ls_ownerlen)){ 3122191783Srmacklem if (LIST_EMPTY(&stp->ls_lock)) { 3123205941Srmacklem nfsrv_freelockowner(stp, NULL, 0, p); 3124191783Srmacklem } else { 3125191783Srmacklem NFSUNLOCKSTATE(); 3126224086Szack error = NFSERR_LOCKSHELD; 3127224086Szack goto out; 3128191783Srmacklem } 3129191783Srmacklem } 3130191783Srmacklem stp = nstp; 3131191783Srmacklem } 3132191783Srmacklem } 3133191783Srmacklem } 3134191783Srmacklem NFSUNLOCKSTATE(); 3135224086Szack 3136224086Szackout: 3137224086Szack NFSEXITCODE(error); 3138224086Szack return (error); 3139191783Srmacklem} 3140191783Srmacklem 3141191783Srmacklem/* 3142191783Srmacklem * Get the file handle for a lock structure. 3143191783Srmacklem */ 3144191783Srmacklemstatic int 3145191783Srmacklemnfsrv_getlockfh(vnode_t vp, u_short flags, 3146191783Srmacklem struct nfslockfile **new_lfpp, fhandle_t *nfhp, NFSPROC_T *p) 3147191783Srmacklem{ 3148191783Srmacklem fhandle_t *fhp = NULL; 3149191783Srmacklem struct nfslockfile *new_lfp; 3150191783Srmacklem int error; 3151191783Srmacklem 3152191783Srmacklem /* 3153191783Srmacklem * For lock, use the new nfslock structure, otherwise just 3154191783Srmacklem * a fhandle_t on the stack. 3155191783Srmacklem */ 3156191783Srmacklem if (flags & NFSLCK_OPEN) { 3157191783Srmacklem new_lfp = *new_lfpp; 3158191783Srmacklem fhp = &new_lfp->lf_fh; 3159191783Srmacklem } else if (nfhp) { 3160191783Srmacklem fhp = nfhp; 3161191783Srmacklem } else { 3162191783Srmacklem panic("nfsrv_getlockfh"); 3163191783Srmacklem } 3164191783Srmacklem error = nfsvno_getfh(vp, fhp, p); 3165224086Szack NFSEXITCODE(error); 3166191783Srmacklem return (error); 3167191783Srmacklem} 3168191783Srmacklem 3169191783Srmacklem/* 3170191783Srmacklem * Get an nfs lock structure. Allocate one, as required, and return a 3171191783Srmacklem * pointer to it. 3172191783Srmacklem * Returns an NFSERR_xxx upon failure or -1 to indicate no current lock. 3173191783Srmacklem */ 3174191783Srmacklemstatic int 3175191783Srmacklemnfsrv_getlockfile(u_short flags, struct nfslockfile **new_lfpp, 3176205941Srmacklem struct nfslockfile **lfpp, fhandle_t *nfhp, int lockit) 3177191783Srmacklem{ 3178191783Srmacklem struct nfslockfile *lfp; 3179191783Srmacklem fhandle_t *fhp = NULL, *tfhp; 3180191783Srmacklem struct nfslockhashhead *hp; 3181191783Srmacklem struct nfslockfile *new_lfp = NULL; 3182191783Srmacklem 3183191783Srmacklem /* 3184191783Srmacklem * For lock, use the new nfslock structure, otherwise just 3185191783Srmacklem * a fhandle_t on the stack. 3186191783Srmacklem */ 3187191783Srmacklem if (flags & NFSLCK_OPEN) { 3188191783Srmacklem new_lfp = *new_lfpp; 3189191783Srmacklem fhp = &new_lfp->lf_fh; 3190191783Srmacklem } else if (nfhp) { 3191191783Srmacklem fhp = nfhp; 3192191783Srmacklem } else { 3193191783Srmacklem panic("nfsrv_getlockfile"); 3194191783Srmacklem } 3195191783Srmacklem 3196191783Srmacklem hp = NFSLOCKHASH(fhp); 3197191783Srmacklem LIST_FOREACH(lfp, hp, lf_hash) { 3198191783Srmacklem tfhp = &lfp->lf_fh; 3199191783Srmacklem if (NFSVNO_CMPFH(fhp, tfhp)) { 3200205941Srmacklem if (lockit) 3201205941Srmacklem nfsrv_locklf(lfp); 3202191783Srmacklem *lfpp = lfp; 3203191783Srmacklem return (0); 3204191783Srmacklem } 3205191783Srmacklem } 3206191783Srmacklem if (!(flags & NFSLCK_OPEN)) 3207191783Srmacklem return (-1); 3208191783Srmacklem 3209191783Srmacklem /* 3210191783Srmacklem * No match, so chain the new one into the list. 3211191783Srmacklem */ 3212191783Srmacklem LIST_INIT(&new_lfp->lf_open); 3213191783Srmacklem LIST_INIT(&new_lfp->lf_lock); 3214191783Srmacklem LIST_INIT(&new_lfp->lf_deleg); 3215205941Srmacklem LIST_INIT(&new_lfp->lf_locallock); 3216205941Srmacklem LIST_INIT(&new_lfp->lf_rollback); 3217205941Srmacklem new_lfp->lf_locallock_lck.nfslock_usecnt = 0; 3218205941Srmacklem new_lfp->lf_locallock_lck.nfslock_lock = 0; 3219205941Srmacklem new_lfp->lf_usecount = 0; 3220191783Srmacklem LIST_INSERT_HEAD(hp, new_lfp, lf_hash); 3221191783Srmacklem *lfpp = new_lfp; 3222191783Srmacklem *new_lfpp = NULL; 3223191783Srmacklem return (0); 3224191783Srmacklem} 3225191783Srmacklem 3226191783Srmacklem/* 3227191783Srmacklem * This function adds a nfslock lock structure to the list for the associated 3228191783Srmacklem * nfsstate and nfslockfile structures. It will be inserted after the 3229191783Srmacklem * entry pointed at by insert_lop. 3230191783Srmacklem */ 3231191783Srmacklemstatic void 3232191783Srmacklemnfsrv_insertlock(struct nfslock *new_lop, struct nfslock *insert_lop, 3233191783Srmacklem struct nfsstate *stp, struct nfslockfile *lfp) 3234191783Srmacklem{ 3235191783Srmacklem struct nfslock *lop, *nlop; 3236191783Srmacklem 3237191783Srmacklem new_lop->lo_stp = stp; 3238191783Srmacklem new_lop->lo_lfp = lfp; 3239191783Srmacklem 3240205941Srmacklem if (stp != NULL) { 3241205941Srmacklem /* Insert in increasing lo_first order */ 3242205941Srmacklem lop = LIST_FIRST(&lfp->lf_lock); 3243205941Srmacklem if (lop == LIST_END(&lfp->lf_lock) || 3244205941Srmacklem new_lop->lo_first <= lop->lo_first) { 3245205941Srmacklem LIST_INSERT_HEAD(&lfp->lf_lock, new_lop, lo_lckfile); 3246205941Srmacklem } else { 3247191783Srmacklem nlop = LIST_NEXT(lop, lo_lckfile); 3248205941Srmacklem while (nlop != LIST_END(&lfp->lf_lock) && 3249205941Srmacklem nlop->lo_first < new_lop->lo_first) { 3250205941Srmacklem lop = nlop; 3251205941Srmacklem nlop = LIST_NEXT(lop, lo_lckfile); 3252205941Srmacklem } 3253205941Srmacklem LIST_INSERT_AFTER(lop, new_lop, lo_lckfile); 3254191783Srmacklem } 3255205941Srmacklem } else { 3256205941Srmacklem new_lop->lo_lckfile.le_prev = NULL; /* list not used */ 3257191783Srmacklem } 3258191783Srmacklem 3259191783Srmacklem /* 3260205941Srmacklem * Insert after insert_lop, which is overloaded as stp or lfp for 3261191783Srmacklem * an empty list. 3262191783Srmacklem */ 3263205941Srmacklem if (stp == NULL && (struct nfslockfile *)insert_lop == lfp) 3264205941Srmacklem LIST_INSERT_HEAD(&lfp->lf_locallock, new_lop, lo_lckowner); 3265205941Srmacklem else if ((struct nfsstate *)insert_lop == stp) 3266191783Srmacklem LIST_INSERT_HEAD(&stp->ls_lock, new_lop, lo_lckowner); 3267191783Srmacklem else 3268191783Srmacklem LIST_INSERT_AFTER(insert_lop, new_lop, lo_lckowner); 3269205941Srmacklem if (stp != NULL) { 3270205941Srmacklem newnfsstats.srvlocks++; 3271205941Srmacklem nfsrv_openpluslock++; 3272205941Srmacklem } 3273191783Srmacklem} 3274191783Srmacklem 3275191783Srmacklem/* 3276191783Srmacklem * This function updates the locking for a lock owner and given file. It 3277191783Srmacklem * maintains a list of lock ranges ordered on increasing file offset that 3278191783Srmacklem * are NFSLCK_READ or NFSLCK_WRITE and non-overlapping (aka POSIX style). 3279191783Srmacklem * It always adds new_lop to the list and sometimes uses the one pointed 3280191783Srmacklem * at by other_lopp. 3281191783Srmacklem */ 3282191783Srmacklemstatic void 3283191783Srmacklemnfsrv_updatelock(struct nfsstate *stp, struct nfslock **new_lopp, 3284191783Srmacklem struct nfslock **other_lopp, struct nfslockfile *lfp) 3285191783Srmacklem{ 3286191783Srmacklem struct nfslock *new_lop = *new_lopp; 3287191783Srmacklem struct nfslock *lop, *tlop, *ilop; 3288191783Srmacklem struct nfslock *other_lop = *other_lopp; 3289191783Srmacklem int unlock = 0, myfile = 0; 3290191783Srmacklem u_int64_t tmp; 3291191783Srmacklem 3292191783Srmacklem /* 3293191783Srmacklem * Work down the list until the lock is merged. 3294191783Srmacklem */ 3295191783Srmacklem if (new_lop->lo_flags & NFSLCK_UNLOCK) 3296191783Srmacklem unlock = 1; 3297205941Srmacklem if (stp != NULL) { 3298205941Srmacklem ilop = (struct nfslock *)stp; 3299205941Srmacklem lop = LIST_FIRST(&stp->ls_lock); 3300205941Srmacklem } else { 3301205941Srmacklem ilop = (struct nfslock *)lfp; 3302205941Srmacklem lop = LIST_FIRST(&lfp->lf_locallock); 3303205941Srmacklem } 3304205941Srmacklem while (lop != NULL) { 3305191783Srmacklem /* 3306191783Srmacklem * Only check locks for this file that aren't before the start of 3307191783Srmacklem * new lock's range. 3308191783Srmacklem */ 3309191783Srmacklem if (lop->lo_lfp == lfp) { 3310191783Srmacklem myfile = 1; 3311191783Srmacklem if (lop->lo_end >= new_lop->lo_first) { 3312191783Srmacklem if (new_lop->lo_end < lop->lo_first) { 3313191783Srmacklem /* 3314191783Srmacklem * If the new lock ends before the start of the 3315191783Srmacklem * current lock's range, no merge, just insert 3316191783Srmacklem * the new lock. 3317191783Srmacklem */ 3318191783Srmacklem break; 3319191783Srmacklem } 3320191783Srmacklem if (new_lop->lo_flags == lop->lo_flags || 3321191783Srmacklem (new_lop->lo_first <= lop->lo_first && 3322191783Srmacklem new_lop->lo_end >= lop->lo_end)) { 3323191783Srmacklem /* 3324191783Srmacklem * This lock can be absorbed by the new lock/unlock. 3325191783Srmacklem * This happens when it covers the entire range 3326191783Srmacklem * of the old lock or is contiguous 3327191783Srmacklem * with the old lock and is of the same type or an 3328191783Srmacklem * unlock. 3329191783Srmacklem */ 3330191783Srmacklem if (lop->lo_first < new_lop->lo_first) 3331191783Srmacklem new_lop->lo_first = lop->lo_first; 3332191783Srmacklem if (lop->lo_end > new_lop->lo_end) 3333191783Srmacklem new_lop->lo_end = lop->lo_end; 3334191783Srmacklem tlop = lop; 3335191783Srmacklem lop = LIST_NEXT(lop, lo_lckowner); 3336191783Srmacklem nfsrv_freenfslock(tlop); 3337191783Srmacklem continue; 3338191783Srmacklem } 3339191783Srmacklem 3340191783Srmacklem /* 3341191783Srmacklem * All these cases are for contiguous locks that are not the 3342191783Srmacklem * same type, so they can't be merged. 3343191783Srmacklem */ 3344191783Srmacklem if (new_lop->lo_first <= lop->lo_first) { 3345191783Srmacklem /* 3346191783Srmacklem * This case is where the new lock overlaps with the 3347191783Srmacklem * first part of the old lock. Move the start of the 3348191783Srmacklem * old lock to just past the end of the new lock. The 3349191783Srmacklem * new lock will be inserted in front of the old, since 3350191783Srmacklem * ilop hasn't been updated. (We are done now.) 3351191783Srmacklem */ 3352191783Srmacklem lop->lo_first = new_lop->lo_end; 3353191783Srmacklem break; 3354191783Srmacklem } 3355191783Srmacklem if (new_lop->lo_end >= lop->lo_end) { 3356191783Srmacklem /* 3357191783Srmacklem * This case is where the new lock overlaps with the 3358191783Srmacklem * end of the old lock's range. Move the old lock's 3359191783Srmacklem * end to just before the new lock's first and insert 3360191783Srmacklem * the new lock after the old lock. 3361191783Srmacklem * Might not be done yet, since the new lock could 3362191783Srmacklem * overlap further locks with higher ranges. 3363191783Srmacklem */ 3364191783Srmacklem lop->lo_end = new_lop->lo_first; 3365191783Srmacklem ilop = lop; 3366191783Srmacklem lop = LIST_NEXT(lop, lo_lckowner); 3367191783Srmacklem continue; 3368191783Srmacklem } 3369191783Srmacklem /* 3370191783Srmacklem * The final case is where the new lock's range is in the 3371191783Srmacklem * middle of the current lock's and splits the current lock 3372191783Srmacklem * up. Use *other_lopp to handle the second part of the 3373191783Srmacklem * split old lock range. (We are done now.) 3374191783Srmacklem * For unlock, we use new_lop as other_lop and tmp, since 3375191783Srmacklem * other_lop and new_lop are the same for this case. 3376191783Srmacklem * We noted the unlock case above, so we don't need 3377191783Srmacklem * new_lop->lo_flags any longer. 3378191783Srmacklem */ 3379191783Srmacklem tmp = new_lop->lo_first; 3380191783Srmacklem if (other_lop == NULL) { 3381191783Srmacklem if (!unlock) 3382191783Srmacklem panic("nfsd srv update unlock"); 3383191783Srmacklem other_lop = new_lop; 3384191783Srmacklem *new_lopp = NULL; 3385191783Srmacklem } 3386191783Srmacklem other_lop->lo_first = new_lop->lo_end; 3387191783Srmacklem other_lop->lo_end = lop->lo_end; 3388191783Srmacklem other_lop->lo_flags = lop->lo_flags; 3389191783Srmacklem other_lop->lo_stp = stp; 3390191783Srmacklem other_lop->lo_lfp = lfp; 3391191783Srmacklem lop->lo_end = tmp; 3392191783Srmacklem nfsrv_insertlock(other_lop, lop, stp, lfp); 3393191783Srmacklem *other_lopp = NULL; 3394191783Srmacklem ilop = lop; 3395191783Srmacklem break; 3396191783Srmacklem } 3397191783Srmacklem } 3398191783Srmacklem ilop = lop; 3399191783Srmacklem lop = LIST_NEXT(lop, lo_lckowner); 3400205941Srmacklem if (myfile && (lop == NULL || lop->lo_lfp != lfp)) 3401191783Srmacklem break; 3402191783Srmacklem } 3403191783Srmacklem 3404191783Srmacklem /* 3405191783Srmacklem * Insert the new lock in the list at the appropriate place. 3406191783Srmacklem */ 3407191783Srmacklem if (!unlock) { 3408191783Srmacklem nfsrv_insertlock(new_lop, ilop, stp, lfp); 3409191783Srmacklem *new_lopp = NULL; 3410191783Srmacklem } 3411191783Srmacklem} 3412191783Srmacklem 3413191783Srmacklem/* 3414191783Srmacklem * This function handles sequencing of locks, etc. 3415191783Srmacklem * It returns an error that indicates what the caller should do. 3416191783Srmacklem */ 3417191783Srmacklemstatic int 3418191783Srmacklemnfsrv_checkseqid(struct nfsrv_descript *nd, u_int32_t seqid, 3419191783Srmacklem struct nfsstate *stp, struct nfsrvcache *op) 3420191783Srmacklem{ 3421224086Szack int error = 0; 3422191783Srmacklem 3423191783Srmacklem if (op != nd->nd_rp) 3424191783Srmacklem panic("nfsrvstate checkseqid"); 3425191783Srmacklem if (!(op->rc_flag & RC_INPROG)) 3426191783Srmacklem panic("nfsrvstate not inprog"); 3427191783Srmacklem if (stp->ls_op && stp->ls_op->rc_refcnt <= 0) { 3428191783Srmacklem printf("refcnt=%d\n", stp->ls_op->rc_refcnt); 3429191783Srmacklem panic("nfsrvstate op refcnt"); 3430191783Srmacklem } 3431191783Srmacklem if ((stp->ls_seq + 1) == seqid) { 3432191783Srmacklem if (stp->ls_op) 3433191783Srmacklem nfsrvd_derefcache(stp->ls_op); 3434191783Srmacklem stp->ls_op = op; 3435191783Srmacklem nfsrvd_refcache(op); 3436191783Srmacklem stp->ls_seq = seqid; 3437224086Szack goto out; 3438191783Srmacklem } else if (stp->ls_seq == seqid && stp->ls_op && 3439191783Srmacklem op->rc_xid == stp->ls_op->rc_xid && 3440191783Srmacklem op->rc_refcnt == 0 && 3441191783Srmacklem op->rc_reqlen == stp->ls_op->rc_reqlen && 3442191783Srmacklem op->rc_cksum == stp->ls_op->rc_cksum) { 3443224086Szack if (stp->ls_op->rc_flag & RC_INPROG) { 3444224086Szack error = NFSERR_DONTREPLY; 3445224086Szack goto out; 3446224086Szack } 3447191783Srmacklem nd->nd_rp = stp->ls_op; 3448191783Srmacklem nd->nd_rp->rc_flag |= RC_INPROG; 3449191783Srmacklem nfsrvd_delcache(op); 3450224086Szack error = NFSERR_REPLYFROMCACHE; 3451224086Szack goto out; 3452191783Srmacklem } 3453224086Szack error = NFSERR_BADSEQID; 3454224086Szack 3455224086Szackout: 3456224086Szack NFSEXITCODE2(error, nd); 3457224086Szack return (error); 3458191783Srmacklem} 3459191783Srmacklem 3460191783Srmacklem/* 3461191783Srmacklem * Get the client ip address for callbacks. If the strings can't be parsed, 3462191783Srmacklem * just set lc_program to 0 to indicate no callbacks are possible. 3463191783Srmacklem * (For cases where the address can't be parsed or is 0.0.0.0.0.0, set 3464191783Srmacklem * the address to the client's transport address. This won't be used 3465191783Srmacklem * for callbacks, but can be printed out by newnfsstats for info.) 3466191783Srmacklem * Return error if the xdr can't be parsed, 0 otherwise. 3467191783Srmacklem */ 3468191783SrmacklemAPPLESTATIC int 3469191783Srmacklemnfsrv_getclientipaddr(struct nfsrv_descript *nd, struct nfsclient *clp) 3470191783Srmacklem{ 3471191783Srmacklem u_int32_t *tl; 3472191783Srmacklem u_char *cp, *cp2; 3473191783Srmacklem int i, j; 3474191783Srmacklem struct sockaddr_in *rad, *sad; 3475191783Srmacklem u_char protocol[5], addr[24]; 3476191783Srmacklem int error = 0, cantparse = 0; 3477191783Srmacklem union { 3478191783Srmacklem u_long ival; 3479191783Srmacklem u_char cval[4]; 3480191783Srmacklem } ip; 3481191783Srmacklem union { 3482191783Srmacklem u_short sval; 3483191783Srmacklem u_char cval[2]; 3484191783Srmacklem } port; 3485191783Srmacklem 3486191783Srmacklem rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *); 3487191783Srmacklem rad->sin_family = AF_INET; 3488191783Srmacklem rad->sin_len = sizeof (struct sockaddr_in); 3489191783Srmacklem rad->sin_addr.s_addr = 0; 3490191783Srmacklem rad->sin_port = 0; 3491191783Srmacklem clp->lc_req.nr_client = NULL; 3492191783Srmacklem clp->lc_req.nr_lock = 0; 3493191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3494191783Srmacklem i = fxdr_unsigned(int, *tl); 3495191783Srmacklem if (i >= 3 && i <= 4) { 3496191783Srmacklem error = nfsrv_mtostr(nd, protocol, i); 3497191783Srmacklem if (error) 3498191783Srmacklem goto nfsmout; 3499191783Srmacklem if (!strcmp(protocol, "tcp")) { 3500191783Srmacklem clp->lc_flags |= LCL_TCPCALLBACK; 3501191783Srmacklem clp->lc_req.nr_sotype = SOCK_STREAM; 3502191783Srmacklem clp->lc_req.nr_soproto = IPPROTO_TCP; 3503191783Srmacklem } else if (!strcmp(protocol, "udp")) { 3504191783Srmacklem clp->lc_req.nr_sotype = SOCK_DGRAM; 3505191783Srmacklem clp->lc_req.nr_soproto = IPPROTO_UDP; 3506191783Srmacklem } else { 3507191783Srmacklem cantparse = 1; 3508191783Srmacklem } 3509191783Srmacklem } else { 3510191783Srmacklem cantparse = 1; 3511191783Srmacklem if (i > 0) { 3512191783Srmacklem error = nfsm_advance(nd, NFSM_RNDUP(i), -1); 3513191783Srmacklem if (error) 3514191783Srmacklem goto nfsmout; 3515191783Srmacklem } 3516191783Srmacklem } 3517191783Srmacklem NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3518191783Srmacklem i = fxdr_unsigned(int, *tl); 3519191783Srmacklem if (i < 0) { 3520191783Srmacklem error = NFSERR_BADXDR; 3521191783Srmacklem goto nfsmout; 3522191783Srmacklem } else if (i == 0) { 3523191783Srmacklem cantparse = 1; 3524191783Srmacklem } else if (!cantparse && i <= 23 && i >= 11) { 3525191783Srmacklem error = nfsrv_mtostr(nd, addr, i); 3526191783Srmacklem if (error) 3527191783Srmacklem goto nfsmout; 3528191783Srmacklem 3529191783Srmacklem /* 3530191783Srmacklem * Parse out the address fields. We expect 6 decimal numbers 3531191783Srmacklem * separated by '.'s. 3532191783Srmacklem */ 3533191783Srmacklem cp = addr; 3534191783Srmacklem i = 0; 3535191783Srmacklem while (*cp && i < 6) { 3536191783Srmacklem cp2 = cp; 3537191783Srmacklem while (*cp2 && *cp2 != '.') 3538191783Srmacklem cp2++; 3539191783Srmacklem if (*cp2) 3540191783Srmacklem *cp2++ = '\0'; 3541191783Srmacklem else if (i != 5) { 3542191783Srmacklem cantparse = 1; 3543191783Srmacklem break; 3544191783Srmacklem } 3545191783Srmacklem j = nfsrv_getipnumber(cp); 3546191783Srmacklem if (j >= 0) { 3547191783Srmacklem if (i < 4) 3548191783Srmacklem ip.cval[3 - i] = j; 3549191783Srmacklem else 3550191783Srmacklem port.cval[5 - i] = j; 3551191783Srmacklem } else { 3552191783Srmacklem cantparse = 1; 3553191783Srmacklem break; 3554191783Srmacklem } 3555191783Srmacklem cp = cp2; 3556191783Srmacklem i++; 3557191783Srmacklem } 3558191783Srmacklem if (!cantparse) { 3559191783Srmacklem if (ip.ival != 0x0) { 3560191783Srmacklem rad->sin_addr.s_addr = htonl(ip.ival); 3561191783Srmacklem rad->sin_port = htons(port.sval); 3562191783Srmacklem } else { 3563191783Srmacklem cantparse = 1; 3564191783Srmacklem } 3565191783Srmacklem } 3566191783Srmacklem } else { 3567191783Srmacklem cantparse = 1; 3568191783Srmacklem if (i > 0) { 3569191783Srmacklem error = nfsm_advance(nd, NFSM_RNDUP(i), -1); 3570191783Srmacklem if (error) 3571191783Srmacklem goto nfsmout; 3572191783Srmacklem } 3573191783Srmacklem } 3574191783Srmacklem if (cantparse) { 3575191783Srmacklem sad = NFSSOCKADDR(nd->nd_nam, struct sockaddr_in *); 3576191783Srmacklem rad->sin_addr.s_addr = sad->sin_addr.s_addr; 3577191783Srmacklem rad->sin_port = 0x0; 3578191783Srmacklem clp->lc_program = 0; 3579191783Srmacklem } 3580191783Srmacklemnfsmout: 3581224086Szack NFSEXITCODE2(error, nd); 3582191783Srmacklem return (error); 3583191783Srmacklem} 3584191783Srmacklem 3585191783Srmacklem/* 3586191783Srmacklem * Turn a string of up to three decimal digits into a number. Return -1 upon 3587191783Srmacklem * error. 3588191783Srmacklem */ 3589191783Srmacklemstatic int 3590191783Srmacklemnfsrv_getipnumber(u_char *cp) 3591191783Srmacklem{ 3592191783Srmacklem int i = 0, j = 0; 3593191783Srmacklem 3594191783Srmacklem while (*cp) { 3595191783Srmacklem if (j > 2 || *cp < '0' || *cp > '9') 3596191783Srmacklem return (-1); 3597191783Srmacklem i *= 10; 3598191783Srmacklem i += (*cp - '0'); 3599191783Srmacklem cp++; 3600191783Srmacklem j++; 3601191783Srmacklem } 3602191783Srmacklem if (i < 256) 3603191783Srmacklem return (i); 3604191783Srmacklem return (-1); 3605191783Srmacklem} 3606191783Srmacklem 3607191783Srmacklem/* 3608191783Srmacklem * This function checks for restart conditions. 3609191783Srmacklem */ 3610191783Srmacklemstatic int 3611191783Srmacklemnfsrv_checkrestart(nfsquad_t clientid, u_int32_t flags, 3612191783Srmacklem nfsv4stateid_t *stateidp, int specialid) 3613191783Srmacklem{ 3614224086Szack int ret = 0; 3615191783Srmacklem 3616191783Srmacklem /* 3617191783Srmacklem * First check for a server restart. Open, LockT, ReleaseLockOwner 3618191783Srmacklem * and DelegPurge have a clientid, the rest a stateid. 3619191783Srmacklem */ 3620191783Srmacklem if (flags & 3621191783Srmacklem (NFSLCK_OPEN | NFSLCK_TEST | NFSLCK_RELEASE | NFSLCK_DELEGPURGE)) { 3622224086Szack if (clientid.lval[0] != nfsrvboottime) { 3623224086Szack ret = NFSERR_STALECLIENTID; 3624224086Szack goto out; 3625224086Szack } 3626191783Srmacklem } else if (stateidp->other[0] != nfsrvboottime && 3627224086Szack specialid == 0) { 3628224086Szack ret = NFSERR_STALESTATEID; 3629224086Szack goto out; 3630224086Szack } 3631191783Srmacklem 3632191783Srmacklem /* 3633191783Srmacklem * Read, Write, Setattr and LockT can return NFSERR_GRACE and do 3634191783Srmacklem * not use a lock/open owner seqid#, so the check can be done now. 3635191783Srmacklem * (The others will be checked, as required, later.) 3636191783Srmacklem */ 3637191783Srmacklem if (!(flags & (NFSLCK_CHECK | NFSLCK_TEST))) 3638224086Szack goto out; 3639191783Srmacklem 3640191783Srmacklem NFSLOCKSTATE(); 3641191783Srmacklem ret = nfsrv_checkgrace(flags); 3642191783Srmacklem NFSUNLOCKSTATE(); 3643224086Szack 3644224086Szackout: 3645224086Szack NFSEXITCODE(ret); 3646191783Srmacklem return (ret); 3647191783Srmacklem} 3648191783Srmacklem 3649191783Srmacklem/* 3650191783Srmacklem * Check for grace. 3651191783Srmacklem */ 3652191783Srmacklemstatic int 3653191783Srmacklemnfsrv_checkgrace(u_int32_t flags) 3654191783Srmacklem{ 3655224086Szack int error = 0; 3656191783Srmacklem 3657191783Srmacklem if (nfsrv_stablefirst.nsf_flags & NFSNSF_GRACEOVER) { 3658224086Szack if (flags & NFSLCK_RECLAIM) { 3659224086Szack error = NFSERR_NOGRACE; 3660224086Szack goto out; 3661224086Szack } 3662191783Srmacklem } else { 3663224086Szack if (!(flags & NFSLCK_RECLAIM)) { 3664224086Szack error = NFSERR_GRACE; 3665224086Szack goto out; 3666224086Szack } 3667191783Srmacklem 3668191783Srmacklem /* 3669191783Srmacklem * If grace is almost over and we are still getting Reclaims, 3670191783Srmacklem * extend grace a bit. 3671191783Srmacklem */ 3672191783Srmacklem if ((NFSD_MONOSEC + NFSRV_LEASEDELTA) > 3673191783Srmacklem nfsrv_stablefirst.nsf_eograce) 3674191783Srmacklem nfsrv_stablefirst.nsf_eograce = NFSD_MONOSEC + 3675191783Srmacklem NFSRV_LEASEDELTA; 3676191783Srmacklem } 3677224086Szack 3678224086Szackout: 3679224086Szack NFSEXITCODE(error); 3680224086Szack return (error); 3681191783Srmacklem} 3682191783Srmacklem 3683191783Srmacklem/* 3684191783Srmacklem * Do a server callback. 3685191783Srmacklem */ 3686191783Srmacklemstatic int 3687191783Srmacklemnfsrv_docallback(struct nfsclient *clp, int procnum, 3688191783Srmacklem nfsv4stateid_t *stateidp, int trunc, fhandle_t *fhp, 3689191783Srmacklem struct nfsvattr *nap, nfsattrbit_t *attrbitp, NFSPROC_T *p) 3690191783Srmacklem{ 3691191783Srmacklem mbuf_t m; 3692191783Srmacklem u_int32_t *tl; 3693191783Srmacklem struct nfsrv_descript nfsd, *nd = &nfsd; 3694191783Srmacklem struct ucred *cred; 3695191783Srmacklem int error = 0; 3696191783Srmacklem u_int32_t callback; 3697191783Srmacklem 3698191783Srmacklem cred = newnfs_getcred(); 3699191783Srmacklem NFSLOCKSTATE(); /* mostly for lc_cbref++ */ 3700191783Srmacklem if (clp->lc_flags & LCL_NEEDSCONFIRM) { 3701191783Srmacklem NFSUNLOCKSTATE(); 3702191783Srmacklem panic("docallb"); 3703191783Srmacklem } 3704191783Srmacklem clp->lc_cbref++; 3705192181Srmacklem 3706191783Srmacklem /* 3707192181Srmacklem * Fill the callback program# and version into the request 3708192181Srmacklem * structure for newnfs_connect() to use. 3709192181Srmacklem */ 3710192181Srmacklem clp->lc_req.nr_prog = clp->lc_program; 3711192181Srmacklem clp->lc_req.nr_vers = NFSV4_CBVERS; 3712192181Srmacklem 3713192181Srmacklem /* 3714191783Srmacklem * First, fill in some of the fields of nd and cr. 3715191783Srmacklem */ 3716191783Srmacklem nd->nd_flag = ND_NFSV4; 3717191783Srmacklem if (clp->lc_flags & LCL_GSS) 3718191783Srmacklem nd->nd_flag |= ND_KERBV; 3719191783Srmacklem nd->nd_repstat = 0; 3720191783Srmacklem cred->cr_uid = clp->lc_uid; 3721191783Srmacklem cred->cr_gid = clp->lc_gid; 3722191783Srmacklem callback = clp->lc_callback; 3723191783Srmacklem NFSUNLOCKSTATE(); 3724191783Srmacklem cred->cr_ngroups = 1; 3725191783Srmacklem 3726191783Srmacklem /* 3727191783Srmacklem * Get the first mbuf for the request. 3728191783Srmacklem */ 3729243882Sglebius MGET(m, M_WAITOK, MT_DATA); 3730191783Srmacklem mbuf_setlen(m, 0); 3731191783Srmacklem nd->nd_mreq = nd->nd_mb = m; 3732191783Srmacklem nd->nd_bpos = NFSMTOD(m, caddr_t); 3733191783Srmacklem 3734191783Srmacklem /* 3735191783Srmacklem * and build the callback request. 3736191783Srmacklem */ 3737191783Srmacklem if (procnum == NFSV4OP_CBGETATTR) { 3738191783Srmacklem nd->nd_procnum = NFSV4PROC_CBCOMPOUND; 3739191783Srmacklem (void) nfsm_strtom(nd, "CB Getattr", 10); 3740191783Srmacklem NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED); 3741191783Srmacklem *tl++ = txdr_unsigned(NFSV4_MINORVERSION); 3742191783Srmacklem *tl++ = txdr_unsigned(callback); 3743191783Srmacklem *tl++ = txdr_unsigned(1); 3744191783Srmacklem *tl = txdr_unsigned(NFSV4OP_CBGETATTR); 3745191783Srmacklem (void) nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); 3746191783Srmacklem (void) nfsrv_putattrbit(nd, attrbitp); 3747191783Srmacklem } else if (procnum == NFSV4OP_CBRECALL) { 3748191783Srmacklem nd->nd_procnum = NFSV4PROC_CBCOMPOUND; 3749191783Srmacklem (void) nfsm_strtom(nd, "CB Recall", 9); 3750191783Srmacklem NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID); 3751191783Srmacklem *tl++ = txdr_unsigned(NFSV4_MINORVERSION); 3752191783Srmacklem *tl++ = txdr_unsigned(callback); 3753191783Srmacklem *tl++ = txdr_unsigned(1); 3754191783Srmacklem *tl++ = txdr_unsigned(NFSV4OP_CBRECALL); 3755191783Srmacklem *tl++ = txdr_unsigned(stateidp->seqid); 3756191783Srmacklem NFSBCOPY((caddr_t)stateidp->other, (caddr_t)tl, 3757191783Srmacklem NFSX_STATEIDOTHER); 3758191783Srmacklem tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 3759191783Srmacklem if (trunc) 3760191783Srmacklem *tl = newnfs_true; 3761191783Srmacklem else 3762191783Srmacklem *tl = newnfs_false; 3763191783Srmacklem (void) nfsm_fhtom(nd, (u_int8_t *)fhp, NFSX_MYFH, 0); 3764191783Srmacklem } else { 3765191783Srmacklem nd->nd_procnum = NFSV4PROC_CBNULL; 3766191783Srmacklem } 3767191783Srmacklem 3768191783Srmacklem /* 3769191783Srmacklem * Call newnfs_connect(), as required, and then newnfs_request(). 3770191783Srmacklem */ 3771191783Srmacklem (void) newnfs_sndlock(&clp->lc_req.nr_lock); 3772191783Srmacklem if (clp->lc_req.nr_client == NULL) { 3773191783Srmacklem if (nd->nd_procnum == NFSV4PROC_CBNULL) 3774191783Srmacklem error = newnfs_connect(NULL, &clp->lc_req, cred, 3775191783Srmacklem NULL, 1); 3776191783Srmacklem else 3777191783Srmacklem error = newnfs_connect(NULL, &clp->lc_req, cred, 3778191783Srmacklem NULL, 3); 3779191783Srmacklem } 3780191783Srmacklem newnfs_sndunlock(&clp->lc_req.nr_lock); 3781191783Srmacklem if (!error) { 3782191783Srmacklem error = newnfs_request(nd, NULL, clp, &clp->lc_req, NULL, 3783244042Srmacklem NULL, cred, clp->lc_program, NFSV4_CBVERS, NULL, 1, NULL, 3784244042Srmacklem NULL); 3785191783Srmacklem } 3786191783Srmacklem NFSFREECRED(cred); 3787191783Srmacklem 3788191783Srmacklem /* 3789191783Srmacklem * If error is set here, the Callback path isn't working 3790191783Srmacklem * properly, so twiddle the appropriate LCL_ flags. 3791191783Srmacklem * (nd_repstat != 0 indicates the Callback path is working, 3792191783Srmacklem * but the callback failed on the client.) 3793191783Srmacklem */ 3794191783Srmacklem if (error) { 3795191783Srmacklem /* 3796191783Srmacklem * Mark the callback pathway down, which disabled issuing 3797191783Srmacklem * of delegations and gets Renew to return NFSERR_CBPATHDOWN. 3798191783Srmacklem */ 3799191783Srmacklem NFSLOCKSTATE(); 3800191783Srmacklem clp->lc_flags |= LCL_CBDOWN; 3801191783Srmacklem NFSUNLOCKSTATE(); 3802191783Srmacklem } else { 3803191783Srmacklem /* 3804191783Srmacklem * Callback worked. If the callback path was down, disable 3805191783Srmacklem * callbacks, so no more delegations will be issued. (This 3806191783Srmacklem * is done on the assumption that the callback pathway is 3807191783Srmacklem * flakey.) 3808191783Srmacklem */ 3809191783Srmacklem NFSLOCKSTATE(); 3810191783Srmacklem if (clp->lc_flags & LCL_CBDOWN) 3811191783Srmacklem clp->lc_flags &= ~(LCL_CBDOWN | LCL_CALLBACKSON); 3812191783Srmacklem NFSUNLOCKSTATE(); 3813191783Srmacklem if (nd->nd_repstat) 3814191783Srmacklem error = nd->nd_repstat; 3815191783Srmacklem else if (procnum == NFSV4OP_CBGETATTR) 3816191783Srmacklem error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, 3817191783Srmacklem NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, 3818191783Srmacklem p, NULL); 3819191783Srmacklem mbuf_freem(nd->nd_mrep); 3820191783Srmacklem } 3821191783Srmacklem NFSLOCKSTATE(); 3822191783Srmacklem clp->lc_cbref--; 3823191783Srmacklem if ((clp->lc_flags & LCL_WAKEUPWANTED) && clp->lc_cbref == 0) { 3824191783Srmacklem clp->lc_flags &= ~LCL_WAKEUPWANTED; 3825235381Srmacklem wakeup(clp); 3826191783Srmacklem } 3827235381Srmacklem NFSUNLOCKSTATE(); 3828224086Szack 3829224086Szack NFSEXITCODE(error); 3830191783Srmacklem return (error); 3831191783Srmacklem} 3832191783Srmacklem 3833191783Srmacklem/* 3834191783Srmacklem * Return the next index# for a clientid. Mostly just increment and return 3835191783Srmacklem * the next one, but... if the 32bit unsigned does actually wrap around, 3836192589Srmacklem * it should be rebooted. 3837192589Srmacklem * At an average rate of one new client per second, it will wrap around in 3838191783Srmacklem * approximately 136 years. (I think the server will have been shut 3839191783Srmacklem * down or rebooted before then.) 3840191783Srmacklem */ 3841191783Srmacklemstatic u_int32_t 3842191783Srmacklemnfsrv_nextclientindex(void) 3843191783Srmacklem{ 3844191783Srmacklem static u_int32_t client_index = 0; 3845191783Srmacklem 3846191783Srmacklem client_index++; 3847191783Srmacklem if (client_index != 0) 3848191783Srmacklem return (client_index); 3849191783Srmacklem 3850192596Srmacklem printf("%s: out of clientids\n", __func__); 3851192588Srmacklem return (client_index); 3852191783Srmacklem} 3853191783Srmacklem 3854191783Srmacklem/* 3855191783Srmacklem * Return the next index# for a stateid. Mostly just increment and return 3856191783Srmacklem * the next one, but... if the 32bit unsigned does actually wrap around 3857191783Srmacklem * (will a BSD server stay up that long?), find 3858191783Srmacklem * new start and end values. 3859191783Srmacklem */ 3860191783Srmacklemstatic u_int32_t 3861191783Srmacklemnfsrv_nextstateindex(struct nfsclient *clp) 3862191783Srmacklem{ 3863191783Srmacklem struct nfsstate *stp; 3864191783Srmacklem int i; 3865191783Srmacklem u_int32_t canuse, min_index, max_index; 3866191783Srmacklem 3867191783Srmacklem if (!(clp->lc_flags & LCL_INDEXNOTOK)) { 3868191783Srmacklem clp->lc_stateindex++; 3869191783Srmacklem if (clp->lc_stateindex != clp->lc_statemaxindex) 3870191783Srmacklem return (clp->lc_stateindex); 3871191783Srmacklem } 3872191783Srmacklem 3873191783Srmacklem /* 3874191783Srmacklem * Yuck, we've hit the end. 3875191783Srmacklem * Look for a new min and max. 3876191783Srmacklem */ 3877191783Srmacklem min_index = 0; 3878191783Srmacklem max_index = 0xffffffff; 3879191783Srmacklem for (i = 0; i < NFSSTATEHASHSIZE; i++) { 3880191783Srmacklem LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { 3881191783Srmacklem if (stp->ls_stateid.other[2] > 0x80000000) { 3882191783Srmacklem if (stp->ls_stateid.other[2] < max_index) 3883191783Srmacklem max_index = stp->ls_stateid.other[2]; 3884191783Srmacklem } else { 3885191783Srmacklem if (stp->ls_stateid.other[2] > min_index) 3886191783Srmacklem min_index = stp->ls_stateid.other[2]; 3887191783Srmacklem } 3888191783Srmacklem } 3889191783Srmacklem } 3890191783Srmacklem 3891191783Srmacklem /* 3892191783Srmacklem * Yikes, highly unlikely, but I'll handle it anyhow. 3893191783Srmacklem */ 3894191783Srmacklem if (min_index == 0x80000000 && max_index == 0x80000001) { 3895191783Srmacklem canuse = 0; 3896191783Srmacklem /* 3897191783Srmacklem * Loop around until we find an unused entry. Return that 3898191783Srmacklem * and set LCL_INDEXNOTOK, so the search will continue next time. 3899191783Srmacklem * (This is one of those rare cases where a goto is the 3900191783Srmacklem * cleanest way to code the loop.) 3901191783Srmacklem */ 3902191783Srmacklemtryagain: 3903191783Srmacklem for (i = 0; i < NFSSTATEHASHSIZE; i++) { 3904191783Srmacklem LIST_FOREACH(stp, &clp->lc_stateid[i], ls_hash) { 3905191783Srmacklem if (stp->ls_stateid.other[2] == canuse) { 3906191783Srmacklem canuse++; 3907191783Srmacklem goto tryagain; 3908191783Srmacklem } 3909191783Srmacklem } 3910191783Srmacklem } 3911191783Srmacklem clp->lc_flags |= LCL_INDEXNOTOK; 3912191783Srmacklem return (canuse); 3913191783Srmacklem } 3914191783Srmacklem 3915191783Srmacklem /* 3916191783Srmacklem * Ok to start again from min + 1. 3917191783Srmacklem */ 3918191783Srmacklem clp->lc_stateindex = min_index + 1; 3919191783Srmacklem clp->lc_statemaxindex = max_index; 3920191783Srmacklem clp->lc_flags &= ~LCL_INDEXNOTOK; 3921191783Srmacklem return (clp->lc_stateindex); 3922191783Srmacklem} 3923191783Srmacklem 3924191783Srmacklem/* 3925191783Srmacklem * The following functions handle the stable storage file that deals with 3926191783Srmacklem * the edge conditions described in RFC3530 Sec. 8.6.3. 3927191783Srmacklem * The file is as follows: 3928191783Srmacklem * - a single record at the beginning that has the lease time of the 3929191783Srmacklem * previous server instance (before the last reboot) and the nfsrvboottime 3930191783Srmacklem * values for the previous server boots. 3931191783Srmacklem * These previous boot times are used to ensure that the current 3932191783Srmacklem * nfsrvboottime does not, somehow, get set to a previous one. 3933191783Srmacklem * (This is important so that Stale ClientIDs and StateIDs can 3934191783Srmacklem * be recognized.) 3935191783Srmacklem * The number of previous nfsvrboottime values preceeds the list. 3936191783Srmacklem * - followed by some number of appended records with: 3937191783Srmacklem * - client id string 3938191783Srmacklem * - flag that indicates it is a record revoking state via lease 3939191783Srmacklem * expiration or similar 3940191783Srmacklem * OR has successfully acquired state. 3941191783Srmacklem * These structures vary in length, with the client string at the end, up 3942191783Srmacklem * to NFSV4_OPAQUELIMIT in size. 3943191783Srmacklem * 3944191783Srmacklem * At the end of the grace period, the file is truncated, the first 3945191783Srmacklem * record is rewritten with updated information and any acquired state 3946191783Srmacklem * records for successful reclaims of state are written. 3947191783Srmacklem * 3948191783Srmacklem * Subsequent records are appended when the first state is issued to 3949191783Srmacklem * a client and when state is revoked for a client. 3950191783Srmacklem * 3951191783Srmacklem * When reading the file in, state issued records that come later in 3952191783Srmacklem * the file override older ones, since the append log is in cronological order. 3953191783Srmacklem * If, for some reason, the file can't be read, the grace period is 3954191783Srmacklem * immediately terminated and all reclaims get NFSERR_NOGRACE. 3955191783Srmacklem */ 3956191783Srmacklem 3957191783Srmacklem/* 3958191783Srmacklem * Read in the stable storage file. Called by nfssvc() before the nfsd 3959191783Srmacklem * processes start servicing requests. 3960191783Srmacklem */ 3961191783SrmacklemAPPLESTATIC void 3962191783Srmacklemnfsrv_setupstable(NFSPROC_T *p) 3963191783Srmacklem{ 3964191783Srmacklem struct nfsrv_stablefirst *sf = &nfsrv_stablefirst; 3965191783Srmacklem struct nfsrv_stable *sp, *nsp; 3966191783Srmacklem struct nfst_rec *tsp; 3967191783Srmacklem int error, i, tryagain; 3968191783Srmacklem off_t off = 0; 3969231949Skib ssize_t aresid, len; 3970191783Srmacklem 3971191783Srmacklem /* 3972191783Srmacklem * If NFSNSF_UPDATEDONE is set, this is a restart of the nfsds without 3973191783Srmacklem * a reboot, so state has not been lost. 3974191783Srmacklem */ 3975191783Srmacklem if (sf->nsf_flags & NFSNSF_UPDATEDONE) 3976191783Srmacklem return; 3977191783Srmacklem /* 3978191783Srmacklem * Set Grace over just until the file reads successfully. 3979191783Srmacklem */ 3980245909Sjhb nfsrvboottime = time_second; 3981191783Srmacklem LIST_INIT(&sf->nsf_head); 3982191783Srmacklem sf->nsf_flags = (NFSNSF_GRACEOVER | NFSNSF_NEEDLOCK); 3983191783Srmacklem sf->nsf_eograce = NFSD_MONOSEC + NFSRV_LEASEDELTA; 3984191783Srmacklem if (sf->nsf_fp == NULL) 3985191783Srmacklem return; 3986191783Srmacklem error = NFSD_RDWR(UIO_READ, NFSFPVNODE(sf->nsf_fp), 3987191783Srmacklem (caddr_t)&sf->nsf_rec, sizeof (struct nfsf_rec), off, UIO_SYSSPACE, 3988191783Srmacklem 0, NFSFPCRED(sf->nsf_fp), &aresid, p); 3989191783Srmacklem if (error || aresid || sf->nsf_numboots == 0 || 3990191783Srmacklem sf->nsf_numboots > NFSNSF_MAXNUMBOOTS) 3991191783Srmacklem return; 3992191783Srmacklem 3993191783Srmacklem /* 3994191783Srmacklem * Now, read in the boottimes. 3995191783Srmacklem */ 3996191783Srmacklem sf->nsf_bootvals = (time_t *)malloc((sf->nsf_numboots + 1) * 3997191783Srmacklem sizeof (time_t), M_TEMP, M_WAITOK); 3998191783Srmacklem off = sizeof (struct nfsf_rec); 3999191783Srmacklem error = NFSD_RDWR(UIO_READ, NFSFPVNODE(sf->nsf_fp), 4000191783Srmacklem (caddr_t)sf->nsf_bootvals, sf->nsf_numboots * sizeof (time_t), off, 4001191783Srmacklem UIO_SYSSPACE, 0, NFSFPCRED(sf->nsf_fp), &aresid, p); 4002191783Srmacklem if (error || aresid) { 4003191783Srmacklem free((caddr_t)sf->nsf_bootvals, M_TEMP); 4004191783Srmacklem sf->nsf_bootvals = NULL; 4005191783Srmacklem return; 4006191783Srmacklem } 4007191783Srmacklem 4008191783Srmacklem /* 4009191783Srmacklem * Make sure this nfsrvboottime is different from all recorded 4010191783Srmacklem * previous ones. 4011191783Srmacklem */ 4012191783Srmacklem do { 4013191783Srmacklem tryagain = 0; 4014191783Srmacklem for (i = 0; i < sf->nsf_numboots; i++) { 4015191783Srmacklem if (nfsrvboottime == sf->nsf_bootvals[i]) { 4016191783Srmacklem nfsrvboottime++; 4017191783Srmacklem tryagain = 1; 4018191783Srmacklem break; 4019191783Srmacklem } 4020191783Srmacklem } 4021191783Srmacklem } while (tryagain); 4022191783Srmacklem 4023191783Srmacklem sf->nsf_flags |= NFSNSF_OK; 4024191783Srmacklem off += (sf->nsf_numboots * sizeof (time_t)); 4025191783Srmacklem 4026191783Srmacklem /* 4027191783Srmacklem * Read through the file, building a list of records for grace 4028191783Srmacklem * checking. 4029191783Srmacklem * Each record is between sizeof (struct nfst_rec) and 4030191783Srmacklem * sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1 4031191783Srmacklem * and is actually sizeof (struct nfst_rec) + nst_len - 1. 4032191783Srmacklem */ 4033191783Srmacklem tsp = (struct nfst_rec *)malloc(sizeof (struct nfst_rec) + 4034191783Srmacklem NFSV4_OPAQUELIMIT - 1, M_TEMP, M_WAITOK); 4035191783Srmacklem do { 4036191783Srmacklem error = NFSD_RDWR(UIO_READ, NFSFPVNODE(sf->nsf_fp), 4037191783Srmacklem (caddr_t)tsp, sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1, 4038191783Srmacklem off, UIO_SYSSPACE, 0, NFSFPCRED(sf->nsf_fp), &aresid, p); 4039191783Srmacklem len = (sizeof (struct nfst_rec) + NFSV4_OPAQUELIMIT - 1) - aresid; 4040191783Srmacklem if (error || (len > 0 && (len < sizeof (struct nfst_rec) || 4041191783Srmacklem len < (sizeof (struct nfst_rec) + tsp->len - 1)))) { 4042191783Srmacklem /* 4043191783Srmacklem * Yuck, the file has been corrupted, so just return 4044191783Srmacklem * after clearing out any restart state, so the grace period 4045191783Srmacklem * is over. 4046191783Srmacklem */ 4047191783Srmacklem LIST_FOREACH_SAFE(sp, &sf->nsf_head, nst_list, nsp) { 4048191783Srmacklem LIST_REMOVE(sp, nst_list); 4049191783Srmacklem free((caddr_t)sp, M_TEMP); 4050191783Srmacklem } 4051191783Srmacklem free((caddr_t)tsp, M_TEMP); 4052191783Srmacklem sf->nsf_flags &= ~NFSNSF_OK; 4053191783Srmacklem free((caddr_t)sf->nsf_bootvals, M_TEMP); 4054191783Srmacklem sf->nsf_bootvals = NULL; 4055191783Srmacklem return; 4056191783Srmacklem } 4057191783Srmacklem if (len > 0) { 4058191783Srmacklem off += sizeof (struct nfst_rec) + tsp->len - 1; 4059191783Srmacklem /* 4060191783Srmacklem * Search the list for a matching client. 4061191783Srmacklem */ 4062191783Srmacklem LIST_FOREACH(sp, &sf->nsf_head, nst_list) { 4063191783Srmacklem if (tsp->len == sp->nst_len && 4064191783Srmacklem !NFSBCMP(tsp->client, sp->nst_client, tsp->len)) 4065191783Srmacklem break; 4066191783Srmacklem } 4067191783Srmacklem if (sp == LIST_END(&sf->nsf_head)) { 4068191783Srmacklem sp = (struct nfsrv_stable *)malloc(tsp->len + 4069191783Srmacklem sizeof (struct nfsrv_stable) - 1, M_TEMP, 4070191783Srmacklem M_WAITOK); 4071191783Srmacklem NFSBCOPY((caddr_t)tsp, (caddr_t)&sp->nst_rec, 4072191783Srmacklem sizeof (struct nfst_rec) + tsp->len - 1); 4073191783Srmacklem LIST_INSERT_HEAD(&sf->nsf_head, sp, nst_list); 4074191783Srmacklem } else { 4075191783Srmacklem if (tsp->flag == NFSNST_REVOKE) 4076191783Srmacklem sp->nst_flag |= NFSNST_REVOKE; 4077191783Srmacklem else 4078191783Srmacklem /* 4079191783Srmacklem * A subsequent timestamp indicates the client 4080191783Srmacklem * did a setclientid/confirm and any previous 4081191783Srmacklem * revoke is no longer relevant. 4082191783Srmacklem */ 4083191783Srmacklem sp->nst_flag &= ~NFSNST_REVOKE; 4084191783Srmacklem } 4085191783Srmacklem } 4086191783Srmacklem } while (len > 0); 4087191783Srmacklem free((caddr_t)tsp, M_TEMP); 4088191783Srmacklem sf->nsf_flags = NFSNSF_OK; 4089191783Srmacklem sf->nsf_eograce = NFSD_MONOSEC + sf->nsf_lease + 4090191783Srmacklem NFSRV_LEASEDELTA; 4091191783Srmacklem} 4092191783Srmacklem 4093191783Srmacklem/* 4094191783Srmacklem * Update the stable storage file, now that the grace period is over. 4095191783Srmacklem */ 4096191783SrmacklemAPPLESTATIC void 4097191783Srmacklemnfsrv_updatestable(NFSPROC_T *p) 4098191783Srmacklem{ 4099191783Srmacklem struct nfsrv_stablefirst *sf = &nfsrv_stablefirst; 4100191783Srmacklem struct nfsrv_stable *sp, *nsp; 4101191783Srmacklem int i; 4102191783Srmacklem struct nfsvattr nva; 4103191783Srmacklem vnode_t vp; 4104191783Srmacklem#if defined(__FreeBSD_version) && (__FreeBSD_version >= 500000) 4105191783Srmacklem mount_t mp = NULL; 4106191783Srmacklem#endif 4107191783Srmacklem int error; 4108191783Srmacklem 4109191783Srmacklem if (sf->nsf_fp == NULL || (sf->nsf_flags & NFSNSF_UPDATEDONE)) 4110191783Srmacklem return; 4111191783Srmacklem sf->nsf_flags |= NFSNSF_UPDATEDONE; 4112191783Srmacklem /* 4113191783Srmacklem * Ok, we need to rewrite the stable storage file. 4114191783Srmacklem * - truncate to 0 length 4115191783Srmacklem * - write the new first structure 4116191783Srmacklem * - loop through the data structures, writing out any that 4117191783Srmacklem * have timestamps older than the old boot 4118191783Srmacklem */ 4119191783Srmacklem if (sf->nsf_bootvals) { 4120191783Srmacklem sf->nsf_numboots++; 4121191783Srmacklem for (i = sf->nsf_numboots - 2; i >= 0; i--) 4122191783Srmacklem sf->nsf_bootvals[i + 1] = sf->nsf_bootvals[i]; 4123191783Srmacklem } else { 4124191783Srmacklem sf->nsf_numboots = 1; 4125191783Srmacklem sf->nsf_bootvals = (time_t *)malloc(sizeof (time_t), 4126191783Srmacklem M_TEMP, M_WAITOK); 4127191783Srmacklem } 4128191783Srmacklem sf->nsf_bootvals[0] = nfsrvboottime; 4129191783Srmacklem sf->nsf_lease = nfsrv_lease; 4130191783Srmacklem NFSVNO_ATTRINIT(&nva); 4131191783Srmacklem NFSVNO_SETATTRVAL(&nva, size, 0); 4132191783Srmacklem vp = NFSFPVNODE(sf->nsf_fp); 4133217066Srmacklem vn_start_write(vp, &mp, V_WAIT); 4134224081Szack if (NFSVOPLOCK(vp, LK_EXCLUSIVE) == 0) { 4135216893Srmacklem error = nfsvno_setattr(vp, &nva, NFSFPCRED(sf->nsf_fp), p, 4136216893Srmacklem NULL); 4137224082Szack NFSVOPUNLOCK(vp, 0); 4138216893Srmacklem } else 4139216893Srmacklem error = EPERM; 4140217066Srmacklem vn_finished_write(mp); 4141191783Srmacklem if (!error) 4142191783Srmacklem error = NFSD_RDWR(UIO_WRITE, vp, 4143191783Srmacklem (caddr_t)&sf->nsf_rec, sizeof (struct nfsf_rec), (off_t)0, 4144191783Srmacklem UIO_SYSSPACE, IO_SYNC, NFSFPCRED(sf->nsf_fp), NULL, p); 4145191783Srmacklem if (!error) 4146191783Srmacklem error = NFSD_RDWR(UIO_WRITE, vp, 4147191783Srmacklem (caddr_t)sf->nsf_bootvals, 4148191783Srmacklem sf->nsf_numboots * sizeof (time_t), 4149191783Srmacklem (off_t)(sizeof (struct nfsf_rec)), 4150191783Srmacklem UIO_SYSSPACE, IO_SYNC, NFSFPCRED(sf->nsf_fp), NULL, p); 4151191783Srmacklem free((caddr_t)sf->nsf_bootvals, M_TEMP); 4152191783Srmacklem sf->nsf_bootvals = NULL; 4153191783Srmacklem if (error) { 4154191783Srmacklem sf->nsf_flags &= ~NFSNSF_OK; 4155191783Srmacklem printf("EEK! Can't write NfsV4 stable storage file\n"); 4156191783Srmacklem return; 4157191783Srmacklem } 4158191783Srmacklem sf->nsf_flags |= NFSNSF_OK; 4159191783Srmacklem 4160191783Srmacklem /* 4161191783Srmacklem * Loop through the list and write out timestamp records for 4162191783Srmacklem * any clients that successfully reclaimed state. 4163191783Srmacklem */ 4164191783Srmacklem LIST_FOREACH_SAFE(sp, &sf->nsf_head, nst_list, nsp) { 4165191783Srmacklem if (sp->nst_flag & NFSNST_GOTSTATE) { 4166191783Srmacklem nfsrv_writestable(sp->nst_client, sp->nst_len, 4167191783Srmacklem NFSNST_NEWSTATE, p); 4168191783Srmacklem sp->nst_clp->lc_flags |= LCL_STAMPEDSTABLE; 4169191783Srmacklem } 4170191783Srmacklem LIST_REMOVE(sp, nst_list); 4171191783Srmacklem free((caddr_t)sp, M_TEMP); 4172191783Srmacklem } 4173217432Srmacklem nfsrv_backupstable(); 4174191783Srmacklem} 4175191783Srmacklem 4176191783Srmacklem/* 4177191783Srmacklem * Append a record to the stable storage file. 4178191783Srmacklem */ 4179191783SrmacklemAPPLESTATIC void 4180191783Srmacklemnfsrv_writestable(u_char *client, int len, int flag, NFSPROC_T *p) 4181191783Srmacklem{ 4182191783Srmacklem struct nfsrv_stablefirst *sf = &nfsrv_stablefirst; 4183191783Srmacklem struct nfst_rec *sp; 4184191783Srmacklem int error; 4185191783Srmacklem 4186191783Srmacklem if (!(sf->nsf_flags & NFSNSF_OK) || sf->nsf_fp == NULL) 4187191783Srmacklem return; 4188191783Srmacklem sp = (struct nfst_rec *)malloc(sizeof (struct nfst_rec) + 4189191783Srmacklem len - 1, M_TEMP, M_WAITOK); 4190191783Srmacklem sp->len = len; 4191191783Srmacklem NFSBCOPY(client, sp->client, len); 4192191783Srmacklem sp->flag = flag; 4193191783Srmacklem error = NFSD_RDWR(UIO_WRITE, NFSFPVNODE(sf->nsf_fp), 4194191783Srmacklem (caddr_t)sp, sizeof (struct nfst_rec) + len - 1, (off_t)0, 4195191783Srmacklem UIO_SYSSPACE, (IO_SYNC | IO_APPEND), NFSFPCRED(sf->nsf_fp), NULL, p); 4196191783Srmacklem free((caddr_t)sp, M_TEMP); 4197191783Srmacklem if (error) { 4198191783Srmacklem sf->nsf_flags &= ~NFSNSF_OK; 4199191783Srmacklem printf("EEK! Can't write NfsV4 stable storage file\n"); 4200191783Srmacklem } 4201191783Srmacklem} 4202191783Srmacklem 4203191783Srmacklem/* 4204191783Srmacklem * This function is called during the grace period to mark a client 4205191783Srmacklem * that successfully reclaimed state. 4206191783Srmacklem */ 4207191783Srmacklemstatic void 4208191783Srmacklemnfsrv_markstable(struct nfsclient *clp) 4209191783Srmacklem{ 4210191783Srmacklem struct nfsrv_stable *sp; 4211191783Srmacklem 4212191783Srmacklem /* 4213191783Srmacklem * First find the client structure. 4214191783Srmacklem */ 4215191783Srmacklem LIST_FOREACH(sp, &nfsrv_stablefirst.nsf_head, nst_list) { 4216191783Srmacklem if (sp->nst_len == clp->lc_idlen && 4217191783Srmacklem !NFSBCMP(sp->nst_client, clp->lc_id, sp->nst_len)) 4218191783Srmacklem break; 4219191783Srmacklem } 4220191783Srmacklem if (sp == LIST_END(&nfsrv_stablefirst.nsf_head)) 4221191783Srmacklem return; 4222191783Srmacklem 4223191783Srmacklem /* 4224191783Srmacklem * Now, just mark it and set the nfsclient back pointer. 4225191783Srmacklem */ 4226191783Srmacklem sp->nst_flag |= NFSNST_GOTSTATE; 4227191783Srmacklem sp->nst_clp = clp; 4228191783Srmacklem} 4229191783Srmacklem 4230191783Srmacklem/* 4231191783Srmacklem * This function is called for a reclaim, to see if it gets grace. 4232191783Srmacklem * It returns 0 if a reclaim is allowed, 1 otherwise. 4233191783Srmacklem */ 4234191783Srmacklemstatic int 4235191783Srmacklemnfsrv_checkstable(struct nfsclient *clp) 4236191783Srmacklem{ 4237191783Srmacklem struct nfsrv_stable *sp; 4238191783Srmacklem 4239191783Srmacklem /* 4240191783Srmacklem * First, find the entry for the client. 4241191783Srmacklem */ 4242191783Srmacklem LIST_FOREACH(sp, &nfsrv_stablefirst.nsf_head, nst_list) { 4243191783Srmacklem if (sp->nst_len == clp->lc_idlen && 4244191783Srmacklem !NFSBCMP(sp->nst_client, clp->lc_id, sp->nst_len)) 4245191783Srmacklem break; 4246191783Srmacklem } 4247191783Srmacklem 4248191783Srmacklem /* 4249191783Srmacklem * If not in the list, state was revoked or no state was issued 4250191783Srmacklem * since the previous reboot, a reclaim is denied. 4251191783Srmacklem */ 4252191783Srmacklem if (sp == LIST_END(&nfsrv_stablefirst.nsf_head) || 4253191783Srmacklem (sp->nst_flag & NFSNST_REVOKE) || 4254191783Srmacklem !(nfsrv_stablefirst.nsf_flags & NFSNSF_OK)) 4255191783Srmacklem return (1); 4256191783Srmacklem return (0); 4257191783Srmacklem} 4258191783Srmacklem 4259191783Srmacklem/* 4260191783Srmacklem * Test for and try to clear out a conflicting client. This is called by 4261191783Srmacklem * nfsrv_lockctrl() and nfsrv_openctrl() when conflicts with other clients 4262191783Srmacklem * a found. 4263191783Srmacklem * The trick here is that it can't revoke a conflicting client with an 4264191783Srmacklem * expired lease unless it holds the v4root lock, so... 4265191783Srmacklem * If no v4root lock, get the lock and return 1 to indicate "try again". 4266191783Srmacklem * Return 0 to indicate the conflict can't be revoked and 1 to indicate 4267191783Srmacklem * the revocation worked and the conflicting client is "bye, bye", so it 4268191783Srmacklem * can be tried again. 4269224081Szack * Return 2 to indicate that the vnode is VI_DOOMED after NFSVOPLOCK(). 4270191783Srmacklem * Unlocks State before a non-zero value is returned. 4271191783Srmacklem */ 4272191783Srmacklemstatic int 4273216893Srmacklemnfsrv_clientconflict(struct nfsclient *clp, int *haslockp, vnode_t vp, 4274191783Srmacklem NFSPROC_T *p) 4275191783Srmacklem{ 4276216875Srmacklem int gotlock, lktype; 4277191783Srmacklem 4278191783Srmacklem /* 4279191783Srmacklem * If lease hasn't expired, we can't fix it. 4280191783Srmacklem */ 4281191783Srmacklem if (clp->lc_expiry >= NFSD_MONOSEC || 4282191783Srmacklem !(nfsrv_stablefirst.nsf_flags & NFSNSF_UPDATEDONE)) 4283191783Srmacklem return (0); 4284191783Srmacklem if (*haslockp == 0) { 4285191783Srmacklem NFSUNLOCKSTATE(); 4286224083Szack lktype = NFSVOPISLOCKED(vp); 4287224082Szack NFSVOPUNLOCK(vp, 0); 4288191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4289191783Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 4290191783Srmacklem do { 4291191783Srmacklem gotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, 4292222389Srmacklem NFSV4ROOTLOCKMUTEXPTR, NULL); 4293191783Srmacklem } while (!gotlock); 4294191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4295191783Srmacklem *haslockp = 1; 4296224081Szack NFSVOPLOCK(vp, lktype | LK_RETRY); 4297216893Srmacklem if ((vp->v_iflag & VI_DOOMED) != 0) 4298216893Srmacklem return (2); 4299216893Srmacklem else 4300216893Srmacklem return (1); 4301191783Srmacklem } 4302191783Srmacklem NFSUNLOCKSTATE(); 4303191783Srmacklem 4304191783Srmacklem /* 4305191783Srmacklem * Ok, we can expire the conflicting client. 4306191783Srmacklem */ 4307191783Srmacklem nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p); 4308217432Srmacklem nfsrv_backupstable(); 4309191783Srmacklem nfsrv_cleanclient(clp, p); 4310191783Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 4311191783Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 4312191783Srmacklem LIST_REMOVE(clp, lc_hash); 4313191783Srmacklem nfsrv_zapclient(clp, p); 4314191783Srmacklem return (1); 4315191783Srmacklem} 4316191783Srmacklem 4317191783Srmacklem/* 4318191783Srmacklem * Resolve a delegation conflict. 4319191783Srmacklem * Returns 0 to indicate the conflict was resolved without sleeping. 4320191783Srmacklem * Return -1 to indicate that the caller should check for conflicts again. 4321191783Srmacklem * Return > 0 for an error that should be returned, normally NFSERR_DELAY. 4322191783Srmacklem * 4323191783Srmacklem * Also, manipulate the nfsv4root_lock, as required. It isn't changed 4324191783Srmacklem * for a return of 0, since there was no sleep and it could be required 4325191783Srmacklem * later. It is released for a return of NFSERR_DELAY, since the caller 4326191783Srmacklem * will return that error. It is released when a sleep was done waiting 4327191783Srmacklem * for the delegation to be returned or expire (so that other nfsds can 4328191783Srmacklem * handle ops). Then, it must be acquired for the write to stable storage. 4329191783Srmacklem * (This function is somewhat similar to nfsrv_clientconflict(), but 4330191783Srmacklem * the semantics differ in a couple of subtle ways. The return of 0 4331191783Srmacklem * indicates the conflict was resolved without sleeping here, not 4332191783Srmacklem * that the conflict can't be resolved and the handling of nfsv4root_lock 4333191783Srmacklem * differs, as noted above.) 4334191783Srmacklem * Unlocks State before returning a non-zero value. 4335191783Srmacklem */ 4336191783Srmacklemstatic int 4337191783Srmacklemnfsrv_delegconflict(struct nfsstate *stp, int *haslockp, NFSPROC_T *p, 4338216510Srmacklem vnode_t vp) 4339191783Srmacklem{ 4340191783Srmacklem struct nfsclient *clp = stp->ls_clp; 4341216875Srmacklem int gotlock, error, lktype, retrycnt, zapped_clp; 4342191783Srmacklem nfsv4stateid_t tstateid; 4343191783Srmacklem fhandle_t tfh; 4344191783Srmacklem 4345191783Srmacklem /* 4346191783Srmacklem * If the conflict is with an old delegation... 4347191783Srmacklem */ 4348191783Srmacklem if (stp->ls_flags & NFSLCK_OLDDELEG) { 4349191783Srmacklem /* 4350191783Srmacklem * You can delete it, if it has expired. 4351191783Srmacklem */ 4352191783Srmacklem if (clp->lc_delegtime < NFSD_MONOSEC) { 4353191783Srmacklem nfsrv_freedeleg(stp); 4354191783Srmacklem NFSUNLOCKSTATE(); 4355224086Szack error = -1; 4356224086Szack goto out; 4357191783Srmacklem } 4358191783Srmacklem NFSUNLOCKSTATE(); 4359191783Srmacklem /* 4360191783Srmacklem * During this delay, the old delegation could expire or it 4361191783Srmacklem * could be recovered by the client via an Open with 4362191783Srmacklem * CLAIM_DELEGATE_PREV. 4363191783Srmacklem * Release the nfsv4root_lock, if held. 4364191783Srmacklem */ 4365191783Srmacklem if (*haslockp) { 4366191783Srmacklem *haslockp = 0; 4367191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4368191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4369191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4370191783Srmacklem } 4371224086Szack error = NFSERR_DELAY; 4372224086Szack goto out; 4373191783Srmacklem } 4374191783Srmacklem 4375191783Srmacklem /* 4376191783Srmacklem * It's a current delegation, so: 4377191783Srmacklem * - check to see if the delegation has expired 4378191783Srmacklem * - if so, get the v4root lock and then expire it 4379191783Srmacklem */ 4380191783Srmacklem if (!(stp->ls_flags & NFSLCK_DELEGRECALL)) { 4381191783Srmacklem /* 4382191783Srmacklem * - do a recall callback, since not yet done 4383191783Srmacklem * For now, never allow truncate to be set. To use 4384191783Srmacklem * truncate safely, it must be guaranteed that the 4385191783Srmacklem * Remove, Rename or Setattr with size of 0 will 4386191783Srmacklem * succeed and that would require major changes to 4387191783Srmacklem * the VFS/Vnode OPs. 4388191783Srmacklem * Set the expiry time large enough so that it won't expire 4389191783Srmacklem * until after the callback, then set it correctly, once 4390191783Srmacklem * the callback is done. (The delegation will now time 4391191783Srmacklem * out whether or not the Recall worked ok. The timeout 4392191783Srmacklem * will be extended when ops are done on the delegation 4393191783Srmacklem * stateid, up to the timelimit.) 4394191783Srmacklem */ 4395191783Srmacklem stp->ls_delegtime = NFSD_MONOSEC + (2 * nfsrv_lease) + 4396191783Srmacklem NFSRV_LEASEDELTA; 4397191783Srmacklem stp->ls_delegtimelimit = NFSD_MONOSEC + (6 * nfsrv_lease) + 4398191783Srmacklem NFSRV_LEASEDELTA; 4399191783Srmacklem stp->ls_flags |= NFSLCK_DELEGRECALL; 4400191783Srmacklem 4401191783Srmacklem /* 4402191783Srmacklem * Loop NFSRV_CBRETRYCNT times while the CBRecall replies 4403191783Srmacklem * NFSERR_BADSTATEID or NFSERR_BADHANDLE. This is done 4404191783Srmacklem * in order to try and avoid a race that could happen 4405191783Srmacklem * when a CBRecall request passed the Open reply with 4406191783Srmacklem * the delegation in it when transitting the network. 4407191783Srmacklem * Since nfsrv_docallback will sleep, don't use stp after 4408191783Srmacklem * the call. 4409191783Srmacklem */ 4410191783Srmacklem NFSBCOPY((caddr_t)&stp->ls_stateid, (caddr_t)&tstateid, 4411191783Srmacklem sizeof (tstateid)); 4412191783Srmacklem NFSBCOPY((caddr_t)&stp->ls_lfp->lf_fh, (caddr_t)&tfh, 4413191783Srmacklem sizeof (tfh)); 4414191783Srmacklem NFSUNLOCKSTATE(); 4415191783Srmacklem if (*haslockp) { 4416191783Srmacklem *haslockp = 0; 4417191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4418191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4419191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4420191783Srmacklem } 4421191783Srmacklem retrycnt = 0; 4422191783Srmacklem do { 4423191783Srmacklem error = nfsrv_docallback(clp, NFSV4OP_CBRECALL, 4424191783Srmacklem &tstateid, 0, &tfh, NULL, NULL, p); 4425191783Srmacklem retrycnt++; 4426191783Srmacklem } while ((error == NFSERR_BADSTATEID || 4427191783Srmacklem error == NFSERR_BADHANDLE) && retrycnt < NFSV4_CBRETRYCNT); 4428224086Szack error = NFSERR_DELAY; 4429224086Szack goto out; 4430191783Srmacklem } 4431191783Srmacklem 4432191783Srmacklem if (clp->lc_expiry >= NFSD_MONOSEC && 4433191783Srmacklem stp->ls_delegtime >= NFSD_MONOSEC) { 4434191783Srmacklem NFSUNLOCKSTATE(); 4435191783Srmacklem /* 4436191783Srmacklem * A recall has been done, but it has not yet expired. 4437191783Srmacklem * So, RETURN_DELAY. 4438191783Srmacklem */ 4439191783Srmacklem if (*haslockp) { 4440191783Srmacklem *haslockp = 0; 4441191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4442191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4443191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4444191783Srmacklem } 4445224086Szack error = NFSERR_DELAY; 4446224086Szack goto out; 4447191783Srmacklem } 4448191783Srmacklem 4449191783Srmacklem /* 4450191783Srmacklem * If we don't yet have the lock, just get it and then return, 4451191783Srmacklem * since we need that before deleting expired state, such as 4452191783Srmacklem * this delegation. 4453191783Srmacklem * When getting the lock, unlock the vnode, so other nfsds that 4454191783Srmacklem * are in progress, won't get stuck waiting for the vnode lock. 4455191783Srmacklem */ 4456191783Srmacklem if (*haslockp == 0) { 4457191783Srmacklem NFSUNLOCKSTATE(); 4458224083Szack lktype = NFSVOPISLOCKED(vp); 4459224082Szack NFSVOPUNLOCK(vp, 0); 4460191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4461191783Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 4462191783Srmacklem do { 4463191783Srmacklem gotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL, 4464222389Srmacklem NFSV4ROOTLOCKMUTEXPTR, NULL); 4465191783Srmacklem } while (!gotlock); 4466191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4467191783Srmacklem *haslockp = 1; 4468224081Szack NFSVOPLOCK(vp, lktype | LK_RETRY); 4469216893Srmacklem if ((vp->v_iflag & VI_DOOMED) != 0) { 4470216893Srmacklem *haslockp = 0; 4471216893Srmacklem NFSLOCKV4ROOTMUTEX(); 4472216893Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4473216893Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4474224086Szack error = NFSERR_PERM; 4475224086Szack goto out; 4476216893Srmacklem } 4477224086Szack error = -1; 4478224086Szack goto out; 4479191783Srmacklem } 4480191783Srmacklem 4481191783Srmacklem NFSUNLOCKSTATE(); 4482191783Srmacklem /* 4483191783Srmacklem * Ok, we can delete the expired delegation. 4484191783Srmacklem * First, write the Revoke record to stable storage and then 4485191783Srmacklem * clear out the conflict. 4486191783Srmacklem * Since all other nfsd threads are now blocked, we can safely 4487191783Srmacklem * sleep without the state changing. 4488191783Srmacklem */ 4489191783Srmacklem nfsrv_writestable(clp->lc_id, clp->lc_idlen, NFSNST_REVOKE, p); 4490217432Srmacklem nfsrv_backupstable(); 4491191783Srmacklem if (clp->lc_expiry < NFSD_MONOSEC) { 4492191783Srmacklem nfsrv_cleanclient(clp, p); 4493191783Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 4494191783Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 4495191783Srmacklem LIST_REMOVE(clp, lc_hash); 4496191783Srmacklem zapped_clp = 1; 4497191783Srmacklem } else { 4498191783Srmacklem nfsrv_freedeleg(stp); 4499191783Srmacklem zapped_clp = 0; 4500191783Srmacklem } 4501191783Srmacklem if (zapped_clp) 4502191783Srmacklem nfsrv_zapclient(clp, p); 4503224086Szack error = -1; 4504224086Szack 4505224086Szackout: 4506224086Szack NFSEXITCODE(error); 4507224086Szack return (error); 4508191783Srmacklem} 4509191783Srmacklem 4510191783Srmacklem/* 4511191783Srmacklem * Check for a remove allowed, if remove is set to 1 and get rid of 4512191783Srmacklem * delegations. 4513191783Srmacklem */ 4514191783SrmacklemAPPLESTATIC int 4515191783Srmacklemnfsrv_checkremove(vnode_t vp, int remove, NFSPROC_T *p) 4516191783Srmacklem{ 4517191783Srmacklem struct nfsstate *stp; 4518191783Srmacklem struct nfslockfile *lfp; 4519191783Srmacklem int error, haslock = 0; 4520191783Srmacklem fhandle_t nfh; 4521191783Srmacklem 4522191783Srmacklem /* 4523191783Srmacklem * First, get the lock file structure. 4524191783Srmacklem * (A return of -1 means no associated state, so remove ok.) 4525191783Srmacklem */ 4526191783Srmacklem error = nfsrv_getlockfh(vp, NFSLCK_CHECK, NULL, &nfh, p); 4527191783Srmacklemtryagain: 4528191783Srmacklem NFSLOCKSTATE(); 4529191783Srmacklem if (!error) 4530205941Srmacklem error = nfsrv_getlockfile(NFSLCK_CHECK, NULL, &lfp, &nfh, 0); 4531191783Srmacklem if (error) { 4532191783Srmacklem NFSUNLOCKSTATE(); 4533191783Srmacklem if (haslock) { 4534191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4535191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4536191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4537191783Srmacklem } 4538191783Srmacklem if (error == -1) 4539224086Szack error = 0; 4540224086Szack goto out; 4541191783Srmacklem } 4542191783Srmacklem 4543191783Srmacklem /* 4544191783Srmacklem * Now, we must Recall any delegations. 4545191783Srmacklem */ 4546191783Srmacklem error = nfsrv_cleandeleg(vp, lfp, NULL, &haslock, p); 4547191783Srmacklem if (error) { 4548191783Srmacklem /* 4549191783Srmacklem * nfsrv_cleandeleg() unlocks state for non-zero 4550191783Srmacklem * return. 4551191783Srmacklem */ 4552191783Srmacklem if (error == -1) 4553191783Srmacklem goto tryagain; 4554191783Srmacklem if (haslock) { 4555191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4556191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4557191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4558191783Srmacklem } 4559224086Szack goto out; 4560191783Srmacklem } 4561191783Srmacklem 4562191783Srmacklem /* 4563191783Srmacklem * Now, look for a conflicting open share. 4564191783Srmacklem */ 4565191783Srmacklem if (remove) { 4566191783Srmacklem LIST_FOREACH(stp, &lfp->lf_open, ls_file) { 4567191783Srmacklem if (stp->ls_flags & NFSLCK_WRITEDENY) { 4568191783Srmacklem error = NFSERR_FILEOPEN; 4569191783Srmacklem break; 4570191783Srmacklem } 4571191783Srmacklem } 4572191783Srmacklem } 4573191783Srmacklem 4574191783Srmacklem NFSUNLOCKSTATE(); 4575191783Srmacklem if (haslock) { 4576191783Srmacklem NFSLOCKV4ROOTMUTEX(); 4577191783Srmacklem nfsv4_unlock(&nfsv4rootfs_lock, 1); 4578191783Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4579191783Srmacklem } 4580224086Szack 4581224086Szackout: 4582224086Szack NFSEXITCODE(error); 4583191783Srmacklem return (error); 4584191783Srmacklem} 4585191783Srmacklem 4586191783Srmacklem/* 4587191783Srmacklem * Clear out all delegations for the file referred to by lfp. 4588191783Srmacklem * May return NFSERR_DELAY, if there will be a delay waiting for 4589191783Srmacklem * delegations to expire. 4590191783Srmacklem * Returns -1 to indicate it slept while recalling a delegation. 4591191783Srmacklem * This function has the side effect of deleting the nfslockfile structure, 4592191783Srmacklem * if it no longer has associated state and didn't have to sleep. 4593191783Srmacklem * Unlocks State before a non-zero value is returned. 4594191783Srmacklem */ 4595191783Srmacklemstatic int 4596191783Srmacklemnfsrv_cleandeleg(vnode_t vp, struct nfslockfile *lfp, 4597191783Srmacklem struct nfsclient *clp, int *haslockp, NFSPROC_T *p) 4598191783Srmacklem{ 4599191783Srmacklem struct nfsstate *stp, *nstp; 4600224086Szack int ret = 0; 4601191783Srmacklem 4602191783Srmacklem stp = LIST_FIRST(&lfp->lf_deleg); 4603191783Srmacklem while (stp != LIST_END(&lfp->lf_deleg)) { 4604191783Srmacklem nstp = LIST_NEXT(stp, ls_file); 4605191783Srmacklem if (stp->ls_clp != clp) { 4606191783Srmacklem ret = nfsrv_delegconflict(stp, haslockp, p, vp); 4607191783Srmacklem if (ret) { 4608191783Srmacklem /* 4609191783Srmacklem * nfsrv_delegconflict() unlocks state 4610191783Srmacklem * when it returns non-zero. 4611191783Srmacklem */ 4612224086Szack goto out; 4613191783Srmacklem } 4614191783Srmacklem } 4615191783Srmacklem stp = nstp; 4616191783Srmacklem } 4617224086Szackout: 4618224086Szack NFSEXITCODE(ret); 4619224086Szack return (ret); 4620191783Srmacklem} 4621191783Srmacklem 4622191783Srmacklem/* 4623191783Srmacklem * There are certain operations that, when being done outside of NFSv4, 4624191783Srmacklem * require that any NFSv4 delegation for the file be recalled. 4625191783Srmacklem * This function is to be called for those cases: 4626191783Srmacklem * VOP_RENAME() - When a delegation is being recalled for any reason, 4627191783Srmacklem * the client may have to do Opens against the server, using the file's 4628191783Srmacklem * final component name. If the file has been renamed on the server, 4629191783Srmacklem * that component name will be incorrect and the Open will fail. 4630191783Srmacklem * VOP_REMOVE() - Theoretically, a client could Open a file after it has 4631191783Srmacklem * been removed on the server, if there is a delegation issued to 4632191783Srmacklem * that client for the file. I say "theoretically" since clients 4633191783Srmacklem * normally do an Access Op before the Open and that Access Op will 4634191783Srmacklem * fail with ESTALE. Note that NFSv2 and 3 don't even do Opens, so 4635191783Srmacklem * they will detect the file's removal in the same manner. (There is 4636191783Srmacklem * one case where RFC3530 allows a client to do an Open without first 4637191783Srmacklem * doing an Access Op, which is passage of a check against the ACE 4638191783Srmacklem * returned with a Write delegation, but current practice is to ignore 4639191783Srmacklem * the ACE and always do an Access Op.) 4640191783Srmacklem * Since the functions can only be called with an unlocked vnode, this 4641191783Srmacklem * can't be done at this time. 4642191783Srmacklem * VOP_ADVLOCK() - When a client holds a delegation, it can issue byte range 4643191783Srmacklem * locks locally in the client, which are not visible to the server. To 4644191783Srmacklem * deal with this, issuing of delegations for a vnode must be disabled 4645191783Srmacklem * and all delegations for the vnode recalled. This is done via the 4646191783Srmacklem * second function, using the VV_DISABLEDELEG vflag on the vnode. 4647191783Srmacklem */ 4648191783SrmacklemAPPLESTATIC void 4649191783Srmacklemnfsd_recalldelegation(vnode_t vp, NFSPROC_T *p) 4650191783Srmacklem{ 4651245909Sjhb time_t starttime; 4652191783Srmacklem int error; 4653191783Srmacklem 4654191783Srmacklem /* 4655191783Srmacklem * First, check to see if the server is currently running and it has 4656191783Srmacklem * been called for a regular file when issuing delegations. 4657191783Srmacklem */ 4658191783Srmacklem if (newnfs_numnfsd == 0 || vp->v_type != VREG || 4659191783Srmacklem nfsrv_issuedelegs == 0) 4660191783Srmacklem return; 4661191783Srmacklem 4662224083Szack KASSERT((NFSVOPISLOCKED(vp) != LK_EXCLUSIVE), ("vp %p is locked", vp)); 4663191783Srmacklem /* 4664211953Srmacklem * First, get a reference on the nfsv4rootfs_lock so that an 4665211953Srmacklem * exclusive lock cannot be acquired by another thread. 4666211953Srmacklem */ 4667211953Srmacklem NFSLOCKV4ROOTMUTEX(); 4668222389Srmacklem nfsv4_getref(&nfsv4rootfs_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); 4669211953Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4670211953Srmacklem 4671211953Srmacklem /* 4672191783Srmacklem * Now, call nfsrv_checkremove() in a loop while it returns 4673191783Srmacklem * NFSERR_DELAY. Return upon any other error or when timed out. 4674191783Srmacklem */ 4675245909Sjhb starttime = NFSD_MONOSEC; 4676191783Srmacklem do { 4677224081Szack if (NFSVOPLOCK(vp, LK_EXCLUSIVE) == 0) { 4678216510Srmacklem error = nfsrv_checkremove(vp, 0, p); 4679224082Szack NFSVOPUNLOCK(vp, 0); 4680216893Srmacklem } else 4681216510Srmacklem error = EPERM; 4682191783Srmacklem if (error == NFSERR_DELAY) { 4683245909Sjhb if (NFSD_MONOSEC - starttime > NFS_REMOVETIMEO) 4684211953Srmacklem break; 4685191783Srmacklem /* Sleep for a short period of time */ 4686207170Srmacklem (void) nfs_catnap(PZERO, 0, "nfsremove"); 4687191783Srmacklem } 4688191783Srmacklem } while (error == NFSERR_DELAY); 4689211953Srmacklem NFSLOCKV4ROOTMUTEX(); 4690211953Srmacklem nfsv4_relref(&nfsv4rootfs_lock); 4691211953Srmacklem NFSUNLOCKV4ROOTMUTEX(); 4692191783Srmacklem} 4693191783Srmacklem 4694191783SrmacklemAPPLESTATIC void 4695191783Srmacklemnfsd_disabledelegation(vnode_t vp, NFSPROC_T *p) 4696191783Srmacklem{ 4697191783Srmacklem 4698191783Srmacklem#ifdef VV_DISABLEDELEG 4699191783Srmacklem /* 4700191783Srmacklem * First, flag issuance of delegations disabled. 4701191783Srmacklem */ 4702191783Srmacklem atomic_set_long(&vp->v_vflag, VV_DISABLEDELEG); 4703191783Srmacklem#endif 4704191783Srmacklem 4705191783Srmacklem /* 4706191783Srmacklem * Then call nfsd_recalldelegation() to get rid of all extant 4707191783Srmacklem * delegations. 4708191783Srmacklem */ 4709191783Srmacklem nfsd_recalldelegation(vp, p); 4710191783Srmacklem} 4711191783Srmacklem 4712191783Srmacklem/* 4713191783Srmacklem * Check for conflicting locks, etc. and then get rid of delegations. 4714191783Srmacklem * (At one point I thought that I should get rid of delegations for any 4715191783Srmacklem * Setattr, since it could potentially disallow the I/O op (read or write) 4716191783Srmacklem * allowed by the delegation. However, Setattr Ops that aren't changing 4717191783Srmacklem * the size get a stateid of all 0s, so you can't tell if it is a delegation 4718191783Srmacklem * for the same client or a different one, so I decided to only get rid 4719191783Srmacklem * of delegations for other clients when the size is being changed.) 4720191783Srmacklem * In general, a Setattr can disable NFS I/O Ops that are outstanding, such 4721191783Srmacklem * as Write backs, even if there is no delegation, so it really isn't any 4722191783Srmacklem * different?) 4723191783Srmacklem */ 4724191783SrmacklemAPPLESTATIC int 4725191783Srmacklemnfsrv_checksetattr(vnode_t vp, struct nfsrv_descript *nd, 4726191783Srmacklem nfsv4stateid_t *stateidp, struct nfsvattr *nvap, nfsattrbit_t *attrbitp, 4727191783Srmacklem struct nfsexstuff *exp, NFSPROC_T *p) 4728191783Srmacklem{ 4729191783Srmacklem struct nfsstate st, *stp = &st; 4730191783Srmacklem struct nfslock lo, *lop = &lo; 4731191783Srmacklem int error = 0; 4732191783Srmacklem nfsquad_t clientid; 4733191783Srmacklem 4734191783Srmacklem if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SIZE)) { 4735191783Srmacklem stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS); 4736191783Srmacklem lop->lo_first = nvap->na_size; 4737191783Srmacklem } else { 4738191783Srmacklem stp->ls_flags = 0; 4739191783Srmacklem lop->lo_first = 0; 4740191783Srmacklem } 4741191783Srmacklem if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_OWNER) || 4742191783Srmacklem NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_OWNERGROUP) || 4743191783Srmacklem NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_MODE) || 4744191783Srmacklem NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_ACL)) 4745191783Srmacklem stp->ls_flags |= NFSLCK_SETATTR; 4746191783Srmacklem if (stp->ls_flags == 0) 4747224086Szack goto out; 4748191783Srmacklem lop->lo_end = NFS64BITSSET; 4749191783Srmacklem lop->lo_flags = NFSLCK_WRITE; 4750191783Srmacklem stp->ls_ownerlen = 0; 4751191783Srmacklem stp->ls_op = NULL; 4752191783Srmacklem stp->ls_uid = nd->nd_cred->cr_uid; 4753191783Srmacklem stp->ls_stateid.seqid = stateidp->seqid; 4754191783Srmacklem clientid.lval[0] = stp->ls_stateid.other[0] = stateidp->other[0]; 4755191783Srmacklem clientid.lval[1] = stp->ls_stateid.other[1] = stateidp->other[1]; 4756191783Srmacklem stp->ls_stateid.other[2] = stateidp->other[2]; 4757191783Srmacklem error = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid, 4758191783Srmacklem stateidp, exp, nd, p); 4759224086Szack 4760224086Szackout: 4761224086Szack NFSEXITCODE2(error, nd); 4762191783Srmacklem return (error); 4763191783Srmacklem} 4764191783Srmacklem 4765191783Srmacklem/* 4766191783Srmacklem * Check for a write delegation and do a CBGETATTR if there is one, updating 4767191783Srmacklem * the attributes, as required. 4768191783Srmacklem * Should I return an error if I can't get the attributes? (For now, I'll 4769191783Srmacklem * just return ok. 4770191783Srmacklem */ 4771191783SrmacklemAPPLESTATIC int 4772191783Srmacklemnfsrv_checkgetattr(struct nfsrv_descript *nd, vnode_t vp, 4773191783Srmacklem struct nfsvattr *nvap, nfsattrbit_t *attrbitp, struct ucred *cred, 4774191783Srmacklem NFSPROC_T *p) 4775191783Srmacklem{ 4776191783Srmacklem struct nfsstate *stp; 4777191783Srmacklem struct nfslockfile *lfp; 4778191783Srmacklem struct nfsclient *clp; 4779191783Srmacklem struct nfsvattr nva; 4780191783Srmacklem fhandle_t nfh; 4781224086Szack int error = 0; 4782191783Srmacklem nfsattrbit_t cbbits; 4783191783Srmacklem u_quad_t delegfilerev; 4784191783Srmacklem 4785191783Srmacklem NFSCBGETATTR_ATTRBIT(attrbitp, &cbbits); 4786191783Srmacklem if (!NFSNONZERO_ATTRBIT(&cbbits)) 4787224086Szack goto out; 4788191783Srmacklem 4789191783Srmacklem /* 4790191783Srmacklem * Get the lock file structure. 4791191783Srmacklem * (A return of -1 means no associated state, so return ok.) 4792191783Srmacklem */ 4793191783Srmacklem error = nfsrv_getlockfh(vp, NFSLCK_CHECK, NULL, &nfh, p); 4794191783Srmacklem NFSLOCKSTATE(); 4795191783Srmacklem if (!error) 4796205941Srmacklem error = nfsrv_getlockfile(NFSLCK_CHECK, NULL, &lfp, &nfh, 0); 4797191783Srmacklem if (error) { 4798191783Srmacklem NFSUNLOCKSTATE(); 4799191783Srmacklem if (error == -1) 4800224086Szack error = 0; 4801224086Szack goto out; 4802191783Srmacklem } 4803191783Srmacklem 4804191783Srmacklem /* 4805191783Srmacklem * Now, look for a write delegation. 4806191783Srmacklem */ 4807191783Srmacklem LIST_FOREACH(stp, &lfp->lf_deleg, ls_file) { 4808191783Srmacklem if (stp->ls_flags & NFSLCK_DELEGWRITE) 4809191783Srmacklem break; 4810191783Srmacklem } 4811191783Srmacklem if (stp == LIST_END(&lfp->lf_deleg)) { 4812191783Srmacklem NFSUNLOCKSTATE(); 4813224086Szack goto out; 4814191783Srmacklem } 4815191783Srmacklem clp = stp->ls_clp; 4816191783Srmacklem delegfilerev = stp->ls_filerev; 4817191783Srmacklem 4818191783Srmacklem /* 4819191783Srmacklem * If the Write delegation was issued as a part of this Compound RPC 4820191783Srmacklem * or if we have an Implied Clientid (used in a previous Op in this 4821191783Srmacklem * compound) and it is the client the delegation was issued to, 4822191783Srmacklem * just return ok. 4823191783Srmacklem * I also assume that it is from the same client iff the network 4824191783Srmacklem * host IP address is the same as the callback address. (Not 4825191783Srmacklem * exactly correct by the RFC, but avoids a lot of Getattr 4826191783Srmacklem * callbacks.) 4827191783Srmacklem */ 4828191783Srmacklem if (nd->nd_compref == stp->ls_compref || 4829191783Srmacklem ((nd->nd_flag & ND_IMPLIEDCLID) && 4830191783Srmacklem clp->lc_clientid.qval == nd->nd_clientid.qval) || 4831191783Srmacklem nfsaddr2_match(clp->lc_req.nr_nam, nd->nd_nam)) { 4832191783Srmacklem NFSUNLOCKSTATE(); 4833224086Szack goto out; 4834191783Srmacklem } 4835191783Srmacklem 4836191783Srmacklem /* 4837191783Srmacklem * We are now done with the delegation state structure, 4838191783Srmacklem * so the statelock can be released and we can now tsleep(). 4839191783Srmacklem */ 4840191783Srmacklem 4841191783Srmacklem /* 4842191783Srmacklem * Now, we must do the CB Getattr callback, to see if Change or Size 4843191783Srmacklem * has changed. 4844191783Srmacklem */ 4845191783Srmacklem if (clp->lc_expiry >= NFSD_MONOSEC) { 4846191783Srmacklem NFSUNLOCKSTATE(); 4847191783Srmacklem NFSVNO_ATTRINIT(&nva); 4848191783Srmacklem nva.na_filerev = NFS64BITSSET; 4849191783Srmacklem error = nfsrv_docallback(clp, NFSV4OP_CBGETATTR, NULL, 4850191783Srmacklem 0, &nfh, &nva, &cbbits, p); 4851191783Srmacklem if (!error) { 4852191783Srmacklem if ((nva.na_filerev != NFS64BITSSET && 4853191783Srmacklem nva.na_filerev > delegfilerev) || 4854191783Srmacklem (NFSVNO_ISSETSIZE(&nva) && 4855191783Srmacklem nva.na_size != nvap->na_size)) { 4856191783Srmacklem nfsvno_updfilerev(vp, nvap, cred, p); 4857191783Srmacklem if (NFSVNO_ISSETSIZE(&nva)) 4858191783Srmacklem nvap->na_size = nva.na_size; 4859191783Srmacklem } 4860191783Srmacklem } 4861191783Srmacklem } else { 4862191783Srmacklem NFSUNLOCKSTATE(); 4863191783Srmacklem } 4864224086Szack error = 0; 4865224086Szack 4866224086Szackout: 4867224086Szack NFSEXITCODE2(error, nd); 4868224086Szack return (error); 4869191783Srmacklem} 4870191783Srmacklem 4871191783Srmacklem/* 4872191783Srmacklem * This function looks for openowners that haven't had any opens for 4873191783Srmacklem * a while and throws them away. Called by an nfsd when NFSNSF_NOOPENS 4874191783Srmacklem * is set. 4875191783Srmacklem */ 4876191783SrmacklemAPPLESTATIC void 4877191783Srmacklemnfsrv_throwawayopens(NFSPROC_T *p) 4878191783Srmacklem{ 4879191783Srmacklem struct nfsclient *clp, *nclp; 4880191783Srmacklem struct nfsstate *stp, *nstp; 4881191783Srmacklem int i; 4882191783Srmacklem 4883191783Srmacklem NFSLOCKSTATE(); 4884191783Srmacklem nfsrv_stablefirst.nsf_flags &= ~NFSNSF_NOOPENS; 4885191783Srmacklem /* 4886191783Srmacklem * For each client... 4887191783Srmacklem */ 4888191783Srmacklem for (i = 0; i < NFSCLIENTHASHSIZE; i++) { 4889191783Srmacklem LIST_FOREACH_SAFE(clp, &nfsclienthash[i], lc_hash, nclp) { 4890191783Srmacklem LIST_FOREACH_SAFE(stp, &clp->lc_open, ls_list, nstp) { 4891191783Srmacklem if (LIST_EMPTY(&stp->ls_open) && 4892191783Srmacklem (stp->ls_noopens > NFSNOOPEN || 4893191783Srmacklem (nfsrv_openpluslock * 2) > 4894191783Srmacklem NFSRV_V4STATELIMIT)) 4895191783Srmacklem nfsrv_freeopenowner(stp, 0, p); 4896191783Srmacklem } 4897191783Srmacklem } 4898191783Srmacklem } 4899191783Srmacklem NFSUNLOCKSTATE(); 4900191783Srmacklem} 4901191783Srmacklem 4902191783Srmacklem/* 4903191783Srmacklem * This function checks to see if the credentials are the same. 4904191783Srmacklem * Returns 1 for not same, 0 otherwise. 4905191783Srmacklem */ 4906191783Srmacklemstatic int 4907191783Srmacklemnfsrv_notsamecredname(struct nfsrv_descript *nd, struct nfsclient *clp) 4908191783Srmacklem{ 4909191783Srmacklem 4910191783Srmacklem if (nd->nd_flag & ND_GSS) { 4911191783Srmacklem if (!(clp->lc_flags & LCL_GSS)) 4912191783Srmacklem return (1); 4913191783Srmacklem if (clp->lc_flags & LCL_NAME) { 4914191783Srmacklem if (nd->nd_princlen != clp->lc_namelen || 4915191783Srmacklem NFSBCMP(nd->nd_principal, clp->lc_name, 4916191783Srmacklem clp->lc_namelen)) 4917191783Srmacklem return (1); 4918191783Srmacklem else 4919191783Srmacklem return (0); 4920191783Srmacklem } 4921191783Srmacklem if (nd->nd_cred->cr_uid == clp->lc_uid) 4922191783Srmacklem return (0); 4923191783Srmacklem else 4924191783Srmacklem return (1); 4925191783Srmacklem } else if (clp->lc_flags & LCL_GSS) 4926191783Srmacklem return (1); 4927191783Srmacklem /* 4928191783Srmacklem * For AUTH_SYS, allow the same uid or root. (This is underspecified 4929191783Srmacklem * in RFC3530, which talks about principals, but doesn't say anything 4930191783Srmacklem * about uids for AUTH_SYS.) 4931191783Srmacklem */ 4932191783Srmacklem if (nd->nd_cred->cr_uid == clp->lc_uid || nd->nd_cred->cr_uid == 0) 4933191783Srmacklem return (0); 4934191783Srmacklem else 4935191783Srmacklem return (1); 4936191783Srmacklem} 4937191783Srmacklem 4938191783Srmacklem/* 4939191783Srmacklem * Calculate the lease expiry time. 4940191783Srmacklem */ 4941191783Srmacklemstatic time_t 4942191783Srmacklemnfsrv_leaseexpiry(void) 4943191783Srmacklem{ 4944191783Srmacklem 4945191783Srmacklem if (nfsrv_stablefirst.nsf_eograce > NFSD_MONOSEC) 4946191783Srmacklem return (NFSD_MONOSEC + 2 * (nfsrv_lease + NFSRV_LEASEDELTA)); 4947191783Srmacklem return (NFSD_MONOSEC + nfsrv_lease + NFSRV_LEASEDELTA); 4948191783Srmacklem} 4949191783Srmacklem 4950191783Srmacklem/* 4951191783Srmacklem * Delay the delegation timeout as far as ls_delegtimelimit, as required. 4952191783Srmacklem */ 4953191783Srmacklemstatic void 4954191783Srmacklemnfsrv_delaydelegtimeout(struct nfsstate *stp) 4955191783Srmacklem{ 4956191783Srmacklem 4957191783Srmacklem if ((stp->ls_flags & NFSLCK_DELEGRECALL) == 0) 4958191783Srmacklem return; 4959191783Srmacklem 4960191783Srmacklem if ((stp->ls_delegtime + 15) > NFSD_MONOSEC && 4961191783Srmacklem stp->ls_delegtime < stp->ls_delegtimelimit) { 4962191783Srmacklem stp->ls_delegtime += nfsrv_lease; 4963191783Srmacklem if (stp->ls_delegtime > stp->ls_delegtimelimit) 4964191783Srmacklem stp->ls_delegtime = stp->ls_delegtimelimit; 4965191783Srmacklem } 4966191783Srmacklem} 4967191783Srmacklem 4968191783Srmacklem/* 4969205941Srmacklem * This function checks to see if there is any other state associated 4970205941Srmacklem * with the openowner for this Open. 4971205941Srmacklem * It returns 1 if there is no other state, 0 otherwise. 4972191783Srmacklem */ 4973205941Srmacklemstatic int 4974205941Srmacklemnfsrv_nootherstate(struct nfsstate *stp) 4975191783Srmacklem{ 4976205941Srmacklem struct nfsstate *tstp; 4977191783Srmacklem 4978205941Srmacklem LIST_FOREACH(tstp, &stp->ls_openowner->ls_open, ls_list) { 4979205941Srmacklem if (tstp != stp || !LIST_EMPTY(&tstp->ls_lock)) 4980205941Srmacklem return (0); 4981191783Srmacklem } 4982205941Srmacklem return (1); 4983205941Srmacklem} 4984191783Srmacklem 4985205941Srmacklem/* 4986205941Srmacklem * Create a list of lock deltas (changes to local byte range locking 4987205941Srmacklem * that can be rolled back using the list) and apply the changes via 4988205941Srmacklem * nfsvno_advlock(). Optionally, lock the list. It is expected that either 4989205941Srmacklem * the rollback or update function will be called after this. 4990205941Srmacklem * It returns an error (and rolls back, as required), if any nfsvno_advlock() 4991205941Srmacklem * call fails. If it returns an error, it will unlock the list. 4992205941Srmacklem */ 4993205941Srmacklemstatic int 4994205941Srmacklemnfsrv_locallock(vnode_t vp, struct nfslockfile *lfp, int flags, 4995205941Srmacklem uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p) 4996205941Srmacklem{ 4997205941Srmacklem struct nfslock *lop, *nlop; 4998205941Srmacklem int error = 0; 4999191783Srmacklem 5000205941Srmacklem /* Loop through the list of locks. */ 5001205941Srmacklem lop = LIST_FIRST(&lfp->lf_locallock); 5002205941Srmacklem while (first < end && lop != NULL) { 5003205941Srmacklem nlop = LIST_NEXT(lop, lo_lckowner); 5004205941Srmacklem if (first >= lop->lo_end) { 5005205941Srmacklem /* not there yet */ 5006205941Srmacklem lop = nlop; 5007205941Srmacklem } else if (first < lop->lo_first) { 5008205941Srmacklem /* new one starts before entry in list */ 5009205941Srmacklem if (end <= lop->lo_first) { 5010205941Srmacklem /* no overlap between old and new */ 5011205941Srmacklem error = nfsrv_dolocal(vp, lfp, flags, 5012205941Srmacklem NFSLCK_UNLOCK, first, end, cfp, p); 5013205941Srmacklem if (error != 0) 5014205941Srmacklem break; 5015205941Srmacklem first = end; 5016205941Srmacklem } else { 5017205941Srmacklem /* handle fragment overlapped with new one */ 5018205941Srmacklem error = nfsrv_dolocal(vp, lfp, flags, 5019205941Srmacklem NFSLCK_UNLOCK, first, lop->lo_first, cfp, 5020205941Srmacklem p); 5021205941Srmacklem if (error != 0) 5022205941Srmacklem break; 5023205941Srmacklem first = lop->lo_first; 5024205941Srmacklem } 5025205941Srmacklem } else { 5026205941Srmacklem /* new one overlaps this entry in list */ 5027205941Srmacklem if (end <= lop->lo_end) { 5028205941Srmacklem /* overlaps all of new one */ 5029205941Srmacklem error = nfsrv_dolocal(vp, lfp, flags, 5030205941Srmacklem lop->lo_flags, first, end, cfp, p); 5031205941Srmacklem if (error != 0) 5032205941Srmacklem break; 5033205941Srmacklem first = end; 5034205941Srmacklem } else { 5035205941Srmacklem /* handle fragment overlapped with new one */ 5036205941Srmacklem error = nfsrv_dolocal(vp, lfp, flags, 5037205941Srmacklem lop->lo_flags, first, lop->lo_end, cfp, p); 5038205941Srmacklem if (error != 0) 5039205941Srmacklem break; 5040205941Srmacklem first = lop->lo_end; 5041205941Srmacklem lop = nlop; 5042205941Srmacklem } 5043205941Srmacklem } 5044191783Srmacklem } 5045205941Srmacklem if (first < end && error == 0) 5046205941Srmacklem /* handle fragment past end of list */ 5047205941Srmacklem error = nfsrv_dolocal(vp, lfp, flags, NFSLCK_UNLOCK, first, 5048205941Srmacklem end, cfp, p); 5049224086Szack 5050224086Szack NFSEXITCODE(error); 5051205941Srmacklem return (error); 5052205941Srmacklem} 5053191783Srmacklem 5054205941Srmacklem/* 5055205941Srmacklem * Local lock unlock. Unlock all byte ranges that are no longer locked 5056213712Srmacklem * by NFSv4. To do this, unlock any subranges of first-->end that 5057213712Srmacklem * do not overlap with the byte ranges of any lock in the lfp->lf_lock 5058213712Srmacklem * list. This list has all locks for the file held by other 5059213712Srmacklem * <clientid, lockowner> tuples. The list is ordered by increasing 5060213712Srmacklem * lo_first value, but may have entries that overlap each other, for 5061213712Srmacklem * the case of read locks. 5062205941Srmacklem */ 5063205941Srmacklemstatic void 5064205941Srmacklemnfsrv_localunlock(vnode_t vp, struct nfslockfile *lfp, uint64_t init_first, 5065205941Srmacklem uint64_t init_end, NFSPROC_T *p) 5066205941Srmacklem{ 5067205941Srmacklem struct nfslock *lop; 5068213712Srmacklem uint64_t first, end, prevfirst; 5069205941Srmacklem 5070205941Srmacklem first = init_first; 5071205941Srmacklem end = init_end; 5072205941Srmacklem while (first < init_end) { 5073205941Srmacklem /* Loop through all nfs locks, adjusting first and end */ 5074213712Srmacklem prevfirst = 0; 5075205941Srmacklem LIST_FOREACH(lop, &lfp->lf_lock, lo_lckfile) { 5076213712Srmacklem KASSERT(prevfirst <= lop->lo_first, 5077213712Srmacklem ("nfsv4 locks out of order")); 5078213712Srmacklem KASSERT(lop->lo_first < lop->lo_end, 5079213712Srmacklem ("nfsv4 bogus lock")); 5080213712Srmacklem prevfirst = lop->lo_first; 5081205941Srmacklem if (first >= lop->lo_first && 5082205941Srmacklem first < lop->lo_end) 5083213712Srmacklem /* 5084213712Srmacklem * Overlaps with initial part, so trim 5085213712Srmacklem * off that initial part by moving first past 5086213712Srmacklem * it. 5087213712Srmacklem */ 5088205941Srmacklem first = lop->lo_end; 5089205941Srmacklem else if (end > lop->lo_first && 5090213712Srmacklem lop->lo_first > first) { 5091213712Srmacklem /* 5092213712Srmacklem * This lock defines the end of the 5093213712Srmacklem * segment to unlock, so set end to the 5094213712Srmacklem * start of it and break out of the loop. 5095213712Srmacklem */ 5096205941Srmacklem end = lop->lo_first; 5097213712Srmacklem break; 5098213712Srmacklem } 5099205941Srmacklem if (first >= end) 5100213712Srmacklem /* 5101213712Srmacklem * There is no segment left to do, so 5102213712Srmacklem * break out of this loop and then exit 5103213712Srmacklem * the outer while() since first will be set 5104213712Srmacklem * to end, which must equal init_end here. 5105213712Srmacklem */ 5106205941Srmacklem break; 5107191783Srmacklem } 5108205941Srmacklem if (first < end) { 5109205941Srmacklem /* Unlock this segment */ 5110205941Srmacklem (void) nfsrv_dolocal(vp, lfp, NFSLCK_UNLOCK, 5111205941Srmacklem NFSLCK_READ, first, end, NULL, p); 5112205941Srmacklem nfsrv_locallock_commit(lfp, NFSLCK_UNLOCK, 5113205941Srmacklem first, end); 5114191783Srmacklem } 5115213712Srmacklem /* 5116213712Srmacklem * Now move past this segment and look for any further 5117213712Srmacklem * segment in the range, if there is one. 5118213712Srmacklem */ 5119205941Srmacklem first = end; 5120205941Srmacklem end = init_end; 5121191783Srmacklem } 5122191783Srmacklem} 5123191783Srmacklem 5124191783Srmacklem/* 5125205941Srmacklem * Do the local lock operation and update the rollback list, as required. 5126205941Srmacklem * Perform the rollback and return the error if nfsvno_advlock() fails. 5127191783Srmacklem */ 5128191783Srmacklemstatic int 5129205941Srmacklemnfsrv_dolocal(vnode_t vp, struct nfslockfile *lfp, int flags, int oldflags, 5130205941Srmacklem uint64_t first, uint64_t end, struct nfslockconflict *cfp, NFSPROC_T *p) 5131191783Srmacklem{ 5132205941Srmacklem struct nfsrollback *rlp; 5133224086Szack int error = 0, ltype, oldltype; 5134191783Srmacklem 5135205941Srmacklem if (flags & NFSLCK_WRITE) 5136205941Srmacklem ltype = F_WRLCK; 5137205941Srmacklem else if (flags & NFSLCK_READ) 5138205941Srmacklem ltype = F_RDLCK; 5139205941Srmacklem else 5140205941Srmacklem ltype = F_UNLCK; 5141205941Srmacklem if (oldflags & NFSLCK_WRITE) 5142205941Srmacklem oldltype = F_WRLCK; 5143205941Srmacklem else if (oldflags & NFSLCK_READ) 5144205941Srmacklem oldltype = F_RDLCK; 5145205941Srmacklem else 5146205941Srmacklem oldltype = F_UNLCK; 5147205941Srmacklem if (ltype == oldltype || (oldltype == F_WRLCK && ltype == F_RDLCK)) 5148205941Srmacklem /* nothing to do */ 5149224086Szack goto out; 5150205941Srmacklem error = nfsvno_advlock(vp, ltype, first, end, p); 5151205941Srmacklem if (error != 0) { 5152205941Srmacklem if (cfp != NULL) { 5153205941Srmacklem cfp->cl_clientid.lval[0] = 0; 5154205941Srmacklem cfp->cl_clientid.lval[1] = 0; 5155205941Srmacklem cfp->cl_first = 0; 5156205941Srmacklem cfp->cl_end = NFS64BITSSET; 5157205941Srmacklem cfp->cl_flags = NFSLCK_WRITE; 5158205941Srmacklem cfp->cl_ownerlen = 5; 5159205941Srmacklem NFSBCOPY("LOCAL", cfp->cl_owner, 5); 5160205941Srmacklem } 5161205941Srmacklem nfsrv_locallock_rollback(vp, lfp, p); 5162205941Srmacklem } else if (ltype != F_UNLCK) { 5163205941Srmacklem rlp = malloc(sizeof (struct nfsrollback), M_NFSDROLLBACK, 5164205941Srmacklem M_WAITOK); 5165205941Srmacklem rlp->rlck_first = first; 5166205941Srmacklem rlp->rlck_end = end; 5167205941Srmacklem rlp->rlck_type = oldltype; 5168205941Srmacklem LIST_INSERT_HEAD(&lfp->lf_rollback, rlp, rlck_list); 5169191783Srmacklem } 5170224086Szack 5171224086Szackout: 5172224086Szack NFSEXITCODE(error); 5173205941Srmacklem return (error); 5174191783Srmacklem} 5175191783Srmacklem 5176205941Srmacklem/* 5177205941Srmacklem * Roll back local lock changes and free up the rollback list. 5178205941Srmacklem */ 5179205941Srmacklemstatic void 5180205941Srmacklemnfsrv_locallock_rollback(vnode_t vp, struct nfslockfile *lfp, NFSPROC_T *p) 5181205941Srmacklem{ 5182205941Srmacklem struct nfsrollback *rlp, *nrlp; 5183205941Srmacklem 5184205941Srmacklem LIST_FOREACH_SAFE(rlp, &lfp->lf_rollback, rlck_list, nrlp) { 5185205941Srmacklem (void) nfsvno_advlock(vp, rlp->rlck_type, rlp->rlck_first, 5186205941Srmacklem rlp->rlck_end, p); 5187205941Srmacklem free(rlp, M_NFSDROLLBACK); 5188205941Srmacklem } 5189205941Srmacklem LIST_INIT(&lfp->lf_rollback); 5190205941Srmacklem} 5191205941Srmacklem 5192205941Srmacklem/* 5193205941Srmacklem * Update local lock list and delete rollback list (ie now committed to the 5194205941Srmacklem * local locks). Most of the work is done by the internal function. 5195205941Srmacklem */ 5196205941Srmacklemstatic void 5197205941Srmacklemnfsrv_locallock_commit(struct nfslockfile *lfp, int flags, uint64_t first, 5198205941Srmacklem uint64_t end) 5199205941Srmacklem{ 5200205941Srmacklem struct nfsrollback *rlp, *nrlp; 5201205941Srmacklem struct nfslock *new_lop, *other_lop; 5202205941Srmacklem 5203205941Srmacklem new_lop = malloc(sizeof (struct nfslock), M_NFSDLOCK, M_WAITOK); 5204205941Srmacklem if (flags & (NFSLCK_READ | NFSLCK_WRITE)) 5205205941Srmacklem other_lop = malloc(sizeof (struct nfslock), M_NFSDLOCK, 5206205941Srmacklem M_WAITOK); 5207205941Srmacklem else 5208205941Srmacklem other_lop = NULL; 5209205941Srmacklem new_lop->lo_flags = flags; 5210205941Srmacklem new_lop->lo_first = first; 5211205941Srmacklem new_lop->lo_end = end; 5212205941Srmacklem nfsrv_updatelock(NULL, &new_lop, &other_lop, lfp); 5213205941Srmacklem if (new_lop != NULL) 5214205941Srmacklem free(new_lop, M_NFSDLOCK); 5215205941Srmacklem if (other_lop != NULL) 5216205941Srmacklem free(other_lop, M_NFSDLOCK); 5217205941Srmacklem 5218205941Srmacklem /* and get rid of the rollback list */ 5219205941Srmacklem LIST_FOREACH_SAFE(rlp, &lfp->lf_rollback, rlck_list, nrlp) 5220205941Srmacklem free(rlp, M_NFSDROLLBACK); 5221205941Srmacklem LIST_INIT(&lfp->lf_rollback); 5222205941Srmacklem} 5223205941Srmacklem 5224205941Srmacklem/* 5225205941Srmacklem * Lock the struct nfslockfile for local lock updating. 5226205941Srmacklem */ 5227205941Srmacklemstatic void 5228205941Srmacklemnfsrv_locklf(struct nfslockfile *lfp) 5229205941Srmacklem{ 5230205941Srmacklem int gotlock; 5231205941Srmacklem 5232205941Srmacklem /* lf_usecount ensures *lfp won't be free'd */ 5233205941Srmacklem lfp->lf_usecount++; 5234205941Srmacklem do { 5235205941Srmacklem gotlock = nfsv4_lock(&lfp->lf_locallock_lck, 1, NULL, 5236222389Srmacklem NFSSTATEMUTEXPTR, NULL); 5237205941Srmacklem } while (gotlock == 0); 5238205941Srmacklem lfp->lf_usecount--; 5239205941Srmacklem} 5240205941Srmacklem 5241205941Srmacklem/* 5242205941Srmacklem * Unlock the struct nfslockfile after local lock updating. 5243205941Srmacklem */ 5244205941Srmacklemstatic void 5245205941Srmacklemnfsrv_unlocklf(struct nfslockfile *lfp) 5246205941Srmacklem{ 5247205941Srmacklem 5248205941Srmacklem nfsv4_unlock(&lfp->lf_locallock_lck, 0); 5249205941Srmacklem} 5250205941Srmacklem 5251220530Srmacklem/* 5252220530Srmacklem * Clear out all state for the NFSv4 server. 5253220530Srmacklem * Must be called by a thread that can sleep when no nfsds are running. 5254220530Srmacklem */ 5255220530Srmacklemvoid 5256220530Srmacklemnfsrv_throwawayallstate(NFSPROC_T *p) 5257220530Srmacklem{ 5258220530Srmacklem struct nfsclient *clp, *nclp; 5259220530Srmacklem struct nfslockfile *lfp, *nlfp; 5260220530Srmacklem int i; 5261220530Srmacklem 5262220530Srmacklem /* 5263220530Srmacklem * For each client, clean out the state and then free the structure. 5264220530Srmacklem */ 5265220530Srmacklem for (i = 0; i < NFSCLIENTHASHSIZE; i++) { 5266220530Srmacklem LIST_FOREACH_SAFE(clp, &nfsclienthash[i], lc_hash, nclp) { 5267220530Srmacklem nfsrv_cleanclient(clp, p); 5268220530Srmacklem nfsrv_freedeleglist(&clp->lc_deleg); 5269220530Srmacklem nfsrv_freedeleglist(&clp->lc_olddeleg); 5270220530Srmacklem free(clp, M_NFSDCLIENT); 5271220530Srmacklem } 5272220530Srmacklem } 5273220530Srmacklem 5274220530Srmacklem /* 5275220530Srmacklem * Also, free up any remaining lock file structures. 5276220530Srmacklem */ 5277220530Srmacklem for (i = 0; i < NFSLOCKHASHSIZE; i++) { 5278220530Srmacklem LIST_FOREACH_SAFE(lfp, &nfslockhash[i], lf_hash, nlfp) { 5279220530Srmacklem printf("nfsd unload: fnd a lock file struct\n"); 5280220530Srmacklem nfsrv_freenfslockfile(lfp); 5281220530Srmacklem } 5282220530Srmacklem } 5283220530Srmacklem} 5284220530Srmacklem 5285