]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/fs/nfsclient/nfs_clstate.c
MFC: r222389
[FreeBSD/stable/8.git] / sys / fs / nfsclient / nfs_clstate.c
1 /*-
2  * Copyright (c) 2009 Rick Macklem, University of Guelph
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 /*
32  * These functions implement the client side state handling for NFSv4.
33  * NFSv4 state handling:
34  * - A lockowner is used to determine lock contention, so it
35  *   corresponds directly to a Posix pid. (1 to 1 mapping)
36  * - The correct granularity of an OpenOwner is not nearly so
37  *   obvious. An OpenOwner does the following:
38  *   - provides a serial sequencing of Open/Close/Lock-with-new-lockowner
39  *   - is used to check for Open/Share contention (not applicable to
40  *     this client, since all Opens are Deny_None)
41  *   As such, I considered both extreme.
42  *   1 OpenOwner per ClientID - Simple to manage, but fully serializes
43  *   all Open, Close and Lock (with a new lockowner) Ops.
44  *   1 OpenOwner for each Open - This one results in an OpenConfirm for
45  *   every Open, for most servers.
46  *   So, I chose to use the same mapping as I did for LockOwnwers.
47  *   The main concern here is that you can end up with multiple Opens
48  *   for the same File Handle, but on different OpenOwners (opens
49  *   inherited from parents, grandparents...) and you do not know
50  *   which of these the vnodeop close applies to. This is handled by
51  *   delaying the Close Op(s) until all of the Opens have been closed.
52  *   (It is not yet obvious if this is the correct granularity.)
53  * - How the code handles serialization:
54  *   - For the ClientId, it uses an exclusive lock while getting its
55  *     SetClientId and during recovery. Otherwise, it uses a shared
56  *     lock via a reference count.
57  *   - For the rest of the data structures, it uses an SMP mutex
58  *     (once the nfs client is SMP safe) and doesn't sleep while
59  *     manipulating the linked lists.
60  *   - The serialization of Open/Close/Lock/LockU falls out in the
61  *     "wash", since OpenOwners and LockOwners are both mapped from
62  *     Posix pid. In other words, there is only one Posix pid using
63  *     any given owner, so that owner is serialized. (If you change
64  *     the granularity of the OpenOwner, then code must be added to
65  *     serialize Ops on the OpenOwner.)
66  * - When to get rid of OpenOwners and LockOwners.
67  *   - When a process exits, it calls nfscl_cleanup(), which goes
68  *     through the client list looking for all Open and Lock Owners.
69  *     When one is found, it is marked "defunct" or in the case of
70  *     an OpenOwner without any Opens, freed.
71  *     The renew thread scans for defunct Owners and gets rid of them,
72  *     if it can. The LockOwners will also be deleted when the
73  *     associated Open is closed.
74  *   - If the LockU or Close Op(s) fail during close in a way
75  *     that could be recovered upon retry, they are relinked to the
76  *     ClientId's defunct open list and retried by the renew thread
77  *     until they succeed or an unmount/recovery occurs.
78  *     (Since we are done with them, they do not need to be recovered.)
79  */
80
81 #ifndef APPLEKEXT
82 #include <fs/nfs/nfsport.h>
83
84 /*
85  * Global variables
86  */
87 extern struct nfsstats newnfsstats;
88 extern struct nfsreqhead nfsd_reqq;
89 NFSREQSPINLOCK;
90 NFSCLSTATEMUTEX;
91 int nfscl_inited = 0;
92 struct nfsclhead nfsclhead;     /* Head of clientid list */
93 int nfscl_deleghighwater = NFSCLDELEGHIGHWATER;
94 #endif  /* !APPLEKEXT */
95
96 static int nfscl_delegcnt = 0;
97 static int nfscl_getopen(struct nfsclownerhead *, u_int8_t *, int, u_int8_t *,
98     NFSPROC_T *, u_int32_t, struct nfsclowner **, struct nfsclopen **);
99 static void nfscl_clrelease(struct nfsclclient *);
100 static void nfscl_cleanclient(struct nfsclclient *);
101 static void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
102     struct ucred *, NFSPROC_T *);
103 static int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
104     struct nfsmount *, struct ucred *, NFSPROC_T *);
105 static void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
106 static void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
107     struct nfscllock *, int);
108 static int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
109     struct nfscllock **, int);
110 static void nfscl_delegreturnall(struct nfsclclient *, NFSPROC_T *);
111 static u_int32_t nfscl_nextcbident(void);
112 static mount_t nfscl_getmnt(u_int32_t);
113 static struct nfscldeleg *nfscl_finddeleg(struct nfsclclient *, u_int8_t *,
114     int);
115 static int nfscl_checkconflict(struct nfscllockownerhead *, struct nfscllock *,
116     u_int8_t *, struct nfscllock **);
117 static void nfscl_freelockowner(struct nfscllockowner *, int);
118 static void nfscl_freealllocks(struct nfscllockownerhead *, int);
119 static int nfscl_localconflict(struct nfsclclient *, u_int8_t *, int,
120     struct nfscllock *, u_int8_t *, struct nfscldeleg *, struct nfscllock **);
121 static void nfscl_newopen(struct nfsclclient *, struct nfscldeleg *,
122     struct nfsclowner **, struct nfsclowner **, struct nfsclopen **,
123     struct nfsclopen **, u_int8_t *, u_int8_t *, int, int *);
124 static int nfscl_moveopen(vnode_t , struct nfsclclient *,
125     struct nfsmount *, struct nfsclopen *, struct nfsclowner *,
126     struct nfscldeleg *, struct ucred *, NFSPROC_T *);
127 static void nfscl_totalrecall(struct nfsclclient *);
128 static int nfscl_relock(vnode_t , struct nfsclclient *, struct nfsmount *,
129     struct nfscllockowner *, struct nfscllock *, struct ucred *, NFSPROC_T *);
130 static int nfscl_tryopen(struct nfsmount *, vnode_t , u_int8_t *, int,
131     u_int8_t *, int, u_int32_t, struct nfsclopen *, u_int8_t *, int,
132     struct nfscldeleg **, int, u_int32_t, struct ucred *, NFSPROC_T *);
133 static int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
134     int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short,
135     struct ucred *, NFSPROC_T *);
136 static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
137     struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
138 static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
139 static int nfscl_errmap(struct nfsrv_descript *);
140 static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
141 static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
142     struct nfscldeleg *, vnode_t, struct ucred *, NFSPROC_T *, int);
143 static void nfscl_freeopenowner(struct nfsclowner *, int);
144 static void nfscl_cleandeleg(struct nfscldeleg *);
145 static int nfscl_trydelegreturn(struct nfscldeleg *, struct ucred *,
146     struct nfsmount *, NFSPROC_T *);
147
148 static short nfscberr_null[] = {
149         0,
150         0,
151 };
152
153 static short nfscberr_getattr[] = {
154         NFSERR_RESOURCE,
155         NFSERR_BADHANDLE,
156         NFSERR_BADXDR,
157         NFSERR_RESOURCE,
158         NFSERR_SERVERFAULT,
159         0,
160 };
161
162 static short nfscberr_recall[] = {
163         NFSERR_RESOURCE,
164         NFSERR_BADHANDLE,
165         NFSERR_BADSTATEID,
166         NFSERR_BADXDR,
167         NFSERR_RESOURCE,
168         NFSERR_SERVERFAULT,
169         0,
170 };
171
172 static short *nfscl_cberrmap[] = {
173         nfscberr_null,
174         nfscberr_null,
175         nfscberr_null,
176         nfscberr_getattr,
177         nfscberr_recall
178 };
179
180 #define NETFAMILY(clp) \
181                 (((clp)->nfsc_flags & NFSCLFLAGS_AFINET6) ? AF_INET6 : AF_INET)
182
183 /*
184  * Called for an open operation.
185  * If the nfhp argument is NULL, just get an openowner.
186  */
187 APPLESTATIC int
188 nfscl_open(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t amode, int usedeleg,
189     struct ucred *cred, NFSPROC_T *p, struct nfsclowner **owpp,
190     struct nfsclopen **opp, int *newonep, int *retp, int lockit)
191 {
192         struct nfsclclient *clp;
193         struct nfsclowner *owp, *nowp;
194         struct nfsclopen *op = NULL, *nop = NULL;
195         struct nfscldeleg *dp;
196         struct nfsclownerhead *ohp;
197         u_int8_t own[NFSV4CL_LOCKNAMELEN];
198         int ret;
199
200         if (newonep != NULL)
201                 *newonep = 0;
202         if (opp != NULL)
203                 *opp = NULL;
204         if (owpp != NULL)
205                 *owpp = NULL;
206
207         /*
208          * Might need one or both of these, so MALLOC them now, to
209          * avoid a tsleep() in MALLOC later.
210          */
211         MALLOC(nowp, struct nfsclowner *, sizeof (struct nfsclowner),
212             M_NFSCLOWNER, M_WAITOK);
213         if (nfhp != NULL)
214             MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
215                 fhlen - 1, M_NFSCLOPEN, M_WAITOK);
216         ret = nfscl_getcl(vp, cred, p, &clp);
217         if (ret != 0) {
218                 FREE((caddr_t)nowp, M_NFSCLOWNER);
219                 if (nop != NULL)
220                         FREE((caddr_t)nop, M_NFSCLOPEN);
221                 return (ret);
222         }
223
224         /*
225          * Get the Open iff it already exists.
226          * If none found, add the new one or return error, depending upon
227          * "create".
228          */
229         nfscl_filllockowner(p, own);
230         NFSLOCKCLSTATE();
231         dp = NULL;
232         /* First check the delegation list */
233         if (nfhp != NULL && usedeleg) {
234                 LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
235                         if (dp->nfsdl_fhlen == fhlen &&
236                             !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
237                                 if (!(amode & NFSV4OPEN_ACCESSWRITE) ||
238                                     (dp->nfsdl_flags & NFSCLDL_WRITE))
239                                         break;
240                                 dp = NULL;
241                                 break;
242                         }
243                 }
244         }
245
246         if (dp != NULL)
247                 ohp = &dp->nfsdl_owner;
248         else
249                 ohp = &clp->nfsc_owner;
250         /* Now, search for an openowner */
251         LIST_FOREACH(owp, ohp, nfsow_list) {
252                 if (!NFSBCMP(owp->nfsow_owner, own, NFSV4CL_LOCKNAMELEN))
253                         break;
254         }
255
256         /*
257          * Create a new open, as required.
258          */
259         nfscl_newopen(clp, dp, &owp, &nowp, &op, &nop, own, nfhp, fhlen,
260             newonep);
261
262         /*
263          * Serialize modifications to the open owner for multiple threads
264          * within the same process using a read/write sleep lock.
265          */
266         if (lockit)
267                 nfscl_lockexcl(&owp->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
268         NFSUNLOCKCLSTATE();
269         if (nowp != NULL)
270                 FREE((caddr_t)nowp, M_NFSCLOWNER);
271         if (nop != NULL)
272                 FREE((caddr_t)nop, M_NFSCLOPEN);
273         if (owpp != NULL)
274                 *owpp = owp;
275         if (opp != NULL)
276                 *opp = op;
277         if (retp != NULL) {
278                 if (nfhp != NULL && dp != NULL && nop == NULL)
279                         /* new local open on delegation */
280                         *retp = NFSCLOPEN_SETCRED;
281                 else
282                         *retp = NFSCLOPEN_OK;
283         }
284
285         /*
286          * Now, check the mode on the open and return the appropriate
287          * value.
288          */
289         if (op != NULL && (amode & ~(op->nfso_mode))) {
290                 op->nfso_mode |= amode;
291                 if (retp != NULL && dp == NULL)
292                         *retp = NFSCLOPEN_DOOPEN;
293         }
294         return (0);
295 }
296
297 /*
298  * Create a new open, as required.
299  */
300 static void
301 nfscl_newopen(struct nfsclclient *clp, struct nfscldeleg *dp,
302     struct nfsclowner **owpp, struct nfsclowner **nowpp, struct nfsclopen **opp,
303     struct nfsclopen **nopp, u_int8_t *own, u_int8_t *fhp, int fhlen,
304     int *newonep)
305 {
306         struct nfsclowner *owp = *owpp, *nowp;
307         struct nfsclopen *op, *nop;
308
309         if (nowpp != NULL)
310                 nowp = *nowpp;
311         else
312                 nowp = NULL;
313         if (nopp != NULL)
314                 nop = *nopp;
315         else
316                 nop = NULL;
317         if (owp == NULL && nowp != NULL) {
318                 NFSBCOPY(own, nowp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
319                 LIST_INIT(&nowp->nfsow_open);
320                 nowp->nfsow_clp = clp;
321                 nowp->nfsow_seqid = 0;
322                 nowp->nfsow_defunct = 0;
323                 nfscl_lockinit(&nowp->nfsow_rwlock);
324                 if (dp != NULL) {
325                         newnfsstats.cllocalopenowners++;
326                         LIST_INSERT_HEAD(&dp->nfsdl_owner, nowp, nfsow_list);
327                 } else {
328                         newnfsstats.clopenowners++;
329                         LIST_INSERT_HEAD(&clp->nfsc_owner, nowp, nfsow_list);
330                 }
331                 owp = *owpp = nowp;
332                 *nowpp = NULL;
333                 if (newonep != NULL)
334                         *newonep = 1;
335         }
336
337          /* If an fhp has been specified, create an Open as well. */
338         if (fhp != NULL) {
339                 /* and look for the correct open, based upon FH */
340                 LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
341                         if (op->nfso_fhlen == fhlen &&
342                             !NFSBCMP(op->nfso_fh, fhp, fhlen))
343                                 break;
344                 }
345                 if (op == NULL && nop != NULL) {
346                         nop->nfso_own = owp;
347                         nop->nfso_mode = 0;
348                         nop->nfso_opencnt = 0;
349                         nop->nfso_posixlock = 1;
350                         nop->nfso_fhlen = fhlen;
351                         NFSBCOPY(fhp, nop->nfso_fh, fhlen);
352                         LIST_INIT(&nop->nfso_lock);
353                         nop->nfso_stateid.seqid = 0;
354                         nop->nfso_stateid.other[0] = 0;
355                         nop->nfso_stateid.other[1] = 0;
356                         nop->nfso_stateid.other[2] = 0;
357                         if (dp != NULL) {
358                                 TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
359                                 TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
360                                     nfsdl_list);
361                                 dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
362                                 newnfsstats.cllocalopens++;
363                         } else {
364                                 newnfsstats.clopens++;
365                         }
366                         LIST_INSERT_HEAD(&owp->nfsow_open, nop, nfso_list);
367                         *opp = nop;
368                         *nopp = NULL;
369                         if (newonep != NULL)
370                                 *newonep = 1;
371                 } else {
372                         *opp = op;
373                 }
374         }
375 }
376
377 /*
378  * Called to find/add a delegation to a client.
379  */
380 APPLESTATIC int
381 nfscl_deleg(mount_t mp, struct nfsclclient *clp, u_int8_t *nfhp,
382     int fhlen, struct ucred *cred, NFSPROC_T *p, struct nfscldeleg **dpp)
383 {
384         struct nfscldeleg *dp = *dpp, *tdp;
385
386         /*
387          * First, if we have received a Read delegation for a file on a
388          * read/write file system, just return it, because they aren't
389          * useful, imho.
390          */
391         if (mp != NULL && dp != NULL && !NFSMNT_RDONLY(mp) &&
392             (dp->nfsdl_flags & NFSCLDL_READ)) {
393                 (void) nfscl_trydelegreturn(dp, cred, VFSTONFS(mp), p);
394                 FREE((caddr_t)dp, M_NFSCLDELEG);
395                 *dpp = NULL;
396                 return (0);
397         }
398
399         /* Look for the correct deleg, based upon FH */
400         NFSLOCKCLSTATE();
401         tdp = nfscl_finddeleg(clp, nfhp, fhlen);
402         if (tdp == NULL) {
403                 if (dp == NULL) {
404                         NFSUNLOCKCLSTATE();
405                         return (NFSERR_BADSTATEID);
406                 }
407                 *dpp = NULL;
408                 TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
409                 LIST_INSERT_HEAD(NFSCLDELEGHASH(clp, nfhp, fhlen), dp,
410                     nfsdl_hash);
411                 dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
412                 newnfsstats.cldelegates++;
413                 nfscl_delegcnt++;
414         } else {
415                 /*
416                  * Delegation already exists, what do we do if a new one??
417                  */
418                 if (dp != NULL) {
419                         printf("Deleg already exists!\n");
420                         FREE((caddr_t)dp, M_NFSCLDELEG);
421                         *dpp = NULL;
422                 } else {
423                         *dpp = tdp;
424                 }
425         }
426         NFSUNLOCKCLSTATE();
427         return (0);
428 }
429
430 /*
431  * Find a delegation for this file handle. Return NULL upon failure.
432  */
433 static struct nfscldeleg *
434 nfscl_finddeleg(struct nfsclclient *clp, u_int8_t *fhp, int fhlen)
435 {
436         struct nfscldeleg *dp;
437
438         LIST_FOREACH(dp, NFSCLDELEGHASH(clp, fhp, fhlen), nfsdl_hash) {
439             if (dp->nfsdl_fhlen == fhlen &&
440                 !NFSBCMP(dp->nfsdl_fh, fhp, fhlen))
441                 break;
442         }
443         return (dp);
444 }
445
446 /*
447  * Get a stateid for an I/O operation. First, look for an open and iff
448  * found, return either a lockowner stateid or the open stateid.
449  * If no Open is found, just return error and the special stateid of all zeros.
450  */
451 APPLESTATIC int
452 nfscl_getstateid(vnode_t vp, u_int8_t *nfhp, int fhlen, u_int32_t mode,
453     struct ucred *cred, NFSPROC_T *p, nfsv4stateid_t *stateidp,
454     void **lckpp)
455 {
456         struct nfsclclient *clp;
457         struct nfsclowner *owp;
458         struct nfsclopen *op = NULL;
459         struct nfscllockowner *lp;
460         struct nfscldeleg *dp;
461         struct nfsnode *np;
462         u_int8_t own[NFSV4CL_LOCKNAMELEN];
463         int error, done;
464
465         *lckpp = NULL;
466         /*
467          * Initially, just set the special stateid of all zeros.
468          */
469         stateidp->seqid = 0;
470         stateidp->other[0] = 0;
471         stateidp->other[1] = 0;
472         stateidp->other[2] = 0;
473         if (vnode_vtype(vp) != VREG)
474                 return (EISDIR);
475         np = VTONFS(vp);
476         NFSLOCKCLSTATE();
477         clp = nfscl_findcl(VFSTONFS(vnode_mount(vp)));
478         if (clp == NULL) {
479                 NFSUNLOCKCLSTATE();
480                 return (EACCES);
481         }
482
483         /*
484          * Wait for recovery to complete.
485          */
486         while ((clp->nfsc_flags & NFSCLFLAGS_RECVRINPROG))
487                 (void) nfsmsleep(&clp->nfsc_flags, NFSCLSTATEMUTEXPTR,
488                     PZERO, "nfsrecvr", NULL);
489
490         /*
491          * First, look for a delegation.
492          */
493         LIST_FOREACH(dp, NFSCLDELEGHASH(clp, nfhp, fhlen), nfsdl_hash) {
494                 if (dp->nfsdl_fhlen == fhlen &&
495                     !NFSBCMP(nfhp, dp->nfsdl_fh, fhlen)) {
496                         if (!(mode & NFSV4OPEN_ACCESSWRITE) ||
497                             (dp->nfsdl_flags & NFSCLDL_WRITE)) {
498                                 stateidp->seqid = dp->nfsdl_stateid.seqid;
499                                 stateidp->other[0] = dp->nfsdl_stateid.other[0];
500                                 stateidp->other[1] = dp->nfsdl_stateid.other[1];
501                                 stateidp->other[2] = dp->nfsdl_stateid.other[2];
502                                 if (!(np->n_flag & NDELEGRECALL)) {
503                                         TAILQ_REMOVE(&clp->nfsc_deleg, dp,
504                                             nfsdl_list);
505                                         TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp,
506                                             nfsdl_list);
507                                         dp->nfsdl_timestamp = NFSD_MONOSEC +
508                                             120;
509                                         dp->nfsdl_rwlock.nfslock_usecnt++;
510                                         *lckpp = (void *)&dp->nfsdl_rwlock;
511                                 }
512                                 NFSUNLOCKCLSTATE();
513                                 return (0);
514                         }
515                         break;
516                 }
517         }
518
519         if (p != NULL) {
520                 /*
521                  * If p != NULL, we want to search the parentage tree
522                  * for a matching OpenOwner and use that.
523                  */
524                 nfscl_filllockowner(p, own);
525                 error = nfscl_getopen(&clp->nfsc_owner, nfhp, fhlen, NULL, p,
526                     mode, NULL, &op);
527                 if (error == 0) {
528                         /* now look for a lockowner */
529                         LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
530                                 if (!NFSBCMP(lp->nfsl_owner, own,
531                                     NFSV4CL_LOCKNAMELEN)) {
532                                         stateidp->seqid =
533                                             lp->nfsl_stateid.seqid;
534                                         stateidp->other[0] =
535                                             lp->nfsl_stateid.other[0];
536                                         stateidp->other[1] =
537                                             lp->nfsl_stateid.other[1];
538                                         stateidp->other[2] =
539                                             lp->nfsl_stateid.other[2];
540                                         NFSUNLOCKCLSTATE();
541                                         return (0);
542                                 }
543                         }
544                 }
545         }
546         if (op == NULL) {
547                 /* If not found, just look for any OpenOwner that will work. */
548                 done = 0;
549                 owp = LIST_FIRST(&clp->nfsc_owner);
550                 while (!done && owp != NULL) {
551                         LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
552                                 if (op->nfso_fhlen == fhlen &&
553                                     !NFSBCMP(op->nfso_fh, nfhp, fhlen) &&
554                                     (mode & op->nfso_mode) == mode) {
555                                         done = 1;
556                                         break;
557                                 }
558                         }
559                         if (!done)
560                                 owp = LIST_NEXT(owp, nfsow_list);
561                 }
562                 if (!done) {
563                         NFSUNLOCKCLSTATE();
564                         return (ENOENT);
565                 }
566                 /* for read aheads or write behinds, use the open cred */
567                 newnfs_copycred(&op->nfso_cred, cred);
568         }
569
570         /*
571          * No lock stateid, so return the open stateid.
572          */
573         stateidp->seqid = op->nfso_stateid.seqid;
574         stateidp->other[0] = op->nfso_stateid.other[0];
575         stateidp->other[1] = op->nfso_stateid.other[1];
576         stateidp->other[2] = op->nfso_stateid.other[2];
577         NFSUNLOCKCLSTATE();
578         return (0);
579 }
580
581 /*
582  * Get an existing open. Search up the parentage tree for a match and
583  * return with the first one found.
584  */
585 static int
586 nfscl_getopen(struct nfsclownerhead *ohp, u_int8_t *nfhp, int fhlen,
587     u_int8_t *rown, NFSPROC_T *p, u_int32_t mode, struct nfsclowner **owpp,
588     struct nfsclopen **opp)
589 {
590         struct nfsclowner *owp = NULL;
591         struct nfsclopen *op;
592         NFSPROC_T *nproc;
593         u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
594
595         nproc = p;
596         op = NULL;
597         while (op == NULL && (nproc != NULL || rown != NULL)) {
598                 if (nproc != NULL) {
599                         nfscl_filllockowner(nproc, own);
600                         ownp = own;
601                 } else {
602                         ownp = rown;
603                 }
604                 /* Search the client list */
605                 LIST_FOREACH(owp, ohp, nfsow_list) {
606                         if (!NFSBCMP(owp->nfsow_owner, ownp,
607                             NFSV4CL_LOCKNAMELEN))
608                                 break;
609                 }
610                 if (owp != NULL) {
611                         /* and look for the correct open */
612                         LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
613                                 if (op->nfso_fhlen == fhlen &&
614                                     !NFSBCMP(op->nfso_fh, nfhp, fhlen)
615                                     && (op->nfso_mode & mode) == mode) {
616                                         break;
617                                 }
618                         }
619                 }
620                 if (rown != NULL)
621                         break;
622                 if (op == NULL)
623                         nproc = nfscl_getparent(nproc);
624         }
625         if (op == NULL) {
626                 return (EBADF);
627         }
628         if (owpp)
629                 *owpp = owp;
630         *opp = op;
631         return (0);
632 }
633
634 /*
635  * Release use of an open owner. Called when open operations are done
636  * with the open owner.
637  */
638 APPLESTATIC void
639 nfscl_ownerrelease(struct nfsclowner *owp, __unused int error,
640     __unused int candelete, int unlocked)
641 {
642
643         if (owp == NULL)
644                 return;
645         NFSLOCKCLSTATE();
646         if (!unlocked)
647                 nfscl_lockunlock(&owp->nfsow_rwlock);
648         nfscl_clrelease(owp->nfsow_clp);
649         NFSUNLOCKCLSTATE();
650 }
651
652 /*
653  * Release use of an open structure under an open owner.
654  */
655 APPLESTATIC void
656 nfscl_openrelease(struct nfsclopen *op, int error, int candelete)
657 {
658         struct nfsclclient *clp;
659         struct nfsclowner *owp;
660
661         if (op == NULL)
662                 return;
663         NFSLOCKCLSTATE();
664         owp = op->nfso_own;
665         nfscl_lockunlock(&owp->nfsow_rwlock);
666         clp = owp->nfsow_clp;
667         if (error && candelete && op->nfso_opencnt == 0)
668                 nfscl_freeopen(op, 0);
669         nfscl_clrelease(clp);
670         NFSUNLOCKCLSTATE();
671 }
672
673 /*
674  * Called to get a clientid structure. It will optionally lock the
675  * client data structures to do the SetClientId/SetClientId_confirm,
676  * but will release that lock and return the clientid with a refernce
677  * count on it.
678  * If the "cred" argument is NULL, a new clientid should not be created.
679  * If the "p" argument is NULL, a SetClientID/SetClientIDConfirm cannot
680  * be done.
681  * It always clpp with a reference count on it, unless returning an error.
682  */
683 APPLESTATIC int
684 nfscl_getcl(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
685     struct nfsclclient **clpp)
686 {
687         struct nfsclclient *clp;
688         struct nfsclclient *newclp = NULL;
689         struct nfscllockowner *lp, *nlp;
690         struct mount *mp;
691         struct nfsmount *nmp;
692         char uuid[HOSTUUIDLEN];
693         int igotlock = 0, error, trystalecnt, clidinusedelay, i;
694         u_int16_t idlen = 0;
695
696         mp = vnode_mount(vp);
697         nmp = VFSTONFS(mp);
698         if (cred != NULL) {
699                 getcredhostuuid(cred, uuid, sizeof uuid);
700                 idlen = strlen(uuid);
701                 if (idlen > 0)
702                         idlen += sizeof (u_int64_t);
703                 else
704                         idlen += sizeof (u_int64_t) + 16; /* 16 random bytes */
705                 MALLOC(newclp, struct nfsclclient *,
706                     sizeof (struct nfsclclient) + idlen - 1, M_NFSCLCLIENT,
707                     M_WAITOK);
708         }
709         NFSLOCKCLSTATE();
710         /*
711          * If a forced dismount is already in progress, don't
712          * allocate a new clientid and get out now. For the case where
713          * clp != NULL, this is a harmless optimization.
714          */
715         if ((mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
716                 NFSUNLOCKCLSTATE();
717                 if (newclp != NULL)
718                         free(newclp, M_NFSCLCLIENT);
719                 return (EBADF);
720         }
721         clp = nmp->nm_clp;
722         if (clp == NULL) {
723                 if (newclp == NULL) {
724                         NFSUNLOCKCLSTATE();
725                         return (EACCES);
726                 }
727                 clp = newclp;
728                 NFSBZERO((caddr_t)clp, sizeof(struct nfsclclient) + idlen - 1);
729                 clp->nfsc_idlen = idlen;
730                 LIST_INIT(&clp->nfsc_owner);
731                 TAILQ_INIT(&clp->nfsc_deleg);
732                 for (i = 0; i < NFSCLDELEGHASHSIZE; i++)
733                         LIST_INIT(&clp->nfsc_deleghash[i]);
734                 LIST_INIT(&clp->nfsc_defunctlockowner);
735                 clp->nfsc_flags = NFSCLFLAGS_INITED;
736                 clp->nfsc_clientidrev = 1;
737                 clp->nfsc_cbident = nfscl_nextcbident();
738                 nfscl_fillclid(nmp->nm_clval, uuid, clp->nfsc_id,
739                     clp->nfsc_idlen);
740                 LIST_INSERT_HEAD(&nfsclhead, clp, nfsc_list);
741                 nmp->nm_clp = clp;
742                 clp->nfsc_nmp = nmp;
743                 NFSUNLOCKCLSTATE();
744                 nfscl_start_renewthread(clp);
745         } else {
746                 NFSUNLOCKCLSTATE();
747                 if (newclp != NULL)
748                         FREE((caddr_t)newclp, M_NFSCLCLIENT);
749         }
750         NFSLOCKCLSTATE();
751         while ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0 && !igotlock)
752                 igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
753                     NFSCLSTATEMUTEXPTR, mp);
754         if (!igotlock)
755                 nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, mp);
756         if (igotlock == 0 && (mp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
757                 /*
758                  * Both nfsv4_lock() and nfsv4_getref() know to check
759                  * for MNTK_UNMOUNTF and return without sleeping to
760                  * wait for the exclusive lock to be released, since it
761                  * might be held by nfscl_umount() and we need to get out
762                  * now for that case and not wait until nfscl_umount()
763                  * releases it.
764                  */
765                 NFSUNLOCKCLSTATE();
766                 return (EBADF);
767         }
768         NFSUNLOCKCLSTATE();
769
770         /*
771          * If it needs a clientid, do the setclientid now.
772          */
773         if ((clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID) == 0) {
774                 if (!igotlock)
775                         panic("nfscl_clget");
776                 if (p == NULL || cred == NULL) {
777                         NFSLOCKCLSTATE();
778                         nfsv4_unlock(&clp->nfsc_lock, 0);
779                         NFSUNLOCKCLSTATE();
780                         return (EACCES);
781                 }
782                 /* get rid of defunct lockowners */
783                 LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list,
784                     nlp) {
785                         nfscl_freelockowner(lp, 0);
786                 }
787                 /*
788                  * If RFC3530 Sec. 14.2.33 is taken literally,
789                  * NFSERR_CLIDINUSE will be returned persistently for the
790                  * case where a new mount of the same file system is using
791                  * a different principal. In practice, NFSERR_CLIDINUSE is
792                  * only returned when there is outstanding unexpired state
793                  * on the clientid. As such, try for twice the lease
794                  * interval, if we know what that is. Otherwise, make a
795                  * wild ass guess.
796                  * The case of returning NFSERR_STALECLIENTID is far less
797                  * likely, but might occur if there is a significant delay
798                  * between doing the SetClientID and SetClientIDConfirm Ops,
799                  * such that the server throws away the clientid before
800                  * receiving the SetClientIDConfirm.
801                  */
802                 if (clp->nfsc_renew > 0)
803                         clidinusedelay = NFSCL_LEASE(clp->nfsc_renew) * 2;
804                 else
805                         clidinusedelay = 120;
806                 trystalecnt = 3;
807                 do {
808                         error = nfsrpc_setclient(VFSTONFS(vnode_mount(vp)),
809                             clp, cred, p);
810                         if (error == NFSERR_STALECLIENTID ||
811                             error == NFSERR_STALEDONTRECOVER ||
812                             error == NFSERR_CLIDINUSE) {
813                                 (void) nfs_catnap(PZERO, error, "nfs_setcl");
814                         }
815                 } while (((error == NFSERR_STALECLIENTID ||
816                      error == NFSERR_STALEDONTRECOVER) && --trystalecnt > 0) ||
817                     (error == NFSERR_CLIDINUSE && --clidinusedelay > 0));
818                 if (error) {
819                         NFSLOCKCLSTATE();
820                         nfsv4_unlock(&clp->nfsc_lock, 0);
821                         NFSUNLOCKCLSTATE();
822                         return (error);
823                 }
824                 clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
825         }
826         if (igotlock) {
827                 NFSLOCKCLSTATE();
828                 nfsv4_unlock(&clp->nfsc_lock, 1);
829                 NFSUNLOCKCLSTATE();
830         }
831
832         *clpp = clp;
833         return (0);
834 }
835
836 /*
837  * Get a reference to a clientid and return it, if valid.
838  */
839 APPLESTATIC struct nfsclclient *
840 nfscl_findcl(struct nfsmount *nmp)
841 {
842         struct nfsclclient *clp;
843
844         clp = nmp->nm_clp;
845         if (clp == NULL || !(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID))
846                 return (NULL);
847         return (clp);
848 }
849
850 /*
851  * Release the clientid structure. It may be locked or reference counted.
852  */
853 static void
854 nfscl_clrelease(struct nfsclclient *clp)
855 {
856
857         if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
858                 nfsv4_unlock(&clp->nfsc_lock, 0);
859         else
860                 nfsv4_relref(&clp->nfsc_lock);
861 }
862
863 /*
864  * External call for nfscl_clrelease.
865  */
866 APPLESTATIC void
867 nfscl_clientrelease(struct nfsclclient *clp)
868 {
869
870         NFSLOCKCLSTATE();
871         if (clp->nfsc_lock.nfslock_lock & NFSV4LOCK_LOCK)
872                 nfsv4_unlock(&clp->nfsc_lock, 0);
873         else
874                 nfsv4_relref(&clp->nfsc_lock);
875         NFSUNLOCKCLSTATE();
876 }
877
878 /*
879  * Called when wanting to lock a byte region.
880  */
881 APPLESTATIC int
882 nfscl_getbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
883     short type, struct ucred *cred, NFSPROC_T *p, struct nfsclclient *rclp,
884     int recovery, u_int8_t *rownp, u_int8_t *ropenownp,
885     struct nfscllockowner **lpp, int *newonep, int *donelocallyp)
886 {
887         struct nfscllockowner *lp;
888         struct nfsclopen *op;
889         struct nfsclclient *clp;
890         struct nfscllockowner *nlp;
891         struct nfscllock *nlop, *otherlop;
892         struct nfscldeleg *dp = NULL, *ldp = NULL;
893         struct nfscllockownerhead *lhp = NULL;
894         struct nfsnode *np;
895         u_int8_t own[NFSV4CL_LOCKNAMELEN], *ownp;
896         int error = 0, ret, donelocally = 0;
897         u_int32_t mode;
898
899         if (type == F_WRLCK)
900                 mode = NFSV4OPEN_ACCESSWRITE;
901         else
902                 mode = NFSV4OPEN_ACCESSREAD;
903         np = VTONFS(vp);
904         *lpp = NULL;
905         *newonep = 0;
906         *donelocallyp = 0;
907
908         /*
909          * Might need these, so MALLOC them now, to
910          * avoid a tsleep() in MALLOC later.
911          */
912         MALLOC(nlp, struct nfscllockowner *,
913             sizeof (struct nfscllockowner), M_NFSCLLOCKOWNER, M_WAITOK);
914         MALLOC(otherlop, struct nfscllock *,
915             sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
916         MALLOC(nlop, struct nfscllock *,
917             sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
918         nlop->nfslo_type = type;
919         nlop->nfslo_first = off;
920         if (len == NFS64BITSSET) {
921                 nlop->nfslo_end = NFS64BITSSET;
922         } else {
923                 nlop->nfslo_end = off + len;
924                 if (nlop->nfslo_end <= nlop->nfslo_first)
925                         error = NFSERR_INVAL;
926         }
927
928         if (!error) {
929                 if (recovery)
930                         clp = rclp;
931                 else
932                         error = nfscl_getcl(vp, cred, p, &clp);
933         }
934         if (error) {
935                 FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
936                 FREE((caddr_t)otherlop, M_NFSCLLOCK);
937                 FREE((caddr_t)nlop, M_NFSCLLOCK);
938                 return (error);
939         }
940
941         op = NULL;
942         if (recovery) {
943                 ownp = rownp;
944         } else {
945                 nfscl_filllockowner(p, own);
946                 ownp = own;
947         }
948         if (!recovery) {
949                 NFSLOCKCLSTATE();
950                 /*
951                  * First, search for a delegation. If one exists for this file,
952                  * the lock can be done locally against it, so long as there
953                  * isn't a local lock conflict.
954                  */
955                 ldp = dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
956                     np->n_fhp->nfh_len);
957                 /* Just sanity check for correct type of delegation */
958                 if (dp != NULL && ((dp->nfsdl_flags &
959                     (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) != 0 ||
960                      (type == F_WRLCK &&
961                       (dp->nfsdl_flags & NFSCLDL_WRITE) == 0)))
962                         dp = NULL;
963         }
964         if (dp != NULL) {
965                 /* Now, find the associated open to get the correct openowner */
966                 ret = nfscl_getopen(&dp->nfsdl_owner, np->n_fhp->nfh_fh,
967                     np->n_fhp->nfh_len, NULL, p, mode, NULL, &op);
968                 if (ret)
969                         ret = nfscl_getopen(&clp->nfsc_owner,
970                             np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
971                             mode, NULL, &op);
972                 if (!ret) {
973                         lhp = &dp->nfsdl_lock;
974                         TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
975                         TAILQ_INSERT_HEAD(&clp->nfsc_deleg, dp, nfsdl_list);
976                         dp->nfsdl_timestamp = NFSD_MONOSEC + 120;
977                         donelocally = 1;
978                 } else {
979                         dp = NULL;
980                 }
981         }
982         if (!donelocally) {
983                 /*
984                  * Get the related Open.
985                  */
986                 if (recovery)
987                         error = nfscl_getopen(&clp->nfsc_owner,
988                             np->n_fhp->nfh_fh, np->n_fhp->nfh_len, ropenownp,
989                             NULL, mode, NULL, &op);
990                 else
991                         error = nfscl_getopen(&clp->nfsc_owner,
992                             np->n_fhp->nfh_fh, np->n_fhp->nfh_len, NULL, p,
993                             mode, NULL, &op);
994                 if (!error)
995                         lhp = &op->nfso_lock;
996         }
997         if (!error && !recovery)
998                 error = nfscl_localconflict(clp, np->n_fhp->nfh_fh,
999                     np->n_fhp->nfh_len, nlop, ownp, ldp, NULL);
1000         if (error) {
1001                 if (!recovery) {
1002                         nfscl_clrelease(clp);
1003                         NFSUNLOCKCLSTATE();
1004                 }
1005                 FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1006                 FREE((caddr_t)otherlop, M_NFSCLLOCK);
1007                 FREE((caddr_t)nlop, M_NFSCLLOCK);
1008                 return (error);
1009         }
1010
1011         /*
1012          * Ok, see if a lockowner exists and create one, as required.
1013          */
1014         LIST_FOREACH(lp, lhp, nfsl_list) {
1015                 if (!NFSBCMP(lp->nfsl_owner, ownp, NFSV4CL_LOCKNAMELEN))
1016                         break;
1017         }
1018         if (lp == NULL) {
1019                 NFSBCOPY(ownp, nlp->nfsl_owner, NFSV4CL_LOCKNAMELEN);
1020                 if (recovery)
1021                         NFSBCOPY(ropenownp, nlp->nfsl_openowner,
1022                             NFSV4CL_LOCKNAMELEN);
1023                 else
1024                         NFSBCOPY(op->nfso_own->nfsow_owner, nlp->nfsl_openowner,
1025                             NFSV4CL_LOCKNAMELEN);
1026                 nlp->nfsl_seqid = 0;
1027                 nlp->nfsl_defunct = 0;
1028                 nlp->nfsl_inprog = NULL;
1029                 nfscl_lockinit(&nlp->nfsl_rwlock);
1030                 LIST_INIT(&nlp->nfsl_lock);
1031                 if (donelocally) {
1032                         nlp->nfsl_open = NULL;
1033                         newnfsstats.cllocallockowners++;
1034                 } else {
1035                         nlp->nfsl_open = op;
1036                         newnfsstats.cllockowners++;
1037                 }
1038                 LIST_INSERT_HEAD(lhp, nlp, nfsl_list);
1039                 lp = nlp;
1040                 nlp = NULL;
1041                 *newonep = 1;
1042         }
1043
1044         /*
1045          * Now, update the byte ranges for locks.
1046          */
1047         ret = nfscl_updatelock(lp, &nlop, &otherlop, donelocally);
1048         if (!ret)
1049                 donelocally = 1;
1050         if (donelocally) {
1051                 *donelocallyp = 1;
1052                 if (!recovery)
1053                         nfscl_clrelease(clp);
1054         } else {
1055                 /*
1056                  * Serial modifications on the lock owner for multiple threads
1057                  * for the same process using a read/write lock.
1058                  */
1059                 if (!recovery)
1060                         nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1061         }
1062         if (!recovery)
1063                 NFSUNLOCKCLSTATE();
1064
1065         if (nlp)
1066                 FREE((caddr_t)nlp, M_NFSCLLOCKOWNER);
1067         if (nlop)
1068                 FREE((caddr_t)nlop, M_NFSCLLOCK);
1069         if (otherlop)
1070                 FREE((caddr_t)otherlop, M_NFSCLLOCK);
1071
1072         *lpp = lp;
1073         return (0);
1074 }
1075
1076 /*
1077  * Called to unlock a byte range, for LockU.
1078  */
1079 APPLESTATIC int
1080 nfscl_relbytelock(vnode_t vp, u_int64_t off, u_int64_t len,
1081     __unused struct ucred *cred, NFSPROC_T *p, int callcnt,
1082     struct nfsclclient *clp, struct nfscllockowner **lpp, int *dorpcp)
1083 {
1084         struct nfscllockowner *lp;
1085         struct nfsclowner *owp;
1086         struct nfsclopen *op;
1087         struct nfscllock *nlop, *other_lop = NULL;
1088         struct nfscldeleg *dp;
1089         struct nfsnode *np;
1090         u_int8_t own[NFSV4CL_LOCKNAMELEN];
1091         int ret = 0, fnd;
1092
1093         np = VTONFS(vp);
1094         *lpp = NULL;
1095         *dorpcp = 0;
1096
1097         /*
1098          * Might need these, so MALLOC them now, to
1099          * avoid a tsleep() in MALLOC later.
1100          */
1101         MALLOC(nlop, struct nfscllock *,
1102             sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1103         nlop->nfslo_type = F_UNLCK;
1104         nlop->nfslo_first = off;
1105         if (len == NFS64BITSSET) {
1106                 nlop->nfslo_end = NFS64BITSSET;
1107         } else {
1108                 nlop->nfslo_end = off + len;
1109                 if (nlop->nfslo_end <= nlop->nfslo_first) {
1110                         FREE((caddr_t)nlop, M_NFSCLLOCK);
1111                         return (NFSERR_INVAL);
1112                 }
1113         }
1114         if (callcnt == 0) {
1115                 MALLOC(other_lop, struct nfscllock *,
1116                     sizeof (struct nfscllock), M_NFSCLLOCK, M_WAITOK);
1117                 *other_lop = *nlop;
1118         }
1119         nfscl_filllockowner(p, own);
1120         dp = NULL;
1121         NFSLOCKCLSTATE();
1122         if (callcnt == 0)
1123                 dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
1124                     np->n_fhp->nfh_len);
1125
1126         /*
1127          * First, unlock any local regions on a delegation.
1128          */
1129         if (dp != NULL) {
1130                 /* Look for this lockowner. */
1131                 LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1132                         if (!NFSBCMP(lp->nfsl_owner, own,
1133                             NFSV4CL_LOCKNAMELEN))
1134                                 break;
1135                 }
1136                 if (lp != NULL)
1137                         /* Use other_lop, so nlop is still available */
1138                         (void)nfscl_updatelock(lp, &other_lop, NULL, 1);
1139         }
1140
1141         /*
1142          * Now, find a matching open/lockowner that hasn't already been done,
1143          * as marked by nfsl_inprog.
1144          */
1145         lp = NULL;
1146         fnd = 0;
1147         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1148             LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1149                 if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1150                     !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1151                     LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1152                         if (lp->nfsl_inprog == NULL &&
1153                             !NFSBCMP(lp->nfsl_owner, own,
1154                              NFSV4CL_LOCKNAMELEN)) {
1155                                 fnd = 1;
1156                                 break;
1157                         }
1158                     }
1159                     if (fnd)
1160                         break;
1161                 }
1162             }
1163             if (fnd)
1164                 break;
1165         }
1166
1167         if (lp != NULL) {
1168                 ret = nfscl_updatelock(lp, &nlop, NULL, 0);
1169                 if (ret)
1170                         *dorpcp = 1;
1171                 /*
1172                  * Serial modifications on the lock owner for multiple
1173                  * threads for the same process using a read/write lock.
1174                  */
1175                 lp->nfsl_inprog = p;
1176                 nfscl_lockexcl(&lp->nfsl_rwlock, NFSCLSTATEMUTEXPTR);
1177                 *lpp = lp;
1178         }
1179         NFSUNLOCKCLSTATE();
1180         if (nlop)
1181                 FREE((caddr_t)nlop, M_NFSCLLOCK);
1182         if (other_lop)
1183                 FREE((caddr_t)other_lop, M_NFSCLLOCK);
1184         return (0);
1185 }
1186
1187 /*
1188  * Release all lockowners marked in progess for this process and file.
1189  */
1190 APPLESTATIC void
1191 nfscl_releasealllocks(struct nfsclclient *clp, vnode_t vp, NFSPROC_T *p)
1192 {
1193         struct nfsclowner *owp;
1194         struct nfsclopen *op;
1195         struct nfscllockowner *lp;
1196         struct nfsnode *np;
1197         u_int8_t own[NFSV4CL_LOCKNAMELEN];
1198
1199         np = VTONFS(vp);
1200         nfscl_filllockowner(p, own);
1201         NFSLOCKCLSTATE();
1202         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1203             LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1204                 if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1205                     !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1206                     LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1207                         if (lp->nfsl_inprog == p &&
1208                             !NFSBCMP(lp->nfsl_owner, own,
1209                             NFSV4CL_LOCKNAMELEN)) {
1210                             lp->nfsl_inprog = NULL;
1211                             nfscl_lockunlock(&lp->nfsl_rwlock);
1212                         }
1213                     }
1214                 }
1215             }
1216         }
1217         nfscl_clrelease(clp);
1218         NFSUNLOCKCLSTATE();
1219 }
1220
1221 /*
1222  * Called to find out if any bytes within the byte range specified are
1223  * write locked by the calling process. Used to determine if flushing
1224  * is required before a LockU.
1225  * If in doubt, return 1, so the flush will occur.
1226  */
1227 APPLESTATIC int
1228 nfscl_checkwritelocked(vnode_t vp, struct flock *fl,
1229     struct ucred *cred, NFSPROC_T *p)
1230 {
1231         struct nfsclowner *owp;
1232         struct nfscllockowner *lp;
1233         struct nfsclopen *op;
1234         struct nfsclclient *clp;
1235         struct nfscllock *lop;
1236         struct nfscldeleg *dp;
1237         struct nfsnode *np;
1238         u_int64_t off, end;
1239         u_int8_t own[NFSV4CL_LOCKNAMELEN];
1240         int error = 0;
1241
1242         np = VTONFS(vp);
1243         switch (fl->l_whence) {
1244         case SEEK_SET:
1245         case SEEK_CUR:
1246                 /*
1247                  * Caller is responsible for adding any necessary offset
1248                  * when SEEK_CUR is used.
1249                  */
1250                 off = fl->l_start;
1251                 break;
1252         case SEEK_END:
1253                 off = np->n_size + fl->l_start;
1254                 break;
1255         default:
1256                 return (1);
1257         };
1258         if (fl->l_len != 0) {
1259                 end = off + fl->l_len;
1260                 if (end < off)
1261                         return (1);
1262         } else {
1263                 end = NFS64BITSSET;
1264         }
1265
1266         error = nfscl_getcl(vp, cred, p, &clp);
1267         if (error)
1268                 return (1);
1269         nfscl_filllockowner(p, own);
1270         NFSLOCKCLSTATE();
1271
1272         /*
1273          * First check the delegation locks.
1274          */
1275         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
1276         if (dp != NULL) {
1277                 LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
1278                         if (!NFSBCMP(lp->nfsl_owner, own,
1279                             NFSV4CL_LOCKNAMELEN))
1280                                 break;
1281                 }
1282                 if (lp != NULL) {
1283                         LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1284                                 if (lop->nfslo_first >= end)
1285                                         break;
1286                                 if (lop->nfslo_end <= off)
1287                                         continue;
1288                                 if (lop->nfslo_type == F_WRLCK) {
1289                                         nfscl_clrelease(clp);
1290                                         NFSUNLOCKCLSTATE();
1291                                         return (1);
1292                                 }
1293                         }
1294                 }
1295         }
1296
1297         /*
1298          * Now, check state against the server.
1299          */
1300         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
1301             LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1302                 if (op->nfso_fhlen == np->n_fhp->nfh_len &&
1303                     !NFSBCMP(op->nfso_fh, np->n_fhp->nfh_fh, op->nfso_fhlen)) {
1304                     LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1305                         if (!NFSBCMP(lp->nfsl_owner, own,
1306                             NFSV4CL_LOCKNAMELEN))
1307                             break;
1308                     }
1309                     if (lp != NULL) {
1310                         LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
1311                             if (lop->nfslo_first >= end)
1312                                 break;
1313                             if (lop->nfslo_end <= off)
1314                                 continue;
1315                             if (lop->nfslo_type == F_WRLCK) {
1316                                 nfscl_clrelease(clp);
1317                                 NFSUNLOCKCLSTATE();
1318                                 return (1);
1319                             }
1320                         }
1321                     }
1322                 }
1323             }
1324         }
1325         nfscl_clrelease(clp);
1326         NFSUNLOCKCLSTATE();
1327         return (0);
1328 }
1329
1330 /*
1331  * Release a byte range lock owner structure.
1332  */
1333 APPLESTATIC void
1334 nfscl_lockrelease(struct nfscllockowner *lp, int error, int candelete)
1335 {
1336         struct nfsclclient *clp;
1337
1338         if (lp == NULL)
1339                 return;
1340         NFSLOCKCLSTATE();
1341         clp = lp->nfsl_open->nfso_own->nfsow_clp;
1342         if (error != 0 && candelete &&
1343             (lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED) == 0)
1344                 nfscl_freelockowner(lp, 0);
1345         else
1346                 nfscl_lockunlock(&lp->nfsl_rwlock);
1347         nfscl_clrelease(clp);
1348         NFSUNLOCKCLSTATE();
1349 }
1350
1351 /*
1352  * Free up an open structure and any associated byte range lock structures.
1353  */
1354 APPLESTATIC void
1355 nfscl_freeopen(struct nfsclopen *op, int local)
1356 {
1357
1358         LIST_REMOVE(op, nfso_list);
1359         nfscl_freealllocks(&op->nfso_lock, local);
1360         FREE((caddr_t)op, M_NFSCLOPEN);
1361         if (local)
1362                 newnfsstats.cllocalopens--;
1363         else
1364                 newnfsstats.clopens--;
1365 }
1366
1367 /*
1368  * Free up all lock owners and associated locks.
1369  */
1370 static void
1371 nfscl_freealllocks(struct nfscllockownerhead *lhp, int local)
1372 {
1373         struct nfscllockowner *lp, *nlp;
1374
1375         LIST_FOREACH_SAFE(lp, lhp, nfsl_list, nlp) {
1376                 if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1377                         panic("nfscllckw");
1378                 nfscl_freelockowner(lp, local);
1379         }
1380 }
1381
1382 /*
1383  * Called for an Open when NFSERR_EXPIRED is received from the server.
1384  * If there are no byte range locks nor a Share Deny lost, try to do a
1385  * fresh Open. Otherwise, free the open.
1386  */
1387 static int
1388 nfscl_expireopen(struct nfsclclient *clp, struct nfsclopen *op,
1389     struct nfsmount *nmp, struct ucred *cred, NFSPROC_T *p)
1390 {
1391         struct nfscllockowner *lp;
1392         struct nfscldeleg *dp;
1393         int mustdelete = 0, error;
1394
1395         /*
1396          * Look for any byte range lock(s).
1397          */
1398         LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1399                 if (!LIST_EMPTY(&lp->nfsl_lock)) {
1400                         mustdelete = 1;
1401                         break;
1402                 }
1403         }
1404
1405         /*
1406          * If no byte range lock(s) nor a Share deny, try to re-open.
1407          */
1408         if (!mustdelete && (op->nfso_mode & NFSLCK_DENYBITS) == 0) {
1409                 newnfs_copycred(&op->nfso_cred, cred);
1410                 dp = NULL;
1411                 error = nfsrpc_reopen(nmp, op->nfso_fh,
1412                     op->nfso_fhlen, op->nfso_mode, op, &dp, cred, p);
1413                 if (error) {
1414                         mustdelete = 1;
1415                         if (dp != NULL) {
1416                                 FREE((caddr_t)dp, M_NFSCLDELEG);
1417                                 dp = NULL;
1418                         }
1419                 }
1420                 if (dp != NULL)
1421                         nfscl_deleg(nmp->nm_mountp, clp, op->nfso_fh,
1422                             op->nfso_fhlen, cred, p, &dp);
1423         }
1424
1425         /*
1426          * If a byte range lock or Share deny or couldn't re-open, free it.
1427          */
1428         if (mustdelete)
1429                 nfscl_freeopen(op, 0);
1430         return (mustdelete);
1431 }
1432
1433 /*
1434  * Free up an open owner structure.
1435  */
1436 static void
1437 nfscl_freeopenowner(struct nfsclowner *owp, int local)
1438 {
1439
1440         LIST_REMOVE(owp, nfsow_list);
1441         FREE((caddr_t)owp, M_NFSCLOWNER);
1442         if (local)
1443                 newnfsstats.cllocalopenowners--;
1444         else
1445                 newnfsstats.clopenowners--;
1446 }
1447
1448 /*
1449  * Free up a byte range lock owner structure.
1450  */
1451 static void
1452 nfscl_freelockowner(struct nfscllockowner *lp, int local)
1453 {
1454         struct nfscllock *lop, *nlop;
1455
1456         LIST_REMOVE(lp, nfsl_list);
1457         LIST_FOREACH_SAFE(lop, &lp->nfsl_lock, nfslo_list, nlop) {
1458                 nfscl_freelock(lop, local);
1459         }
1460         FREE((caddr_t)lp, M_NFSCLLOCKOWNER);
1461         if (local)
1462                 newnfsstats.cllocallockowners--;
1463         else
1464                 newnfsstats.cllockowners--;
1465 }
1466
1467 /*
1468  * Free up a byte range lock structure.
1469  */
1470 APPLESTATIC void
1471 nfscl_freelock(struct nfscllock *lop, int local)
1472 {
1473
1474         LIST_REMOVE(lop, nfslo_list);
1475         FREE((caddr_t)lop, M_NFSCLLOCK);
1476         if (local)
1477                 newnfsstats.cllocallocks--;
1478         else
1479                 newnfsstats.cllocks--;
1480 }
1481
1482 /*
1483  * Clean out the state related to a delegation.
1484  */
1485 static void
1486 nfscl_cleandeleg(struct nfscldeleg *dp)
1487 {
1488         struct nfsclowner *owp, *nowp;
1489         struct nfsclopen *op;
1490
1491         LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
1492                 op = LIST_FIRST(&owp->nfsow_open);
1493                 if (op != NULL) {
1494                         if (LIST_NEXT(op, nfso_list) != NULL)
1495                                 panic("nfscleandel");
1496                         nfscl_freeopen(op, 1);
1497                 }
1498                 nfscl_freeopenowner(owp, 1);
1499         }
1500         nfscl_freealllocks(&dp->nfsdl_lock, 1);
1501 }
1502
1503 /*
1504  * Free a delegation.
1505  */
1506 static void
1507 nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
1508 {
1509
1510         TAILQ_REMOVE(hdp, dp, nfsdl_list);
1511         LIST_REMOVE(dp, nfsdl_hash);
1512         FREE((caddr_t)dp, M_NFSCLDELEG);
1513         newnfsstats.cldelegates--;
1514         nfscl_delegcnt--;
1515 }
1516
1517 /*
1518  * Free up all state related to this client structure.
1519  */
1520 static void
1521 nfscl_cleanclient(struct nfsclclient *clp)
1522 {
1523         struct nfsclowner *owp, *nowp;
1524         struct nfsclopen *op, *nop;
1525         struct nfscllockowner *lp, *nlp;
1526
1527
1528         /* get rid of defunct lockowners */
1529         LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) {
1530                 nfscl_freelockowner(lp, 0);
1531         }
1532
1533         /* Now, all the OpenOwners, etc. */
1534         LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1535                 LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1536                         nfscl_freeopen(op, 0);
1537                 }
1538                 nfscl_freeopenowner(owp, 0);
1539         }
1540 }
1541
1542 /*
1543  * Called when an NFSERR_EXPIRED is received from the server.
1544  */
1545 static void
1546 nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
1547     struct ucred *cred, NFSPROC_T *p)
1548 {
1549         struct nfsclowner *owp, *nowp, *towp;
1550         struct nfsclopen *op, *nop, *top;
1551         struct nfscldeleg *dp, *ndp;
1552         int ret, printed = 0;
1553
1554         /*
1555          * First, merge locally issued Opens into the list for the server.
1556          */
1557         dp = TAILQ_FIRST(&clp->nfsc_deleg);
1558         while (dp != NULL) {
1559             ndp = TAILQ_NEXT(dp, nfsdl_list);
1560             owp = LIST_FIRST(&dp->nfsdl_owner);
1561             while (owp != NULL) {
1562                 nowp = LIST_NEXT(owp, nfsow_list);
1563                 op = LIST_FIRST(&owp->nfsow_open);
1564                 if (op != NULL) {
1565                     if (LIST_NEXT(op, nfso_list) != NULL)
1566                         panic("nfsclexp");
1567                     LIST_FOREACH(towp, &clp->nfsc_owner, nfsow_list) {
1568                         if (!NFSBCMP(towp->nfsow_owner, owp->nfsow_owner,
1569                             NFSV4CL_LOCKNAMELEN))
1570                             break;
1571                     }
1572                     if (towp != NULL) {
1573                         /* Merge opens in */
1574                         LIST_FOREACH(top, &towp->nfsow_open, nfso_list) {
1575                             if (top->nfso_fhlen == op->nfso_fhlen &&
1576                                 !NFSBCMP(top->nfso_fh, op->nfso_fh,
1577                                  op->nfso_fhlen)) {
1578                                 top->nfso_mode |= op->nfso_mode;
1579                                 top->nfso_opencnt += op->nfso_opencnt;
1580                                 break;
1581                             }
1582                         }
1583                         if (top == NULL) {
1584                             /* Just add the open to the owner list */
1585                             LIST_REMOVE(op, nfso_list);
1586                             op->nfso_own = towp;
1587                             LIST_INSERT_HEAD(&towp->nfsow_open, op, nfso_list);
1588                             newnfsstats.cllocalopens--;
1589                             newnfsstats.clopens++;
1590                         }
1591                     } else {
1592                         /* Just add the openowner to the client list */
1593                         LIST_REMOVE(owp, nfsow_list);
1594                         owp->nfsow_clp = clp;
1595                         LIST_INSERT_HEAD(&clp->nfsc_owner, owp, nfsow_list);
1596                         newnfsstats.cllocalopenowners--;
1597                         newnfsstats.clopenowners++;
1598                         newnfsstats.cllocalopens--;
1599                         newnfsstats.clopens++;
1600                     }
1601                 }
1602                 owp = nowp;
1603             }
1604             if (!printed && !LIST_EMPTY(&dp->nfsdl_lock)) {
1605                 printed = 1;
1606                 printf("nfsv4 expired locks lost\n");
1607             }
1608             nfscl_cleandeleg(dp);
1609             nfscl_freedeleg(&clp->nfsc_deleg, dp);
1610             dp = ndp;
1611         }
1612         if (!TAILQ_EMPTY(&clp->nfsc_deleg))
1613             panic("nfsclexp");
1614
1615         /*
1616          * Now, try and reopen against the server.
1617          */
1618         LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1619                 owp->nfsow_seqid = 0;
1620                 LIST_FOREACH_SAFE(op, &owp->nfsow_open, nfso_list, nop) {
1621                         ret = nfscl_expireopen(clp, op, nmp, cred, p);
1622                         if (ret && !printed) {
1623                                 printed = 1;
1624                                 printf("nfsv4 expired locks lost\n");
1625                         }
1626                 }
1627                 if (LIST_EMPTY(&owp->nfsow_open))
1628                         nfscl_freeopenowner(owp, 0);
1629         }
1630 }
1631
1632 #ifndef __FreeBSD__
1633 /*
1634  * Called from exit() upon process termination.
1635  */
1636 APPLESTATIC void
1637 nfscl_cleanup(NFSPROC_T *p)
1638 {
1639         struct nfsclclient *clp;
1640         u_int8_t own[NFSV4CL_LOCKNAMELEN];
1641
1642         if (!nfscl_inited)
1643                 return;
1644         nfscl_filllockowner(p, own);
1645
1646         NFSLOCKCLSTATE();
1647         /*
1648          * Loop through all the clientids, looking for the OpenOwners.
1649          */
1650         LIST_FOREACH(clp, &nfsclhead, nfsc_list)
1651                 nfscl_cleanup_common(clp, own);
1652         NFSUNLOCKCLSTATE();
1653 }
1654 #endif  /* !__FreeBSD__ */
1655
1656 /*
1657  * Common code used by nfscl_cleanup() and nfscl_cleanupkext().
1658  * Must be called with CLSTATE lock held.
1659  */
1660 static void
1661 nfscl_cleanup_common(struct nfsclclient *clp, u_int8_t *own)
1662 {
1663         struct nfsclowner *owp, *nowp;
1664         struct nfsclopen *op;
1665         struct nfscllockowner *lp, *nlp;
1666         struct nfscldeleg *dp;
1667
1668         /* First, get rid of local locks on delegations. */
1669         TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1670                 LIST_FOREACH_SAFE(lp, &dp->nfsdl_lock, nfsl_list, nlp) {
1671                     if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
1672                         if ((lp->nfsl_rwlock.nfslock_lock & NFSV4LOCK_WANTED))
1673                             panic("nfscllckw");
1674                         nfscl_freelockowner(lp, 1);
1675                     }
1676                 }
1677         }
1678         owp = LIST_FIRST(&clp->nfsc_owner);
1679         while (owp != NULL) {
1680                 nowp = LIST_NEXT(owp, nfsow_list);
1681                 if (!NFSBCMP(owp->nfsow_owner, own,
1682                     NFSV4CL_LOCKNAMELEN)) {
1683                         /*
1684                          * If there are children that haven't closed the
1685                          * file descriptors yet, the opens will still be
1686                          * here. For that case, let the renew thread clear
1687                          * out the OpenOwner later.
1688                          */
1689                         if (LIST_EMPTY(&owp->nfsow_open))
1690                                 nfscl_freeopenowner(owp, 0);
1691                         else
1692                                 owp->nfsow_defunct = 1;
1693                 } else {
1694                         /* look for lockowners on other opens */
1695                         LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
1696                                 LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
1697                                         if (!NFSBCMP(lp->nfsl_owner, own,
1698                                             NFSV4CL_LOCKNAMELEN))
1699                                                 lp->nfsl_defunct = 1;
1700                                 }
1701                         }
1702                 }
1703                 owp = nowp;
1704         }
1705
1706         /* and check the defunct list */
1707         LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) {
1708                 if (!NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN))
1709                     lp->nfsl_defunct = 1;
1710         }
1711 }
1712
1713 #if defined(APPLEKEXT) || defined(__FreeBSD__)
1714 /*
1715  * Simulate the call nfscl_cleanup() by looking for open owners associated
1716  * with processes that no longer exist, since a call to nfscl_cleanup()
1717  * can't be patched into exit().
1718  */
1719 static void
1720 nfscl_cleanupkext(struct nfsclclient *clp)
1721 {
1722         struct nfsclowner *owp, *nowp;
1723         struct nfscllockowner *lp;
1724
1725         NFSPROCLISTLOCK();
1726         NFSLOCKCLSTATE();
1727         LIST_FOREACH_SAFE(owp, &clp->nfsc_owner, nfsow_list, nowp) {
1728                 if (nfscl_procdoesntexist(owp->nfsow_owner))
1729                         nfscl_cleanup_common(clp, owp->nfsow_owner);
1730         }
1731
1732         /* and check the defunct list */
1733         LIST_FOREACH(lp, &clp->nfsc_defunctlockowner, nfsl_list) {
1734                 if (nfscl_procdoesntexist(lp->nfsl_owner))
1735                         lp->nfsl_defunct = 1;
1736         }
1737         NFSUNLOCKCLSTATE();
1738         NFSPROCLISTUNLOCK();
1739 }
1740 #endif  /* APPLEKEXT || __FreeBSD__ */
1741
1742 static int      fake_global;    /* Used to force visibility of MNTK_UNMOUNTF */
1743 /*
1744  * Called from nfs umount to free up the clientid.
1745  */
1746 APPLESTATIC void
1747 nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
1748 {
1749         struct nfsclclient *clp;
1750         struct ucred *cred;
1751         int igotlock;
1752
1753         /*
1754          * For the case that matters, this is the thread that set
1755          * MNTK_UNMOUNTF, so it will see it set. The code that follows is
1756          * done to ensure that any thread executing nfscl_getcl() after
1757          * this time, will see MNTK_UNMOUNTF set. nfscl_getcl() uses the
1758          * mutex for NFSLOCKCLSTATE(), so it is "m" for the following
1759          * explanation, courtesy of Alan Cox.
1760          * What follows is a snippet from Alan Cox's email at:
1761          * http://docs.FreeBSD.org/cgi/
1762          *     mid.cgi?BANLkTikR3d65zPHo9==08ZfJ2vmqZucEvw
1763          * 
1764          * 1. Set MNTK_UNMOUNTF
1765          * 2. Acquire a standard FreeBSD mutex "m".
1766          * 3. Update some data structures.
1767          * 4. Release mutex "m".
1768          * 
1769          * Then, other threads that acquire "m" after step 4 has occurred will
1770          * see MNTK_UNMOUNTF as set.  But, other threads that beat thread X to
1771          * step 2 may or may not see MNTK_UNMOUNTF as set.
1772          */
1773         NFSLOCKCLSTATE();
1774         if ((nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF) != 0) {
1775                 fake_global++;
1776                 NFSUNLOCKCLSTATE();
1777                 NFSLOCKCLSTATE();
1778         }
1779
1780         clp = nmp->nm_clp;
1781         if (clp != NULL) {
1782                 if ((clp->nfsc_flags & NFSCLFLAGS_INITED) == 0)
1783                         panic("nfscl umount");
1784         
1785                 /*
1786                  * First, handshake with the nfscl renew thread, to terminate
1787                  * it.
1788                  */
1789                 clp->nfsc_flags |= NFSCLFLAGS_UMOUNT;
1790                 while (clp->nfsc_flags & NFSCLFLAGS_HASTHREAD)
1791                         (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT,
1792                             "nfsclumnt", hz);
1793         
1794                 /*
1795                  * Now, get the exclusive lock on the client state, so
1796                  * that no uses of the state are still in progress.
1797                  */
1798                 do {
1799                         igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1800                             NFSCLSTATEMUTEXPTR, NULL);
1801                 } while (!igotlock);
1802                 NFSUNLOCKCLSTATE();
1803         
1804                 /*
1805                  * Free up all the state. It will expire on the server, but
1806                  * maybe we should do a SetClientId/SetClientIdConfirm so
1807                  * the server throws it away?
1808                  */
1809                 LIST_REMOVE(clp, nfsc_list);
1810                 nfscl_delegreturnall(clp, p);
1811                 cred = newnfs_getcred();
1812                 (void) nfsrpc_setclient(nmp, clp, cred, p);
1813                 nfscl_cleanclient(clp);
1814                 nmp->nm_clp = NULL;
1815                 NFSFREECRED(cred);
1816                 FREE((caddr_t)clp, M_NFSCLCLIENT);
1817         } else
1818                 NFSUNLOCKCLSTATE();
1819 }
1820
1821 /*
1822  * This function is called when a server replies with NFSERR_STALECLIENTID
1823  * or NFSERR_STALESTATEID. It traverses the clientid lists, doing Opens
1824  * and Locks with reclaim. If these fail, it deletes the corresponding state.
1825  */
1826 static void
1827 nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
1828 {
1829         struct nfsclowner *owp, *nowp;
1830         struct nfsclopen *op, *nop;
1831         struct nfscllockowner *lp, *nlp;
1832         struct nfscllock *lop, *nlop;
1833         struct nfscldeleg *dp, *ndp, *tdp;
1834         struct nfsmount *nmp;
1835         struct ucred *tcred;
1836         struct nfsclopenhead extra_open;
1837         struct nfscldeleghead extra_deleg;
1838         struct nfsreq *rep;
1839         u_int64_t len;
1840         u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
1841         int igotlock = 0, error, trycnt, firstlock, s;
1842
1843         /*
1844          * First, lock the client structure, so everyone else will
1845          * block when trying to use state.
1846          */
1847         NFSLOCKCLSTATE();
1848         clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
1849         do {
1850                 igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
1851                     NFSCLSTATEMUTEXPTR, NULL);
1852         } while (!igotlock);
1853         NFSUNLOCKCLSTATE();
1854
1855         nmp = clp->nfsc_nmp;
1856         if (nmp == NULL)
1857                 panic("nfscl recover");
1858         trycnt = 5;
1859         do {
1860                 error = nfsrpc_setclient(nmp, clp, cred, p);
1861         } while ((error == NFSERR_STALECLIENTID ||
1862              error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
1863         if (error) {
1864                 nfscl_cleanclient(clp);
1865                 NFSLOCKCLSTATE();
1866                 clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
1867                     NFSCLFLAGS_RECOVER | NFSCLFLAGS_RECVRINPROG);
1868                 wakeup(&clp->nfsc_flags);
1869                 nfsv4_unlock(&clp->nfsc_lock, 0);
1870                 NFSUNLOCKCLSTATE();
1871                 return;
1872         }
1873         clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
1874         clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
1875
1876         /*
1877          * Mark requests already queued on the server, so that they don't
1878          * initiate another recovery cycle. Any requests already in the
1879          * queue that handle state information will have the old stale
1880          * clientid/stateid and will get a NFSERR_STALESTATEID or
1881          * NFSERR_STALECLIENTID reply from the server. This will be
1882          * translated to NFSERR_STALEDONTRECOVER when R_DONTRECOVER is set.
1883          */
1884         s = splsoftclock();
1885         NFSLOCKREQ();
1886         TAILQ_FOREACH(rep, &nfsd_reqq, r_chain) {
1887                 if (rep->r_nmp == nmp)
1888                         rep->r_flags |= R_DONTRECOVER;
1889         }
1890         NFSUNLOCKREQ();
1891         splx(s);
1892
1893         /* get rid of defunct lockowners */
1894         LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list, nlp) {
1895                 nfscl_freelockowner(lp, 0);
1896         }
1897
1898         /*
1899          * Now, mark all delegations "need reclaim".
1900          */
1901         TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
1902                 dp->nfsdl_flags |= NFSCLDL_NEEDRECLAIM;
1903
1904         TAILQ_INIT(&extra_deleg);
1905         LIST_INIT(&extra_open);
1906         /*
1907          * Now traverse the state lists, doing Open and Lock Reclaims.
1908          */
1909         tcred = newnfs_getcred();
1910         owp = LIST_FIRST(&clp->nfsc_owner);
1911         while (owp != NULL) {
1912             nowp = LIST_NEXT(owp, nfsow_list);
1913             owp->nfsow_seqid = 0;
1914             op = LIST_FIRST(&owp->nfsow_open);
1915             while (op != NULL) {
1916                 nop = LIST_NEXT(op, nfso_list);
1917                 if (error != NFSERR_NOGRACE) {
1918                     /* Search for a delegation to reclaim with the open */
1919                     TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1920                         if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1921                             continue;
1922                         if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
1923                             mode = NFSV4OPEN_ACCESSWRITE;
1924                             delegtype = NFSV4OPEN_DELEGATEWRITE;
1925                         } else {
1926                             mode = NFSV4OPEN_ACCESSREAD;
1927                             delegtype = NFSV4OPEN_DELEGATEREAD;
1928                         }
1929                         if ((op->nfso_mode & mode) == mode &&
1930                             op->nfso_fhlen == dp->nfsdl_fhlen &&
1931                             !NFSBCMP(op->nfso_fh, dp->nfsdl_fh, op->nfso_fhlen))
1932                             break;
1933                     }
1934                     ndp = dp;
1935                     if (dp == NULL)
1936                         delegtype = NFSV4OPEN_DELEGATENONE;
1937                     newnfs_copycred(&op->nfso_cred, tcred);
1938                     error = nfscl_tryopen(nmp, NULL, op->nfso_fh,
1939                         op->nfso_fhlen, op->nfso_fh, op->nfso_fhlen,
1940                         op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
1941                         tcred, p);
1942                     if (!error) {
1943                         /* Handle any replied delegation */
1944                         if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
1945                             || NFSMNT_RDONLY(nmp->nm_mountp))) {
1946                             if ((ndp->nfsdl_flags & NFSCLDL_WRITE))
1947                                 mode = NFSV4OPEN_ACCESSWRITE;
1948                             else
1949                                 mode = NFSV4OPEN_ACCESSREAD;
1950                             TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
1951                                 if (!(dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM))
1952                                     continue;
1953                                 if ((op->nfso_mode & mode) == mode &&
1954                                     op->nfso_fhlen == dp->nfsdl_fhlen &&
1955                                     !NFSBCMP(op->nfso_fh, dp->nfsdl_fh,
1956                                     op->nfso_fhlen)) {
1957                                     dp->nfsdl_stateid = ndp->nfsdl_stateid;
1958                                     dp->nfsdl_sizelimit = ndp->nfsdl_sizelimit;
1959                                     dp->nfsdl_ace = ndp->nfsdl_ace;
1960                                     dp->nfsdl_change = ndp->nfsdl_change;
1961                                     dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
1962                                     if ((ndp->nfsdl_flags & NFSCLDL_RECALL))
1963                                         dp->nfsdl_flags |= NFSCLDL_RECALL;
1964                                     FREE((caddr_t)ndp, M_NFSCLDELEG);
1965                                     ndp = NULL;
1966                                     break;
1967                                 }
1968                             }
1969                         }
1970                         if (ndp != NULL)
1971                             TAILQ_INSERT_HEAD(&extra_deleg, ndp, nfsdl_list);
1972
1973                         /* and reclaim all byte range locks */
1974                         lp = LIST_FIRST(&op->nfso_lock);
1975                         while (lp != NULL) {
1976                             nlp = LIST_NEXT(lp, nfsl_list);
1977                             lp->nfsl_seqid = 0;
1978                             firstlock = 1;
1979                             lop = LIST_FIRST(&lp->nfsl_lock);
1980                             while (lop != NULL) {
1981                                 nlop = LIST_NEXT(lop, nfslo_list);
1982                                 if (lop->nfslo_end == NFS64BITSSET)
1983                                     len = NFS64BITSSET;
1984                                 else
1985                                     len = lop->nfslo_end - lop->nfslo_first;
1986                                 if (error != NFSERR_NOGRACE)
1987                                     error = nfscl_trylock(nmp, NULL,
1988                                         op->nfso_fh, op->nfso_fhlen, lp,
1989                                         firstlock, 1, lop->nfslo_first, len,
1990                                         lop->nfslo_type, tcred, p);
1991                                 if (error != 0)
1992                                     nfscl_freelock(lop, 0);
1993                                 else
1994                                     firstlock = 0;
1995                                 lop = nlop;
1996                             }
1997                             /* If no locks, but a lockowner, just delete it. */
1998                             if (LIST_EMPTY(&lp->nfsl_lock))
1999                                 nfscl_freelockowner(lp, 0);
2000                             lp = nlp;
2001                         }
2002                     } else {
2003                         nfscl_freeopen(op, 0);
2004                     }
2005                 }
2006                 op = nop;
2007             }
2008             owp = nowp;
2009         }
2010
2011         /*
2012          * Now, try and get any delegations not yet reclaimed by cobbling
2013          * to-gether an appropriate open.
2014          */
2015         nowp = NULL;
2016         dp = TAILQ_FIRST(&clp->nfsc_deleg);
2017         while (dp != NULL) {
2018             ndp = TAILQ_NEXT(dp, nfsdl_list);
2019             if ((dp->nfsdl_flags & NFSCLDL_NEEDRECLAIM)) {
2020                 if (nowp == NULL) {
2021                     MALLOC(nowp, struct nfsclowner *,
2022                         sizeof (struct nfsclowner), M_NFSCLOWNER, M_WAITOK);
2023                     /*
2024                      * Name must be as long an largest possible
2025                      * NFSV4CL_LOCKNAMELEN. 12 for now.
2026                      */
2027                     NFSBCOPY("RECLAIMDELEG", nowp->nfsow_owner,
2028                         NFSV4CL_LOCKNAMELEN);
2029                     LIST_INIT(&nowp->nfsow_open);
2030                     nowp->nfsow_clp = clp;
2031                     nowp->nfsow_seqid = 0;
2032                     nowp->nfsow_defunct = 0;
2033                     nfscl_lockinit(&nowp->nfsow_rwlock);
2034                 }
2035                 nop = NULL;
2036                 if (error != NFSERR_NOGRACE) {
2037                     MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
2038                         dp->nfsdl_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
2039                     nop->nfso_own = nowp;
2040                     if ((dp->nfsdl_flags & NFSCLDL_WRITE)) {
2041                         nop->nfso_mode = NFSV4OPEN_ACCESSWRITE;
2042                         delegtype = NFSV4OPEN_DELEGATEWRITE;
2043                     } else {
2044                         nop->nfso_mode = NFSV4OPEN_ACCESSREAD;
2045                         delegtype = NFSV4OPEN_DELEGATEREAD;
2046                     }
2047                     nop->nfso_opencnt = 0;
2048                     nop->nfso_posixlock = 1;
2049                     nop->nfso_fhlen = dp->nfsdl_fhlen;
2050                     NFSBCOPY(dp->nfsdl_fh, nop->nfso_fh, dp->nfsdl_fhlen);
2051                     LIST_INIT(&nop->nfso_lock);
2052                     nop->nfso_stateid.seqid = 0;
2053                     nop->nfso_stateid.other[0] = 0;
2054                     nop->nfso_stateid.other[1] = 0;
2055                     nop->nfso_stateid.other[2] = 0;
2056                     newnfs_copycred(&dp->nfsdl_cred, tcred);
2057                     newnfs_copyincred(tcred, &nop->nfso_cred);
2058                     tdp = NULL;
2059                     error = nfscl_tryopen(nmp, NULL, nop->nfso_fh,
2060                         nop->nfso_fhlen, nop->nfso_fh, nop->nfso_fhlen,
2061                         nop->nfso_mode, nop, NULL, 0, &tdp, 1,
2062                         delegtype, tcred, p);
2063                     if (tdp != NULL) {
2064                         if ((tdp->nfsdl_flags & NFSCLDL_WRITE))
2065                             mode = NFSV4OPEN_ACCESSWRITE;
2066                         else
2067                             mode = NFSV4OPEN_ACCESSREAD;
2068                         if ((nop->nfso_mode & mode) == mode &&
2069                             nop->nfso_fhlen == tdp->nfsdl_fhlen &&
2070                             !NFSBCMP(nop->nfso_fh, tdp->nfsdl_fh,
2071                             nop->nfso_fhlen)) {
2072                             dp->nfsdl_stateid = tdp->nfsdl_stateid;
2073                             dp->nfsdl_sizelimit = tdp->nfsdl_sizelimit;
2074                             dp->nfsdl_ace = tdp->nfsdl_ace;
2075                             dp->nfsdl_change = tdp->nfsdl_change;
2076                             dp->nfsdl_flags &= ~NFSCLDL_NEEDRECLAIM;
2077                             if ((tdp->nfsdl_flags & NFSCLDL_RECALL))
2078                                 dp->nfsdl_flags |= NFSCLDL_RECALL;
2079                             FREE((caddr_t)tdp, M_NFSCLDELEG);
2080                         } else {
2081                             TAILQ_INSERT_HEAD(&extra_deleg, tdp, nfsdl_list);
2082                         }
2083                     }
2084                 }
2085                 if (error) {
2086                     if (nop != NULL)
2087                         FREE((caddr_t)nop, M_NFSCLOPEN);
2088                     /*
2089                      * Couldn't reclaim it, so throw the state
2090                      * away. Ouch!!
2091                      */
2092                     nfscl_cleandeleg(dp);
2093                     nfscl_freedeleg(&clp->nfsc_deleg, dp);
2094                 } else {
2095                     LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
2096                 }
2097             }
2098             dp = ndp;
2099         }
2100
2101         /*
2102          * Now, get rid of extra Opens and Delegations.
2103          */
2104         LIST_FOREACH_SAFE(op, &extra_open, nfso_list, nop) {
2105                 do {
2106                         newnfs_copycred(&op->nfso_cred, tcred);
2107                         error = nfscl_tryclose(op, tcred, nmp, p);
2108                         if (error == NFSERR_GRACE)
2109                                 (void) nfs_catnap(PZERO, error, "nfsexcls");
2110                 } while (error == NFSERR_GRACE);
2111                 LIST_REMOVE(op, nfso_list);
2112                 FREE((caddr_t)op, M_NFSCLOPEN);
2113         }
2114         if (nowp != NULL)
2115                 FREE((caddr_t)nowp, M_NFSCLOWNER);
2116
2117         TAILQ_FOREACH_SAFE(dp, &extra_deleg, nfsdl_list, ndp) {
2118                 do {
2119                         newnfs_copycred(&dp->nfsdl_cred, tcred);
2120                         error = nfscl_trydelegreturn(dp, tcred, nmp, p);
2121                         if (error == NFSERR_GRACE)
2122                                 (void) nfs_catnap(PZERO, error, "nfsexdlg");
2123                 } while (error == NFSERR_GRACE);
2124                 TAILQ_REMOVE(&extra_deleg, dp, nfsdl_list);
2125                 FREE((caddr_t)dp, M_NFSCLDELEG);
2126         }
2127
2128         NFSLOCKCLSTATE();
2129         clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
2130         wakeup(&clp->nfsc_flags);
2131         nfsv4_unlock(&clp->nfsc_lock, 0);
2132         NFSUNLOCKCLSTATE();
2133         NFSFREECRED(tcred);
2134 }
2135
2136 /*
2137  * This function is called when a server replies with NFSERR_EXPIRED.
2138  * It deletes all state for the client and does a fresh SetClientId/confirm.
2139  * XXX Someday it should post a signal to the process(es) that hold the
2140  * state, so they know that lock state has been lost.
2141  */
2142 APPLESTATIC int
2143 nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
2144 {
2145         struct nfscllockowner *lp, *nlp;
2146         struct nfsmount *nmp;
2147         struct ucred *cred;
2148         int igotlock = 0, error, trycnt;
2149
2150         /*
2151          * If the clientid has gone away or a new SetClientid has already
2152          * been done, just return ok.
2153          */
2154         if (clp == NULL || clidrev != clp->nfsc_clientidrev)
2155                 return (0);
2156
2157         /*
2158          * First, lock the client structure, so everyone else will
2159          * block when trying to use state. Also, use NFSCLFLAGS_EXPIREIT so
2160          * that only one thread does the work.
2161          */
2162         NFSLOCKCLSTATE();
2163         clp->nfsc_flags |= NFSCLFLAGS_EXPIREIT;
2164         do {
2165                 igotlock = nfsv4_lock(&clp->nfsc_lock, 1, NULL,
2166                     NFSCLSTATEMUTEXPTR, NULL);
2167         } while (!igotlock && (clp->nfsc_flags & NFSCLFLAGS_EXPIREIT));
2168         if ((clp->nfsc_flags & NFSCLFLAGS_EXPIREIT) == 0) {
2169                 if (igotlock)
2170                         nfsv4_unlock(&clp->nfsc_lock, 0);
2171                 NFSUNLOCKCLSTATE();
2172                 return (0);
2173         }
2174         clp->nfsc_flags |= NFSCLFLAGS_RECVRINPROG;
2175         NFSUNLOCKCLSTATE();
2176
2177         nmp = clp->nfsc_nmp;
2178         if (nmp == NULL)
2179                 panic("nfscl expired");
2180         cred = newnfs_getcred();
2181         trycnt = 5;
2182         do {
2183                 error = nfsrpc_setclient(nmp, clp, cred, p);
2184         } while ((error == NFSERR_STALECLIENTID ||
2185              error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
2186         if (error) {
2187                 /*
2188                  * Clear out any state.
2189                  */
2190                 nfscl_cleanclient(clp);
2191                 NFSLOCKCLSTATE();
2192                 clp->nfsc_flags &= ~(NFSCLFLAGS_HASCLIENTID |
2193                     NFSCLFLAGS_RECOVER);
2194         } else {
2195                 /* get rid of defunct lockowners */
2196                 LIST_FOREACH_SAFE(lp, &clp->nfsc_defunctlockowner, nfsl_list,
2197                     nlp) {
2198                         nfscl_freelockowner(lp, 0);
2199                 }
2200
2201                 /*
2202                  * Expire the state for the client.
2203                  */
2204                 nfscl_expireclient(clp, nmp, cred, p);
2205                 NFSLOCKCLSTATE();
2206                 clp->nfsc_flags |= NFSCLFLAGS_HASCLIENTID;
2207                 clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2208         }
2209         clp->nfsc_flags &= ~(NFSCLFLAGS_EXPIREIT | NFSCLFLAGS_RECVRINPROG);
2210         wakeup(&clp->nfsc_flags);
2211         nfsv4_unlock(&clp->nfsc_lock, 0);
2212         NFSUNLOCKCLSTATE();
2213         NFSFREECRED(cred);
2214         return (error);
2215 }
2216
2217 /*
2218  * This function inserts a lock in the list after insert_lop.
2219  */
2220 static void
2221 nfscl_insertlock(struct nfscllockowner *lp, struct nfscllock *new_lop,
2222     struct nfscllock *insert_lop, int local)
2223 {
2224
2225         if ((struct nfscllockowner *)insert_lop == lp)
2226                 LIST_INSERT_HEAD(&lp->nfsl_lock, new_lop, nfslo_list);
2227         else
2228                 LIST_INSERT_AFTER(insert_lop, new_lop, nfslo_list);
2229         if (local)
2230                 newnfsstats.cllocallocks++;
2231         else
2232                 newnfsstats.cllocks++;
2233 }
2234
2235 /*
2236  * This function updates the locking for a lock owner and given file. It
2237  * maintains a list of lock ranges ordered on increasing file offset that
2238  * are NFSCLLOCK_READ or NFSCLLOCK_WRITE and non-overlapping (aka POSIX style).
2239  * It always adds new_lop to the list and sometimes uses the one pointed
2240  * at by other_lopp.
2241  * Returns 1 if the locks were modified, 0 otherwise.
2242  */
2243 static int
2244 nfscl_updatelock(struct nfscllockowner *lp, struct nfscllock **new_lopp,
2245     struct nfscllock **other_lopp, int local)
2246 {
2247         struct nfscllock *new_lop = *new_lopp;
2248         struct nfscllock *lop, *tlop, *ilop;
2249         struct nfscllock *other_lop;
2250         int unlock = 0, modified = 0;
2251         u_int64_t tmp;
2252
2253         /*
2254          * Work down the list until the lock is merged.
2255          */
2256         if (new_lop->nfslo_type == F_UNLCK)
2257                 unlock = 1;
2258         ilop = (struct nfscllock *)lp;
2259         lop = LIST_FIRST(&lp->nfsl_lock);
2260         while (lop != NULL) {
2261             /*
2262              * Only check locks for this file that aren't before the start of
2263              * new lock's range.
2264              */
2265             if (lop->nfslo_end >= new_lop->nfslo_first) {
2266                 if (new_lop->nfslo_end < lop->nfslo_first) {
2267                     /*
2268                      * If the new lock ends before the start of the
2269                      * current lock's range, no merge, just insert
2270                      * the new lock.
2271                      */
2272                     break;
2273                 }
2274                 if (new_lop->nfslo_type == lop->nfslo_type ||
2275                     (new_lop->nfslo_first <= lop->nfslo_first &&
2276                      new_lop->nfslo_end >= lop->nfslo_end)) {
2277                     /*
2278                      * This lock can be absorbed by the new lock/unlock.
2279                      * This happens when it covers the entire range
2280                      * of the old lock or is contiguous
2281                      * with the old lock and is of the same type or an
2282                      * unlock.
2283                      */
2284                     if (new_lop->nfslo_type != lop->nfslo_type ||
2285                         new_lop->nfslo_first != lop->nfslo_first ||
2286                         new_lop->nfslo_end != lop->nfslo_end)
2287                         modified = 1;
2288                     if (lop->nfslo_first < new_lop->nfslo_first)
2289                         new_lop->nfslo_first = lop->nfslo_first;
2290                     if (lop->nfslo_end > new_lop->nfslo_end)
2291                         new_lop->nfslo_end = lop->nfslo_end;
2292                     tlop = lop;
2293                     lop = LIST_NEXT(lop, nfslo_list);
2294                     nfscl_freelock(tlop, local);
2295                     continue;
2296                 }
2297
2298                 /*
2299                  * All these cases are for contiguous locks that are not the
2300                  * same type, so they can't be merged.
2301                  */
2302                 if (new_lop->nfslo_first <= lop->nfslo_first) {
2303                     /*
2304                      * This case is where the new lock overlaps with the
2305                      * first part of the old lock. Move the start of the
2306                      * old lock to just past the end of the new lock. The
2307                      * new lock will be inserted in front of the old, since
2308                      * ilop hasn't been updated. (We are done now.)
2309                      */
2310                     if (lop->nfslo_first != new_lop->nfslo_end) {
2311                         lop->nfslo_first = new_lop->nfslo_end;
2312                         modified = 1;
2313                     }
2314                     break;
2315                 }
2316                 if (new_lop->nfslo_end >= lop->nfslo_end) {
2317                     /*
2318                      * This case is where the new lock overlaps with the
2319                      * end of the old lock's range. Move the old lock's
2320                      * end to just before the new lock's first and insert
2321                      * the new lock after the old lock.
2322                      * Might not be done yet, since the new lock could
2323                      * overlap further locks with higher ranges.
2324                      */
2325                     if (lop->nfslo_end != new_lop->nfslo_first) {
2326                         lop->nfslo_end = new_lop->nfslo_first;
2327                         modified = 1;
2328                     }
2329                     ilop = lop;
2330                     lop = LIST_NEXT(lop, nfslo_list);
2331                     continue;
2332                 }
2333                 /*
2334                  * The final case is where the new lock's range is in the
2335                  * middle of the current lock's and splits the current lock
2336                  * up. Use *other_lopp to handle the second part of the
2337                  * split old lock range. (We are done now.)
2338                  * For unlock, we use new_lop as other_lop and tmp, since
2339                  * other_lop and new_lop are the same for this case.
2340                  * We noted the unlock case above, so we don't need
2341                  * new_lop->nfslo_type any longer.
2342                  */
2343                 tmp = new_lop->nfslo_first;
2344                 if (unlock) {
2345                     other_lop = new_lop;
2346                     *new_lopp = NULL;
2347                 } else {
2348                     other_lop = *other_lopp;
2349                     *other_lopp = NULL;
2350                 }
2351                 other_lop->nfslo_first = new_lop->nfslo_end;
2352                 other_lop->nfslo_end = lop->nfslo_end;
2353                 other_lop->nfslo_type = lop->nfslo_type;
2354                 lop->nfslo_end = tmp;
2355                 nfscl_insertlock(lp, other_lop, lop, local);
2356                 ilop = lop;
2357                 modified = 1;
2358                 break;
2359             }
2360             ilop = lop;
2361             lop = LIST_NEXT(lop, nfslo_list);
2362             if (lop == NULL)
2363                 break;
2364         }
2365
2366         /*
2367          * Insert the new lock in the list at the appropriate place.
2368          */
2369         if (!unlock) {
2370                 nfscl_insertlock(lp, new_lop, ilop, local);
2371                 *new_lopp = NULL;
2372                 modified = 1;
2373         }
2374         return (modified);
2375 }
2376
2377 /*
2378  * This function must be run as a kernel thread.
2379  * It does Renew Ops and recovery, when required.
2380  */
2381 APPLESTATIC void
2382 nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
2383 {
2384         struct nfsclowner *owp, *nowp;
2385         struct nfsclopen *op;
2386         struct nfscllockowner *lp, *nlp, *olp;
2387         struct nfscldeleghead dh;
2388         struct nfscllockownerhead lh;
2389         struct nfscldeleg *dp, *ndp;
2390         struct ucred *cred;
2391         u_int32_t clidrev;
2392         int error, cbpathdown, islept, igotlock, ret, clearok;
2393         uint32_t recover_done_time = 0;
2394
2395         cred = newnfs_getcred();
2396         NFSLOCKCLSTATE();
2397         clp->nfsc_flags |= NFSCLFLAGS_HASTHREAD;
2398         NFSUNLOCKCLSTATE();
2399         for(;;) {
2400                 newnfs_setroot(cred);
2401                 cbpathdown = 0;
2402                 if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
2403                         /*
2404                          * Only allow one recover within 1/2 of the lease
2405                          * duration (nfsc_renew).
2406                          */
2407                         if (recover_done_time < NFSD_MONOSEC) {
2408                                 recover_done_time = NFSD_MONOSEC +
2409                                     clp->nfsc_renew;
2410                                 nfscl_recover(clp, cred, p);
2411                         } else {
2412                                 NFSLOCKCLSTATE();
2413                                 clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
2414                                 NFSUNLOCKCLSTATE();
2415                         }
2416                 }
2417                 if (clp->nfsc_expire <= NFSD_MONOSEC &&
2418                     (clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
2419                         clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
2420                         clidrev = clp->nfsc_clientidrev;
2421                         error = nfsrpc_renew(clp, cred, p);
2422                         if (error == NFSERR_CBPATHDOWN)
2423                             cbpathdown = 1;
2424                         else if (error == NFSERR_STALECLIENTID) {
2425                             NFSLOCKCLSTATE();
2426                             clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2427                             NFSUNLOCKCLSTATE();
2428                         } else if (error == NFSERR_EXPIRED)
2429                             (void) nfscl_hasexpired(clp, clidrev, p);
2430                 }
2431
2432                 LIST_INIT(&lh);
2433                 TAILQ_INIT(&dh);
2434                 NFSLOCKCLSTATE();
2435                 if (cbpathdown)
2436                         /* It's a Total Recall! */
2437                         nfscl_totalrecall(clp);
2438
2439                 /*
2440                  * Now, handle defunct owners.
2441                  */
2442                 owp = LIST_FIRST(&clp->nfsc_owner);
2443                 while (owp != NULL) {
2444                     nowp = LIST_NEXT(owp, nfsow_list);
2445                     if (LIST_EMPTY(&owp->nfsow_open)) {
2446                         if (owp->nfsow_defunct)
2447                             nfscl_freeopenowner(owp, 0);
2448                     } else {
2449                         LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2450                             lp = LIST_FIRST(&op->nfso_lock);
2451                             while (lp != NULL) {
2452                                 nlp = LIST_NEXT(lp, nfsl_list);
2453                                 if (lp->nfsl_defunct &&
2454                                     LIST_EMPTY(&lp->nfsl_lock)) {
2455                                     LIST_FOREACH(olp, &lh, nfsl_list) {
2456                                         if (!NFSBCMP(olp->nfsl_owner,
2457                                             lp->nfsl_owner,NFSV4CL_LOCKNAMELEN))
2458                                             break;
2459                                     }
2460                                     if (olp == NULL) {
2461                                         LIST_REMOVE(lp, nfsl_list);
2462                                         LIST_INSERT_HEAD(&lh, lp, nfsl_list);
2463                                     } else {
2464                                         nfscl_freelockowner(lp, 0);
2465                                     }
2466                                 }
2467                                 lp = nlp;
2468                             }
2469                         }
2470                     }
2471                     owp = nowp;
2472                 }
2473
2474                 /* also search the defunct list */
2475                 lp = LIST_FIRST(&clp->nfsc_defunctlockowner);
2476                 while (lp != NULL) {
2477                     nlp = LIST_NEXT(lp, nfsl_list);
2478                     if (lp->nfsl_defunct) {
2479                         LIST_FOREACH(olp, &lh, nfsl_list) {
2480                             if (!NFSBCMP(olp->nfsl_owner, lp->nfsl_owner,
2481                                 NFSV4CL_LOCKNAMELEN))
2482                                 break;
2483                         }
2484                         if (olp == NULL) {
2485                             LIST_REMOVE(lp, nfsl_list);
2486                             LIST_INSERT_HEAD(&lh, lp, nfsl_list);
2487                         } else {
2488                             nfscl_freelockowner(lp, 0);
2489                         }
2490                     }
2491                     lp = nlp;
2492                 }
2493                 /* and release defunct lock owners */
2494                 LIST_FOREACH_SAFE(lp, &lh, nfsl_list, nlp) {
2495                     nfscl_freelockowner(lp, 0);
2496                 }
2497
2498                 /*
2499                  * Do the recall on any delegations. To avoid trouble, always
2500                  * come back up here after having slept.
2501                  */
2502                 igotlock = 0;
2503 tryagain:
2504                 dp = TAILQ_FIRST(&clp->nfsc_deleg);
2505                 while (dp != NULL) {
2506                         ndp = TAILQ_NEXT(dp, nfsdl_list);
2507                         if ((dp->nfsdl_flags & NFSCLDL_RECALL)) {
2508                                 /*
2509                                  * Wait for outstanding I/O ops to be done.
2510                                  */
2511                                 if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
2512                                     if (igotlock) {
2513                                         nfsv4_unlock(&clp->nfsc_lock, 0);
2514                                         igotlock = 0;
2515                                     }
2516                                     dp->nfsdl_rwlock.nfslock_lock |=
2517                                         NFSV4LOCK_WANTED;
2518                                     (void) nfsmsleep(&dp->nfsdl_rwlock,
2519                                         NFSCLSTATEMUTEXPTR, PZERO, "nfscld",
2520                                         NULL);
2521                                     goto tryagain;
2522                                 }
2523                                 while (!igotlock) {
2524                                     igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
2525                                         &islept, NFSCLSTATEMUTEXPTR, NULL);
2526                                     if (islept)
2527                                         goto tryagain;
2528                                 }
2529                                 NFSUNLOCKCLSTATE();
2530                                 newnfs_copycred(&dp->nfsdl_cred, cred);
2531                                 ret = nfscl_recalldeleg(clp, clp->nfsc_nmp, dp,
2532                                     NULL, cred, p, 1);
2533                                 if (!ret) {
2534                                     nfscl_cleandeleg(dp);
2535                                     TAILQ_REMOVE(&clp->nfsc_deleg, dp,
2536                                         nfsdl_list);
2537                                     LIST_REMOVE(dp, nfsdl_hash);
2538                                     TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2539                                     nfscl_delegcnt--;
2540                                     newnfsstats.cldelegates--;
2541                                 }
2542                                 NFSLOCKCLSTATE();
2543                         }
2544                         dp = ndp;
2545                 }
2546
2547                 /*
2548                  * Clear out old delegations, if we are above the high water
2549                  * mark. Only clear out ones with no state related to them.
2550                  * The tailq list is in LRU order.
2551                  */
2552                 dp = TAILQ_LAST(&clp->nfsc_deleg, nfscldeleghead);
2553                 while (nfscl_delegcnt > nfscl_deleghighwater && dp != NULL) {
2554                     ndp = TAILQ_PREV(dp, nfscldeleghead, nfsdl_list);
2555                     if (dp->nfsdl_rwlock.nfslock_usecnt == 0 &&
2556                         dp->nfsdl_rwlock.nfslock_lock == 0 &&
2557                         dp->nfsdl_timestamp < NFSD_MONOSEC &&
2558                         (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_ZAPPED |
2559                           NFSCLDL_NEEDRECLAIM | NFSCLDL_DELEGRET)) == 0) {
2560                         clearok = 1;
2561                         LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2562                             op = LIST_FIRST(&owp->nfsow_open);
2563                             if (op != NULL) {
2564                                 clearok = 0;
2565                                 break;
2566                             }
2567                         }
2568                         if (clearok) {
2569                             LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
2570                                 if (!LIST_EMPTY(&lp->nfsl_lock)) {
2571                                     clearok = 0;
2572                                     break;
2573                                 }
2574                             }
2575                         }
2576                         if (clearok) {
2577                             TAILQ_REMOVE(&clp->nfsc_deleg, dp, nfsdl_list);
2578                             LIST_REMOVE(dp, nfsdl_hash);
2579                             TAILQ_INSERT_HEAD(&dh, dp, nfsdl_list);
2580                             nfscl_delegcnt--;
2581                             newnfsstats.cldelegates--;
2582                         }
2583                     }
2584                     dp = ndp;
2585                 }
2586                 if (igotlock)
2587                         nfsv4_unlock(&clp->nfsc_lock, 0);
2588                 NFSUNLOCKCLSTATE();
2589
2590                 /*
2591                  * Delegreturn any delegations cleaned out or recalled.
2592                  */
2593                 TAILQ_FOREACH_SAFE(dp, &dh, nfsdl_list, ndp) {
2594                         newnfs_copycred(&dp->nfsdl_cred, cred);
2595                         (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
2596                         TAILQ_REMOVE(&dh, dp, nfsdl_list);
2597                         FREE((caddr_t)dp, M_NFSCLDELEG);
2598                 }
2599
2600 #if defined(APPLEKEXT) || defined(__FreeBSD__)
2601                 /*
2602                  * Simulate the calls to nfscl_cleanup() when a process
2603                  * exits, since the call can't be patched into exit().
2604                  */
2605                 {
2606                         struct timespec mytime;
2607                         static time_t prevsec = 0;
2608
2609                         NFSGETNANOTIME(&mytime);
2610                         if (prevsec != mytime.tv_sec) {
2611                                 prevsec = mytime.tv_sec;
2612                                 nfscl_cleanupkext(clp);
2613                         }
2614                 }
2615 #endif  /* APPLEKEXT || __FreeBSD__ */
2616
2617                 NFSLOCKCLSTATE();
2618                 if ((clp->nfsc_flags & NFSCLFLAGS_RECOVER) == 0)
2619                         (void)mtx_sleep(clp, NFSCLSTATEMUTEXPTR, PWAIT, "nfscl",
2620                             hz);
2621                 if (clp->nfsc_flags & NFSCLFLAGS_UMOUNT) {
2622                         clp->nfsc_flags &= ~NFSCLFLAGS_HASTHREAD;
2623                         NFSUNLOCKCLSTATE();
2624                         NFSFREECRED(cred);
2625                         wakeup((caddr_t)clp);
2626                         return;
2627                 }
2628                 NFSUNLOCKCLSTATE();
2629         }
2630 }
2631
2632 /*
2633  * Initiate state recovery. Called when NFSERR_STALECLIENTID or
2634  * NFSERR_STALESTATEID is received.
2635  */
2636 APPLESTATIC void
2637 nfscl_initiate_recovery(struct nfsclclient *clp)
2638 {
2639
2640         if (clp == NULL)
2641                 return;
2642         NFSLOCKCLSTATE();
2643         clp->nfsc_flags |= NFSCLFLAGS_RECOVER;
2644         NFSUNLOCKCLSTATE();
2645         wakeup((caddr_t)clp);
2646 }
2647
2648 /*
2649  * Dump out the state stuff for debugging.
2650  */
2651 APPLESTATIC void
2652 nfscl_dumpstate(struct nfsmount *nmp, int openowner, int opens,
2653     int lockowner, int locks)
2654 {
2655         struct nfsclclient *clp;
2656         struct nfsclowner *owp;
2657         struct nfsclopen *op;
2658         struct nfscllockowner *lp;
2659         struct nfscllock *lop;
2660         struct nfscldeleg *dp;
2661
2662         clp = nmp->nm_clp;
2663         if (clp == NULL) {
2664                 printf("nfscl dumpstate NULL clp\n");
2665                 return;
2666         }
2667         NFSLOCKCLSTATE();
2668         TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
2669           LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2670             if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2671                 printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2672                     owp->nfsow_owner[0], owp->nfsow_owner[1],
2673                     owp->nfsow_owner[2], owp->nfsow_owner[3],
2674                     owp->nfsow_seqid);
2675             LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2676                 if (opens)
2677                     printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2678                         op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2679                         op->nfso_stateid.other[2], op->nfso_opencnt,
2680                         op->nfso_fh[12]);
2681                 LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2682                     if (lockowner)
2683                         printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2684                             lp->nfsl_owner[0], lp->nfsl_owner[1],
2685                             lp->nfsl_owner[2], lp->nfsl_owner[3],
2686                             lp->nfsl_seqid,
2687                             lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2688                             lp->nfsl_stateid.other[2]);
2689                     LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2690                         if (locks)
2691 #ifdef __FreeBSD__
2692                             printf("lck typ=%d fst=%ju end=%ju\n",
2693                                 lop->nfslo_type, (intmax_t)lop->nfslo_first,
2694                                 (intmax_t)lop->nfslo_end);
2695 #else
2696                             printf("lck typ=%d fst=%qd end=%qd\n",
2697                                 lop->nfslo_type, lop->nfslo_first,
2698                                 lop->nfslo_end);
2699 #endif
2700                     }
2701                 }
2702             }
2703           }
2704         }
2705         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2706             if (openowner && !LIST_EMPTY(&owp->nfsow_open))
2707                 printf("owner=0x%x 0x%x 0x%x 0x%x seqid=%d\n",
2708                     owp->nfsow_owner[0], owp->nfsow_owner[1],
2709                     owp->nfsow_owner[2], owp->nfsow_owner[3],
2710                     owp->nfsow_seqid);
2711             LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2712                 if (opens)
2713                     printf("open st=0x%x 0x%x 0x%x cnt=%d fh12=0x%x\n",
2714                         op->nfso_stateid.other[0], op->nfso_stateid.other[1],
2715                         op->nfso_stateid.other[2], op->nfso_opencnt,
2716                         op->nfso_fh[12]);
2717                 LIST_FOREACH(lp, &op->nfso_lock, nfsl_list) {
2718                     if (lockowner)
2719                         printf("lckown=0x%x 0x%x 0x%x 0x%x seqid=%d st=0x%x 0x%x 0x%x\n",
2720                             lp->nfsl_owner[0], lp->nfsl_owner[1],
2721                             lp->nfsl_owner[2], lp->nfsl_owner[3],
2722                             lp->nfsl_seqid,
2723                             lp->nfsl_stateid.other[0], lp->nfsl_stateid.other[1],
2724                             lp->nfsl_stateid.other[2]);
2725                     LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
2726                         if (locks)
2727 #ifdef __FreeBSD__
2728                             printf("lck typ=%d fst=%ju end=%ju\n",
2729                                 lop->nfslo_type, (intmax_t)lop->nfslo_first,
2730                                 (intmax_t)lop->nfslo_end);
2731 #else
2732                             printf("lck typ=%d fst=%qd end=%qd\n",
2733                                 lop->nfslo_type, lop->nfslo_first,
2734                                 lop->nfslo_end);
2735 #endif
2736                     }
2737                 }
2738             }
2739         }
2740         NFSUNLOCKCLSTATE();
2741 }
2742
2743 /*
2744  * Check for duplicate open owners and opens.
2745  * (Only used as a diagnostic aid.)
2746  */
2747 APPLESTATIC void
2748 nfscl_dupopen(vnode_t vp, int dupopens)
2749 {
2750         struct nfsclclient *clp;
2751         struct nfsclowner *owp, *owp2;
2752         struct nfsclopen *op, *op2;
2753         struct nfsfh *nfhp;
2754
2755         clp = VFSTONFS(vnode_mount(vp))->nm_clp;
2756         if (clp == NULL) {
2757                 printf("nfscl dupopen NULL clp\n");
2758                 return;
2759         }
2760         nfhp = VTONFS(vp)->n_fhp;
2761         NFSLOCKCLSTATE();
2762
2763         /*
2764          * First, search for duplicate owners.
2765          * These should never happen!
2766          */
2767         LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2768             LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2769                 if (owp != owp2 &&
2770                     !NFSBCMP(owp->nfsow_owner, owp2->nfsow_owner,
2771                     NFSV4CL_LOCKNAMELEN)) {
2772                         NFSUNLOCKCLSTATE();
2773                         printf("DUP OWNER\n");
2774                         nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0, 0);
2775                         return;
2776                 }
2777             }
2778         }
2779
2780         /*
2781          * Now, search for duplicate stateids.
2782          * These shouldn't happen, either.
2783          */
2784         LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2785             LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2786                 LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2787                     LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2788                         if (op != op2 &&
2789                             (op->nfso_stateid.other[0] != 0 ||
2790                              op->nfso_stateid.other[1] != 0 ||
2791                              op->nfso_stateid.other[2] != 0) &&
2792                             op->nfso_stateid.other[0] == op2->nfso_stateid.other[0] &&
2793                             op->nfso_stateid.other[1] == op2->nfso_stateid.other[1] &&
2794                             op->nfso_stateid.other[2] == op2->nfso_stateid.other[2]) {
2795                             NFSUNLOCKCLSTATE();
2796                             printf("DUP STATEID\n");
2797                             nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1, 0,
2798                                 0);
2799                             return;
2800                         }
2801                     }
2802                 }
2803             }
2804         }
2805
2806         /*
2807          * Now search for duplicate opens.
2808          * Duplicate opens for the same owner
2809          * should never occur. Other duplicates are
2810          * possible and are checked for if "dupopens"
2811          * is true.
2812          */
2813         LIST_FOREACH(owp2, &clp->nfsc_owner, nfsow_list) {
2814             LIST_FOREACH(op2, &owp2->nfsow_open, nfso_list) {
2815                 if (nfhp->nfh_len == op2->nfso_fhlen &&
2816                     !NFSBCMP(nfhp->nfh_fh, op2->nfso_fh, nfhp->nfh_len)) {
2817                     LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2818                         LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2819                             if (op != op2 && nfhp->nfh_len == op->nfso_fhlen &&
2820                                 !NFSBCMP(nfhp->nfh_fh, op->nfso_fh, nfhp->nfh_len) &&
2821                                 (!NFSBCMP(op->nfso_own->nfsow_owner,
2822                                  op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN) ||
2823                                  dupopens)) {
2824                                 if (!NFSBCMP(op->nfso_own->nfsow_owner,
2825                                     op2->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
2826                                     NFSUNLOCKCLSTATE();
2827                                     printf("BADDUP OPEN\n");
2828                                 } else {
2829                                     NFSUNLOCKCLSTATE();
2830                                     printf("DUP OPEN\n");
2831                                 }
2832                                 nfscl_dumpstate(VFSTONFS(vnode_mount(vp)), 1, 1,
2833                                     0, 0);
2834                                 return;
2835                             }
2836                         }
2837                     }
2838                 }
2839             }
2840         }
2841         NFSUNLOCKCLSTATE();
2842 }
2843
2844 /*
2845  * During close, find an open that needs to be dereferenced and
2846  * dereference it. If there are no more opens for this file,
2847  * log a message to that effect.
2848  * Opens aren't actually Close'd until VOP_INACTIVE() is performed
2849  * on the file's vnode.
2850  * This is the safe way, since it is difficult to identify
2851  * which open the close is for and I/O can be performed after the
2852  * close(2) system call when a file is mmap'd.
2853  * If it returns 0 for success, there will be a referenced
2854  * clp returned via clpp.
2855  */
2856 APPLESTATIC int
2857 nfscl_getclose(vnode_t vp, struct nfsclclient **clpp)
2858 {
2859         struct nfsclclient *clp;
2860         struct nfsclowner *owp;
2861         struct nfsclopen *op;
2862         struct nfscldeleg *dp;
2863         struct nfsfh *nfhp;
2864         int error, notdecr;
2865
2866         error = nfscl_getcl(vp, NULL, NULL, &clp);
2867         if (error)
2868                 return (error);
2869         *clpp = clp;
2870
2871         nfhp = VTONFS(vp)->n_fhp;
2872         notdecr = 1;
2873         NFSLOCKCLSTATE();
2874         /*
2875          * First, look for one under a delegation that was locally issued
2876          * and just decrement the opencnt for it. Since all my Opens against
2877          * the server are DENY_NONE, I don't see a problem with hanging
2878          * onto them. (It is much easier to use one of the extant Opens
2879          * that I already have on the server when a Delegation is recalled
2880          * than to do fresh Opens.) Someday, I might need to rethink this, but.
2881          */
2882         dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
2883         if (dp != NULL) {
2884                 LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
2885                         op = LIST_FIRST(&owp->nfsow_open);
2886                         if (op != NULL) {
2887                                 /*
2888                                  * Since a delegation is for a file, there
2889                                  * should never be more than one open for
2890                                  * each openowner.
2891                                  */
2892                                 if (LIST_NEXT(op, nfso_list) != NULL)
2893                                         panic("nfscdeleg opens");
2894                                 if (notdecr && op->nfso_opencnt > 0) {
2895                                         notdecr = 0;
2896                                         op->nfso_opencnt--;
2897                                         break;
2898                                 }
2899                         }
2900                 }
2901         }
2902
2903         /* Now process the opens against the server. */
2904         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2905                 LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
2906                         if (op->nfso_fhlen == nfhp->nfh_len &&
2907                             !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
2908                             nfhp->nfh_len)) {
2909                                 /* Found an open, decrement cnt if possible */
2910                                 if (notdecr && op->nfso_opencnt > 0) {
2911                                         notdecr = 0;
2912                                         op->nfso_opencnt--;
2913                                 }
2914                                 /*
2915                                  * There are more opens, so just return.
2916                                  */
2917                                 if (op->nfso_opencnt > 0) {
2918                                         NFSUNLOCKCLSTATE();
2919                                         return (0);
2920                                 }
2921                         }
2922                 }
2923         }
2924         NFSUNLOCKCLSTATE();
2925         if (notdecr)
2926                 printf("nfscl: never fnd open\n");
2927         return (0);
2928 }
2929
2930 APPLESTATIC int
2931 nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
2932 {
2933         struct nfsclclient *clp;
2934         struct nfsclowner *owp, *nowp;
2935         struct nfsclopen *op;
2936         struct nfscldeleg *dp;
2937         struct nfsfh *nfhp;
2938         int error;
2939
2940         error = nfscl_getcl(vp, NULL, NULL, &clp);
2941         if (error)
2942                 return (error);
2943         *clpp = clp;
2944
2945         nfhp = VTONFS(vp)->n_fhp;
2946         NFSLOCKCLSTATE();
2947         /*
2948          * First get rid of the local Open structures, which should be no
2949          * longer in use.
2950          */
2951         dp = nfscl_finddeleg(clp, nfhp->nfh_fh, nfhp->nfh_len);
2952         if (dp != NULL) {
2953                 LIST_FOREACH_SAFE(owp, &dp->nfsdl_owner, nfsow_list, nowp) {
2954                         op = LIST_FIRST(&owp->nfsow_open);
2955                         if (op != NULL) {
2956                                 KASSERT((op->nfso_opencnt == 0),
2957                                     ("nfscl: bad open cnt on deleg"));
2958                                 nfscl_freeopen(op, 1);
2959                         }
2960                         nfscl_freeopenowner(owp, 1);
2961                 }
2962         }
2963
2964         /* Now process the opens against the server. */
2965 lookformore:
2966         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
2967                 op = LIST_FIRST(&owp->nfsow_open);
2968                 while (op != NULL) {
2969                         if (op->nfso_fhlen == nfhp->nfh_len &&
2970                             !NFSBCMP(op->nfso_fh, nfhp->nfh_fh,
2971                             nfhp->nfh_len)) {
2972                                 /* Found an open, close it. */
2973                                 KASSERT((op->nfso_opencnt == 0),
2974                                     ("nfscl: bad open cnt on server"));
2975                                 NFSUNLOCKCLSTATE();
2976                                 nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op,
2977                                     p);
2978                                 NFSLOCKCLSTATE();
2979                                 goto lookformore;
2980                         }
2981                         op = LIST_NEXT(op, nfso_list);
2982                 }
2983         }
2984         NFSUNLOCKCLSTATE();
2985         return (0);
2986 }
2987
2988 /*
2989  * Return all delegations on this client.
2990  * (Must be called with client sleep lock.)
2991  */
2992 static void
2993 nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
2994 {
2995         struct nfscldeleg *dp, *ndp;
2996         struct ucred *cred;
2997
2998         cred = newnfs_getcred();
2999         TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
3000                 nfscl_cleandeleg(dp);
3001                 (void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
3002                 nfscl_freedeleg(&clp->nfsc_deleg, dp);
3003         }
3004         NFSFREECRED(cred);
3005 }
3006
3007 /*
3008  * Do a callback RPC.
3009  */
3010 APPLESTATIC void
3011 nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
3012 {
3013         int i, op;
3014         u_int32_t *tl;
3015         struct nfsclclient *clp;
3016         struct nfscldeleg *dp = NULL;
3017         int numops, taglen = -1, error = 0, trunc, ret = 0;
3018         u_int32_t minorvers, retops = 0, *retopsp = NULL, *repp, cbident;
3019         u_char tag[NFSV4_SMALLSTR + 1], *tagstr;
3020         vnode_t vp = NULL;
3021         struct nfsnode *np;
3022         struct vattr va;
3023         struct nfsfh *nfhp;
3024         mount_t mp;
3025         nfsattrbit_t attrbits, rattrbits;
3026         nfsv4stateid_t stateid;
3027
3028         nfsrvd_rephead(nd);
3029         NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3030         taglen = fxdr_unsigned(int, *tl);
3031         if (taglen < 0) {
3032                 error = EBADRPC;
3033                 goto nfsmout;
3034         }
3035         if (taglen <= NFSV4_SMALLSTR)
3036                 tagstr = tag;
3037         else
3038                 tagstr = malloc(taglen + 1, M_TEMP, M_WAITOK);
3039         error = nfsrv_mtostr(nd, tagstr, taglen);
3040         if (error) {
3041                 if (taglen > NFSV4_SMALLSTR)
3042                         free(tagstr, M_TEMP);
3043                 taglen = -1;
3044                 goto nfsmout;
3045         }
3046         (void) nfsm_strtom(nd, tag, taglen);
3047         if (taglen > NFSV4_SMALLSTR) {
3048                 free(tagstr, M_TEMP);
3049         }
3050         NFSM_BUILD(retopsp, u_int32_t *, NFSX_UNSIGNED);
3051         NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
3052         minorvers = fxdr_unsigned(u_int32_t, *tl++);
3053         if (minorvers != NFSV4_MINORVERSION)
3054                 nd->nd_repstat = NFSERR_MINORVERMISMATCH;
3055         cbident = fxdr_unsigned(u_int32_t, *tl++);
3056         if (nd->nd_repstat)
3057                 numops = 0;
3058         else
3059                 numops = fxdr_unsigned(int, *tl);
3060         /*
3061          * Loop around doing the sub ops.
3062          */
3063         for (i = 0; i < numops; i++) {
3064                 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
3065                 NFSM_BUILD(repp, u_int32_t *, 2 * NFSX_UNSIGNED);
3066                 *repp++ = *tl;
3067                 op = fxdr_unsigned(int, *tl);
3068                 if (op < NFSV4OP_CBGETATTR || op > NFSV4OP_CBRECALL) {
3069                     nd->nd_repstat = NFSERR_OPILLEGAL;
3070                     *repp = nfscl_errmap(nd);
3071                     retops++;
3072                     break;
3073                 }
3074                 nd->nd_procnum = op;
3075                 newnfsstats.cbrpccnt[nd->nd_procnum]++;
3076                 switch (op) {
3077                 case NFSV4OP_CBGETATTR:
3078                         clp = NULL;
3079                         error = nfsm_getfh(nd, &nfhp);
3080                         if (!error)
3081                                 error = nfsrv_getattrbits(nd, &attrbits,
3082                                     NULL, NULL);
3083                         if (!error) {
3084                                 mp = nfscl_getmnt(cbident);
3085                                 if (mp == NULL)
3086                                         error = NFSERR_SERVERFAULT;
3087                         }
3088                         if (!error) {
3089                                 dp = NULL;
3090                                 NFSLOCKCLSTATE();
3091                                 clp = nfscl_findcl(VFSTONFS(mp));
3092                                 if (clp != NULL)
3093                                         dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3094                                             nfhp->nfh_len);
3095                                 NFSUNLOCKCLSTATE();
3096                                 if (dp == NULL)
3097                                         error = NFSERR_SERVERFAULT;
3098                         }
3099                         if (!error) {
3100                                 ret = nfscl_ngetreopen(mp, nfhp->nfh_fh,
3101                                     nfhp->nfh_len, p, &np);
3102                                 if (!ret)
3103                                         vp = NFSTOV(np);
3104                         }
3105                         if (nfhp != NULL)
3106                                 FREE((caddr_t)nfhp, M_NFSFH);
3107                         if (!error) {
3108                                 NFSZERO_ATTRBIT(&rattrbits);
3109                                 if (NFSISSET_ATTRBIT(&attrbits,
3110                                     NFSATTRBIT_SIZE)) {
3111                                         if (!ret)
3112                                                 va.va_size = np->n_size;
3113                                         else
3114                                                 va.va_size = dp->nfsdl_size;
3115                                         NFSSETBIT_ATTRBIT(&rattrbits,
3116                                             NFSATTRBIT_SIZE);
3117                                 }
3118                                 if (NFSISSET_ATTRBIT(&attrbits,
3119                                     NFSATTRBIT_CHANGE)) {
3120                                         va.va_filerev = dp->nfsdl_change;
3121                                         if (ret || (np->n_flag & NDELEGMOD))
3122                                                 va.va_filerev++;
3123                                         NFSSETBIT_ATTRBIT(&rattrbits,
3124                                             NFSATTRBIT_CHANGE);
3125                                 }
3126                                 (void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
3127                                     NULL, 0, &rattrbits, NULL, NULL, 0, 0, 0, 0,
3128                                     (uint64_t)0);
3129                                 if (!ret)
3130                                         vrele(vp);
3131                         }
3132                         break;
3133                 case NFSV4OP_CBRECALL:
3134                         clp = NULL;
3135                         NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID +
3136                             NFSX_UNSIGNED);
3137                         stateid.seqid = *tl++;
3138                         NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other,
3139                             NFSX_STATEIDOTHER);
3140                         tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
3141                         trunc = fxdr_unsigned(int, *tl);
3142                         error = nfsm_getfh(nd, &nfhp);
3143                         if (!error) {
3144                                 mp = nfscl_getmnt(cbident);
3145                                 if (mp == NULL)
3146                                         error = NFSERR_SERVERFAULT;
3147                         }
3148                         if (!error) {
3149                                 NFSLOCKCLSTATE();
3150                                 clp = nfscl_findcl(VFSTONFS(mp));
3151                                 if (clp != NULL) {
3152                                         dp = nfscl_finddeleg(clp, nfhp->nfh_fh,
3153                                             nfhp->nfh_len);
3154                                         if (dp != NULL && (dp->nfsdl_flags &
3155                                             NFSCLDL_DELEGRET) == 0) {
3156                                                 dp->nfsdl_flags |=
3157                                                     NFSCLDL_RECALL;
3158                                                 wakeup((caddr_t)clp);
3159                                         }
3160                                 } else {
3161                                         error = NFSERR_SERVERFAULT;
3162                                 }
3163                                 NFSUNLOCKCLSTATE();
3164                         }
3165                         if (nfhp != NULL)
3166                                 FREE((caddr_t)nfhp, M_NFSFH);
3167                         break;
3168                 };
3169                 if (error) {
3170                         if (error == EBADRPC || error == NFSERR_BADXDR) {
3171                                 nd->nd_repstat = NFSERR_BADXDR;
3172                         } else {
3173                                 nd->nd_repstat = error;
3174                         }
3175                         error = 0;
3176                 }
3177                 retops++;
3178                 if (nd->nd_repstat) {
3179                         *repp = nfscl_errmap(nd);
3180                         break;
3181                 } else
3182                         *repp = 0;      /* NFS4_OK */
3183         }
3184 nfsmout:
3185         if (error) {
3186                 if (error == EBADRPC || error == NFSERR_BADXDR)
3187                         nd->nd_repstat = NFSERR_BADXDR;
3188                 else
3189                         printf("nfsv4 comperr1=%d\n", error);
3190         }
3191         if (taglen == -1) {
3192                 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
3193                 *tl++ = 0;
3194                 *tl = 0;
3195         } else {
3196                 *retopsp = txdr_unsigned(retops);
3197         }
3198         *nd->nd_errp = nfscl_errmap(nd);
3199 }
3200
3201 /*
3202  * Generate the next cbident value. Basically just increment a static value
3203  * and then check that it isn't already in the list, if it has wrapped around.
3204  */
3205 static u_int32_t
3206 nfscl_nextcbident(void)
3207 {
3208         struct nfsclclient *clp;
3209         int matched;
3210         static u_int32_t nextcbident = 0;
3211         static int haswrapped = 0;
3212
3213         nextcbident++;
3214         if (nextcbident == 0)
3215                 haswrapped = 1;
3216         if (haswrapped) {
3217                 /*
3218                  * Search the clientid list for one already using this cbident.
3219                  */
3220                 do {
3221                         matched = 0;
3222                         NFSLOCKCLSTATE();
3223                         LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3224                                 if (clp->nfsc_cbident == nextcbident) {
3225                                         matched = 1;
3226                                         break;
3227                                 }
3228                         }
3229                         NFSUNLOCKCLSTATE();
3230                         if (matched == 1)
3231                                 nextcbident++;
3232                 } while (matched);
3233         }
3234         return (nextcbident);
3235 }
3236
3237 /*
3238  * Get the mount point related to a given cbident.
3239  */
3240 static mount_t
3241 nfscl_getmnt(u_int32_t cbident)
3242 {
3243         struct nfsclclient *clp;
3244         struct nfsmount *nmp;
3245
3246         NFSLOCKCLSTATE();
3247         LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
3248                 if (clp->nfsc_cbident == cbident)
3249                         break;
3250         }
3251         if (clp == NULL) {
3252                 NFSUNLOCKCLSTATE();
3253                 return (NULL);
3254         }
3255         nmp = clp->nfsc_nmp;
3256         NFSUNLOCKCLSTATE();
3257         return (nmp->nm_mountp);
3258 }
3259
3260 /*
3261  * Search for a lock conflict locally on the client. A conflict occurs if
3262  * - not same owner and overlapping byte range and at least one of them is
3263  *   a write lock or this is an unlock.
3264  */
3265 static int
3266 nfscl_localconflict(struct nfsclclient *clp, u_int8_t *fhp, int fhlen,
3267     struct nfscllock *nlop, u_int8_t *own, struct nfscldeleg *dp,
3268     struct nfscllock **lopp)
3269 {
3270         struct nfsclowner *owp;
3271         struct nfsclopen *op;
3272         int ret;
3273
3274         if (dp != NULL) {
3275                 ret = nfscl_checkconflict(&dp->nfsdl_lock, nlop, own, lopp);
3276                 if (ret)
3277                         return (ret);
3278         }
3279         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3280                 LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3281                         if (op->nfso_fhlen == fhlen &&
3282                             !NFSBCMP(op->nfso_fh, fhp, fhlen)) {
3283                                 ret = nfscl_checkconflict(&op->nfso_lock, nlop,
3284                                     own, lopp);
3285                                 if (ret)
3286                                         return (ret);
3287                         }
3288                 }
3289         }
3290         return (0);
3291 }
3292
3293 static int
3294 nfscl_checkconflict(struct nfscllockownerhead *lhp, struct nfscllock *nlop,
3295     u_int8_t *own, struct nfscllock **lopp)
3296 {
3297         struct nfscllockowner *lp;
3298         struct nfscllock *lop;
3299
3300         LIST_FOREACH(lp, lhp, nfsl_list) {
3301                 if (NFSBCMP(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN)) {
3302                         LIST_FOREACH(lop, &lp->nfsl_lock, nfslo_list) {
3303                                 if (lop->nfslo_first >= nlop->nfslo_end)
3304                                         break;
3305                                 if (lop->nfslo_end <= nlop->nfslo_first)
3306                                         continue;
3307                                 if (lop->nfslo_type == F_WRLCK ||
3308                                     nlop->nfslo_type == F_WRLCK ||
3309                                     nlop->nfslo_type == F_UNLCK) {
3310                                         if (lopp != NULL)
3311                                                 *lopp = lop;
3312                                         return (NFSERR_DENIED);
3313                                 }
3314                         }
3315                 }
3316         }
3317         return (0);
3318 }
3319
3320 /*
3321  * Check for a local conflicting lock.
3322  */
3323 APPLESTATIC int
3324 nfscl_lockt(vnode_t vp, struct nfsclclient *clp, u_int64_t off,
3325     u_int64_t len, struct flock *fl, NFSPROC_T *p)
3326 {
3327         struct nfscllock *lop, nlck;
3328         struct nfscldeleg *dp;
3329         struct nfsnode *np;
3330         u_int8_t own[NFSV4CL_LOCKNAMELEN];
3331         int error;
3332
3333         nlck.nfslo_type = fl->l_type;
3334         nlck.nfslo_first = off;
3335         if (len == NFS64BITSSET) {
3336                 nlck.nfslo_end = NFS64BITSSET;
3337         } else {
3338                 nlck.nfslo_end = off + len;
3339                 if (nlck.nfslo_end <= nlck.nfslo_first)
3340                         return (NFSERR_INVAL);
3341         }
3342         np = VTONFS(vp);
3343         nfscl_filllockowner(p, own);
3344         NFSLOCKCLSTATE();
3345         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3346         error = nfscl_localconflict(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len,
3347             &nlck, own, dp, &lop);
3348         if (error != 0) {
3349                 fl->l_whence = SEEK_SET;
3350                 fl->l_start = lop->nfslo_first;
3351                 if (lop->nfslo_end == NFS64BITSSET)
3352                         fl->l_len = 0;
3353                 else
3354                         fl->l_len = lop->nfslo_end - lop->nfslo_first;
3355                 fl->l_pid = (pid_t)0;
3356                 fl->l_type = lop->nfslo_type;
3357                 error = -1;                     /* no RPC required */
3358         } else if (dp != NULL && ((dp->nfsdl_flags & NFSCLDL_WRITE) ||
3359             fl->l_type == F_RDLCK)) {
3360                 /*
3361                  * The delegation ensures that there isn't a conflicting
3362                  * lock on the server, so return -1 to indicate an RPC
3363                  * isn't required.
3364                  */
3365                 fl->l_type = F_UNLCK;
3366                 error = -1;
3367         }
3368         NFSUNLOCKCLSTATE();
3369         return (error);
3370 }
3371
3372 /*
3373  * Handle Recall of a delegation.
3374  * The clp must be exclusive locked when this is called.
3375  */
3376 static int
3377 nfscl_recalldeleg(struct nfsclclient *clp, struct nfsmount *nmp,
3378     struct nfscldeleg *dp, vnode_t vp, struct ucred *cred, NFSPROC_T *p,
3379     int called_from_renewthread)
3380 {
3381         struct nfsclowner *owp, *lowp, *nowp;
3382         struct nfsclopen *op, *lop;
3383         struct nfscllockowner *lp;
3384         struct nfscllock *lckp;
3385         struct nfsnode *np;
3386         int error = 0, ret, gotvp = 0;
3387
3388         if (vp == NULL) {
3389                 /*
3390                  * First, get a vnode for the file. This is needed to do RPCs.
3391                  */
3392                 ret = nfscl_ngetreopen(nmp->nm_mountp, dp->nfsdl_fh,
3393                     dp->nfsdl_fhlen, p, &np);
3394                 if (ret) {
3395                         /*
3396                          * File isn't open, so nothing to move over to the
3397                          * server.
3398                          */
3399                         return (0);
3400                 }
3401                 vp = NFSTOV(np);
3402                 gotvp = 1;
3403         } else {
3404                 np = VTONFS(vp);
3405         }
3406         dp->nfsdl_flags &= ~NFSCLDL_MODTIMESET;
3407
3408         /*
3409          * Ok, if it's a write delegation, flush data to the server, so
3410          * that close/open consistency is retained.
3411          */
3412         ret = 0;
3413         NFSLOCKNODE(np);
3414         if ((dp->nfsdl_flags & NFSCLDL_WRITE) && (np->n_flag & NMODIFIED)) {
3415                 np->n_flag |= NDELEGRECALL;
3416                 NFSUNLOCKNODE(np);
3417                 ret = ncl_flush(vp, MNT_WAIT, cred, p, 1,
3418                     called_from_renewthread);
3419                 NFSLOCKNODE(np);
3420                 np->n_flag &= ~NDELEGRECALL;
3421         }
3422         NFSINVALATTRCACHE(np);
3423         NFSUNLOCKNODE(np);
3424         if (ret == EIO && called_from_renewthread != 0) {
3425                 /*
3426                  * If the flush failed with EIO for the renew thread,
3427                  * return now, so that the dirty buffer will be flushed
3428                  * later.
3429                  */
3430                 if (gotvp != 0)
3431                         vrele(vp);
3432                 return (ret);
3433         }
3434
3435         /*
3436          * Now, for each openowner with opens issued locally, move them
3437          * over to state against the server.
3438          */
3439         LIST_FOREACH(lowp, &dp->nfsdl_owner, nfsow_list) {
3440                 lop = LIST_FIRST(&lowp->nfsow_open);
3441                 if (lop != NULL) {
3442                         if (LIST_NEXT(lop, nfso_list) != NULL)
3443                                 panic("nfsdlg mult opens");
3444                         /*
3445                          * Look for the same openowner against the server.
3446                          */
3447                         LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) {
3448                                 if (!NFSBCMP(lowp->nfsow_owner,
3449                                     owp->nfsow_owner, NFSV4CL_LOCKNAMELEN)) {
3450                                         newnfs_copycred(&dp->nfsdl_cred, cred);
3451                                         ret = nfscl_moveopen(vp, clp, nmp, lop,
3452                                             owp, dp, cred, p);
3453                                         if (ret == NFSERR_STALECLIENTID ||
3454                                             ret == NFSERR_STALEDONTRECOVER) {
3455                                                 if (gotvp)
3456                                                         vrele(vp);
3457                                                 return (ret);
3458                                         }
3459                                         if (ret) {
3460                                                 nfscl_freeopen(lop, 1);
3461                                                 if (!error)
3462                                                         error = ret;
3463                                         }
3464                                         break;
3465                                 }
3466                         }
3467
3468                         /*
3469                          * If no openowner found, create one and get an open
3470                          * for it.
3471                          */
3472                         if (owp == NULL) {
3473                                 MALLOC(nowp, struct nfsclowner *,
3474                                     sizeof (struct nfsclowner), M_NFSCLOWNER,
3475                                     M_WAITOK);
3476                                 nfscl_newopen(clp, NULL, &owp, &nowp, &op, 
3477                                     NULL, lowp->nfsow_owner, dp->nfsdl_fh,
3478                                     dp->nfsdl_fhlen, NULL);
3479                                 newnfs_copycred(&dp->nfsdl_cred, cred);
3480                                 ret = nfscl_moveopen(vp, clp, nmp, lop,
3481                                     owp, dp, cred, p);
3482                                 if (ret) {
3483                                         nfscl_freeopenowner(owp, 0);
3484                                         if (ret == NFSERR_STALECLIENTID ||
3485                                             ret == NFSERR_STALEDONTRECOVER) {
3486                                                 if (gotvp)
3487                                                         vrele(vp);
3488                                                 return (ret);
3489                                         }
3490                                         if (ret) {
3491                                                 nfscl_freeopen(lop, 1);
3492                                                 if (!error)
3493                                                         error = ret;
3494                                         }
3495                                 }
3496                         }
3497                 }
3498         }
3499
3500         /*
3501          * Now, get byte range locks for any locks done locally.
3502          */
3503         LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3504                 LIST_FOREACH(lckp, &lp->nfsl_lock, nfslo_list) {
3505                         newnfs_copycred(&dp->nfsdl_cred, cred);
3506                         ret = nfscl_relock(vp, clp, nmp, lp, lckp, cred, p);
3507                         if (ret == NFSERR_STALESTATEID ||
3508                             ret == NFSERR_STALEDONTRECOVER ||
3509                             ret == NFSERR_STALECLIENTID) {
3510                                 if (gotvp)
3511                                         vrele(vp);
3512                                 return (ret);
3513                         }
3514                         if (ret && !error)
3515                                 error = ret;
3516                 }
3517         }
3518         if (gotvp)
3519                 vrele(vp);
3520         return (error);
3521 }
3522
3523 /*
3524  * Move a locally issued open over to an owner on the state list.
3525  * SIDE EFFECT: If it needs to sleep (do an rpc), it unlocks clstate and
3526  * returns with it unlocked.
3527  */
3528 static int
3529 nfscl_moveopen(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3530     struct nfsclopen *lop, struct nfsclowner *owp, struct nfscldeleg *dp,
3531     struct ucred *cred, NFSPROC_T *p)
3532 {
3533         struct nfsclopen *op, *nop;
3534         struct nfscldeleg *ndp;
3535         struct nfsnode *np;
3536         int error = 0, newone;
3537
3538         /*
3539          * First, look for an appropriate open, If found, just increment the
3540          * opencnt in it.
3541          */
3542         LIST_FOREACH(op, &owp->nfsow_open, nfso_list) {
3543                 if ((op->nfso_mode & lop->nfso_mode) == lop->nfso_mode &&
3544                     op->nfso_fhlen == lop->nfso_fhlen &&
3545                     !NFSBCMP(op->nfso_fh, lop->nfso_fh, op->nfso_fhlen)) {
3546                         op->nfso_opencnt += lop->nfso_opencnt;
3547                         nfscl_freeopen(lop, 1);
3548                         return (0);
3549                 }
3550         }
3551
3552         /* No appropriate open, so we have to do one against the server. */
3553         np = VTONFS(vp);
3554         MALLOC(nop, struct nfsclopen *, sizeof (struct nfsclopen) +
3555             lop->nfso_fhlen - 1, M_NFSCLOPEN, M_WAITOK);
3556         newone = 0;
3557         nfscl_newopen(clp, NULL, &owp, NULL, &op, &nop, owp->nfsow_owner,
3558             lop->nfso_fh, lop->nfso_fhlen, &newone);
3559         ndp = dp;
3560         error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data, np->n_v4->n4_fhlen,
3561             lop->nfso_fh, lop->nfso_fhlen, lop->nfso_mode, op,
3562             NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, &ndp, 0, 0, cred, p);
3563         if (error) {
3564                 if (newone)
3565                         nfscl_freeopen(op, 0);
3566         } else {
3567                 if (newone)
3568                         newnfs_copyincred(cred, &op->nfso_cred);
3569                 op->nfso_mode |= lop->nfso_mode;
3570                 op->nfso_opencnt += lop->nfso_opencnt;
3571                 nfscl_freeopen(lop, 1);
3572         }
3573         if (nop != NULL)
3574                 FREE((caddr_t)nop, M_NFSCLOPEN);
3575         if (ndp != NULL) {
3576                 /*
3577                  * What should I do with the returned delegation, since the
3578                  * delegation is being recalled? For now, just printf and
3579                  * through it away.
3580                  */
3581                 printf("Moveopen returned deleg\n");
3582                 FREE((caddr_t)ndp, M_NFSCLDELEG);
3583         }
3584         return (error);
3585 }
3586
3587 /*
3588  * Recall all delegations on this client.
3589  */
3590 static void
3591 nfscl_totalrecall(struct nfsclclient *clp)
3592 {
3593         struct nfscldeleg *dp;
3594
3595         TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list) {
3596                 if ((dp->nfsdl_flags & NFSCLDL_DELEGRET) == 0)
3597                         dp->nfsdl_flags |= NFSCLDL_RECALL;
3598         }
3599 }
3600
3601 /*
3602  * Relock byte ranges. Called for delegation recall and state expiry.
3603  */
3604 static int
3605 nfscl_relock(vnode_t vp, struct nfsclclient *clp, struct nfsmount *nmp,
3606     struct nfscllockowner *lp, struct nfscllock *lop, struct ucred *cred,
3607     NFSPROC_T *p)
3608 {
3609         struct nfscllockowner *nlp;
3610         struct nfsfh *nfhp;
3611         u_int64_t off, len;
3612         u_int32_t clidrev = 0;
3613         int error, newone, donelocally;
3614
3615         off = lop->nfslo_first;
3616         len = lop->nfslo_end - lop->nfslo_first;
3617         error = nfscl_getbytelock(vp, off, len, lop->nfslo_type, cred, p,
3618             clp, 1, lp->nfsl_owner, lp->nfsl_openowner, &nlp, &newone,
3619             &donelocally);
3620         if (error || donelocally)
3621                 return (error);
3622         if (nmp->nm_clp != NULL)
3623                 clidrev = nmp->nm_clp->nfsc_clientidrev;
3624         else
3625                 clidrev = 0;
3626         nfhp = VTONFS(vp)->n_fhp;
3627         error = nfscl_trylock(nmp, vp, nfhp->nfh_fh,
3628             nfhp->nfh_len, nlp, newone, 0, off,
3629             len, lop->nfslo_type, cred, p);
3630         if (error)
3631                 nfscl_freelockowner(nlp, 0);
3632         return (error);
3633 }
3634
3635 /*
3636  * Called to re-open a file. Basically get a vnode for the file handle
3637  * and then call nfsrpc_openrpc() to do the rest.
3638  */
3639 static int
3640 nfsrpc_reopen(struct nfsmount *nmp, u_int8_t *fhp, int fhlen,
3641     u_int32_t mode, struct nfsclopen *op, struct nfscldeleg **dpp,
3642     struct ucred *cred, NFSPROC_T *p)
3643 {
3644         struct nfsnode *np;
3645         vnode_t vp;
3646         int error;
3647
3648         error = nfscl_ngetreopen(nmp->nm_mountp, fhp, fhlen, p, &np);
3649         if (error)
3650                 return (error);
3651         vp = NFSTOV(np);
3652         if (np->n_v4 != NULL) {
3653                 error = nfscl_tryopen(nmp, vp, np->n_v4->n4_data,
3654                     np->n_v4->n4_fhlen, fhp, fhlen, mode, op,
3655                     NFS4NODENAME(np->n_v4), np->n_v4->n4_namelen, dpp, 0, 0,
3656                     cred, p);
3657         } else {
3658                 error = EINVAL;
3659         }
3660         vrele(vp);
3661         return (error);
3662 }
3663
3664 /*
3665  * Try an open against the server. Just call nfsrpc_openrpc(), retrying while
3666  * NFSERR_DELAY. Also, try system credentials, if the passed in credentials
3667  * fail.
3668  */
3669 static int
3670 nfscl_tryopen(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp, int fhlen,
3671     u_int8_t *newfhp, int newfhlen, u_int32_t mode, struct nfsclopen *op,
3672     u_int8_t *name, int namelen, struct nfscldeleg **ndpp,
3673     int reclaim, u_int32_t delegtype, struct ucred *cred, NFSPROC_T *p)
3674 {
3675         int error;
3676
3677         do {
3678                 error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp, newfhlen,
3679                     mode, op, name, namelen, ndpp, reclaim, delegtype, cred, p,
3680                     0, 0);
3681                 if (error == NFSERR_DELAY)
3682                         (void) nfs_catnap(PZERO, error, "nfstryop");
3683         } while (error == NFSERR_DELAY);
3684         if (error == EAUTH || error == EACCES) {
3685                 /* Try again using system credentials */
3686                 newnfs_setroot(cred);
3687                 do {
3688                     error = nfsrpc_openrpc(nmp, vp, fhp, fhlen, newfhp,
3689                         newfhlen, mode, op, name, namelen, ndpp, reclaim,
3690                         delegtype, cred, p, 1, 0);
3691                     if (error == NFSERR_DELAY)
3692                         (void) nfs_catnap(PZERO, error, "nfstryop");
3693                 } while (error == NFSERR_DELAY);
3694         }
3695         return (error);
3696 }
3697
3698 /*
3699  * Try a byte range lock. Just loop on nfsrpc_lock() while it returns
3700  * NFSERR_DELAY. Also, retry with system credentials, if the provided
3701  * cred don't work.
3702  */
3703 static int
3704 nfscl_trylock(struct nfsmount *nmp, vnode_t vp, u_int8_t *fhp,
3705     int fhlen, struct nfscllockowner *nlp, int newone, int reclaim,
3706     u_int64_t off, u_int64_t len, short type, struct ucred *cred, NFSPROC_T *p)
3707 {
3708         struct nfsrv_descript nfsd, *nd = &nfsd;
3709         int error;
3710
3711         do {
3712                 error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp, newone,
3713                     reclaim, off, len, type, cred, p, 0);
3714                 if (!error && nd->nd_repstat == NFSERR_DELAY)
3715                         (void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3716                             "nfstrylck");
3717         } while (!error && nd->nd_repstat == NFSERR_DELAY);
3718         if (!error)
3719                 error = nd->nd_repstat;
3720         if (error == EAUTH || error == EACCES) {
3721                 /* Try again using root credentials */
3722                 newnfs_setroot(cred);
3723                 do {
3724                         error = nfsrpc_lock(nd, nmp, vp, fhp, fhlen, nlp,
3725                             newone, reclaim, off, len, type, cred, p, 1);
3726                         if (!error && nd->nd_repstat == NFSERR_DELAY)
3727                                 (void) nfs_catnap(PZERO, (int)nd->nd_repstat,
3728                                     "nfstrylck");
3729                 } while (!error && nd->nd_repstat == NFSERR_DELAY);
3730                 if (!error)
3731                         error = nd->nd_repstat;
3732         }
3733         return (error);
3734 }
3735
3736 /*
3737  * Try a delegreturn against the server. Just call nfsrpc_delegreturn(),
3738  * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
3739  * credentials fail.
3740  */
3741 static int
3742 nfscl_trydelegreturn(struct nfscldeleg *dp, struct ucred *cred,
3743     struct nfsmount *nmp, NFSPROC_T *p)
3744 {
3745         int error;
3746
3747         do {
3748                 error = nfsrpc_delegreturn(dp, cred, nmp, p, 0);
3749                 if (error == NFSERR_DELAY)
3750                         (void) nfs_catnap(PZERO, error, "nfstrydp");
3751         } while (error == NFSERR_DELAY);
3752         if (error == EAUTH || error == EACCES) {
3753                 /* Try again using system credentials */
3754                 newnfs_setroot(cred);
3755                 do {
3756                         error = nfsrpc_delegreturn(dp, cred, nmp, p, 1);
3757                         if (error == NFSERR_DELAY)
3758                                 (void) nfs_catnap(PZERO, error, "nfstrydp");
3759                 } while (error == NFSERR_DELAY);
3760         }
3761         return (error);
3762 }
3763
3764 /*
3765  * Try a close against the server. Just call nfsrpc_closerpc(),
3766  * retrying while NFSERR_DELAY. Also, try system credentials, if the passed in
3767  * credentials fail.
3768  */
3769 APPLESTATIC int
3770 nfscl_tryclose(struct nfsclopen *op, struct ucred *cred,
3771     struct nfsmount *nmp, NFSPROC_T *p)
3772 {
3773         struct nfsrv_descript nfsd, *nd = &nfsd;
3774         int error;
3775
3776         do {
3777                 error = nfsrpc_closerpc(nd, nmp, op, cred, p, 0);
3778                 if (error == NFSERR_DELAY)
3779                         (void) nfs_catnap(PZERO, error, "nfstrycl");
3780         } while (error == NFSERR_DELAY);
3781         if (error == EAUTH || error == EACCES) {
3782                 /* Try again using system credentials */
3783                 newnfs_setroot(cred);
3784                 do {
3785                         error = nfsrpc_closerpc(nd, nmp, op, cred, p, 1);
3786                         if (error == NFSERR_DELAY)
3787                                 (void) nfs_catnap(PZERO, error, "nfstrycl");
3788                 } while (error == NFSERR_DELAY);
3789         }
3790         return (error);
3791 }
3792
3793 /*
3794  * Decide if a delegation on a file permits close without flushing writes
3795  * to the server. This might be a big performance win in some environments.
3796  * (Not useful until the client does caching on local stable storage.)
3797  */
3798 APPLESTATIC int
3799 nfscl_mustflush(vnode_t vp)
3800 {
3801         struct nfsclclient *clp;
3802         struct nfscldeleg *dp;
3803         struct nfsnode *np;
3804         struct nfsmount *nmp;
3805
3806         np = VTONFS(vp);
3807         nmp = VFSTONFS(vnode_mount(vp));
3808         if (!NFSHASNFSV4(nmp))
3809                 return (1);
3810         NFSLOCKCLSTATE();
3811         clp = nfscl_findcl(nmp);
3812         if (clp == NULL) {
3813                 NFSUNLOCKCLSTATE();
3814                 return (1);
3815         }
3816         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3817         if (dp != NULL && (dp->nfsdl_flags &
3818             (NFSCLDL_WRITE | NFSCLDL_RECALL | NFSCLDL_DELEGRET)) ==
3819              NFSCLDL_WRITE &&
3820             (dp->nfsdl_sizelimit >= np->n_size ||
3821              !NFSHASSTRICT3530(nmp))) {
3822                 NFSUNLOCKCLSTATE();
3823                 return (0);
3824         }
3825         NFSUNLOCKCLSTATE();
3826         return (1);
3827 }
3828
3829 /*
3830  * See if a (write) delegation exists for this file.
3831  */
3832 APPLESTATIC int
3833 nfscl_nodeleg(vnode_t vp, int writedeleg)
3834 {
3835         struct nfsclclient *clp;
3836         struct nfscldeleg *dp;
3837         struct nfsnode *np;
3838         struct nfsmount *nmp;
3839
3840         np = VTONFS(vp);
3841         nmp = VFSTONFS(vnode_mount(vp));
3842         if (!NFSHASNFSV4(nmp))
3843                 return (1);
3844         NFSLOCKCLSTATE();
3845         clp = nfscl_findcl(nmp);
3846         if (clp == NULL) {
3847                 NFSUNLOCKCLSTATE();
3848                 return (1);
3849         }
3850         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
3851         if (dp != NULL &&
3852             (dp->nfsdl_flags & (NFSCLDL_RECALL | NFSCLDL_DELEGRET)) == 0 &&
3853             (writedeleg == 0 || (dp->nfsdl_flags & NFSCLDL_WRITE) ==
3854              NFSCLDL_WRITE)) {
3855                 NFSUNLOCKCLSTATE();
3856                 return (0);
3857         }
3858         NFSUNLOCKCLSTATE();
3859         return (1);
3860 }
3861
3862 /*
3863  * Look for an associated delegation that should be DelegReturned.
3864  */
3865 APPLESTATIC int
3866 nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
3867 {
3868         struct nfsclclient *clp;
3869         struct nfscldeleg *dp;
3870         struct nfsclowner *owp;
3871         struct nfscllockowner *lp;
3872         struct nfsmount *nmp;
3873         struct ucred *cred;
3874         struct nfsnode *np;
3875         int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
3876
3877         nmp = VFSTONFS(vnode_mount(vp));
3878         np = VTONFS(vp);
3879         NFSLOCKCLSTATE();
3880         /*
3881          * Loop around waiting for:
3882          * - outstanding I/O operations on delegations to complete
3883          * - for a delegation on vp that has state, lock the client and
3884          *   do a recall
3885          * - return delegation with no state
3886          */
3887         while (1) {
3888                 clp = nfscl_findcl(nmp);
3889                 if (clp == NULL) {
3890                         NFSUNLOCKCLSTATE();
3891                         return (retcnt);
3892                 }
3893                 dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3894                     np->n_fhp->nfh_len);
3895                 if (dp != NULL) {
3896                     /*
3897                      * Wait for outstanding I/O ops to be done.
3898                      */
3899                     if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3900                         if (igotlock) {
3901                             nfsv4_unlock(&clp->nfsc_lock, 0);
3902                             igotlock = 0;
3903                         }
3904                         dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
3905                         (void) nfsmsleep(&dp->nfsdl_rwlock,
3906                             NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
3907                         continue;
3908                     }
3909                     needsrecall = 0;
3910                     LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
3911                         if (!LIST_EMPTY(&owp->nfsow_open)) {
3912                             needsrecall = 1;
3913                             break;
3914                         }
3915                     }
3916                     if (!needsrecall) {
3917                         LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
3918                             if (!LIST_EMPTY(&lp->nfsl_lock)) {
3919                                 needsrecall = 1;
3920                                 break;
3921                             }
3922                         }
3923                     }
3924                     if (needsrecall && !triedrecall) {
3925                         dp->nfsdl_flags |= NFSCLDL_DELEGRET;
3926                         islept = 0;
3927                         while (!igotlock) {
3928                             igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
3929                                 &islept, NFSCLSTATEMUTEXPTR, NULL);
3930                             if (islept)
3931                                 break;
3932                         }
3933                         if (islept)
3934                             continue;
3935                         NFSUNLOCKCLSTATE();
3936                         cred = newnfs_getcred();
3937                         newnfs_copycred(&dp->nfsdl_cred, cred);
3938                         (void) nfscl_recalldeleg(clp, nmp, dp, vp, cred, p, 0);
3939                         NFSFREECRED(cred);
3940                         triedrecall = 1;
3941                         NFSLOCKCLSTATE();
3942                         nfsv4_unlock(&clp->nfsc_lock, 0);
3943                         igotlock = 0;
3944                         continue;
3945                     }
3946                     *stp = dp->nfsdl_stateid;
3947                     retcnt = 1;
3948                     nfscl_cleandeleg(dp);
3949                     nfscl_freedeleg(&clp->nfsc_deleg, dp);
3950                 }
3951                 if (igotlock)
3952                     nfsv4_unlock(&clp->nfsc_lock, 0);
3953                 NFSUNLOCKCLSTATE();
3954                 return (retcnt);
3955         }
3956 }
3957
3958 /*
3959  * Look for associated delegation(s) that should be DelegReturned.
3960  */
3961 APPLESTATIC int
3962 nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
3963     nfsv4stateid_t *tstp, int *gottdp, NFSPROC_T *p)
3964 {
3965         struct nfsclclient *clp;
3966         struct nfscldeleg *dp;
3967         struct nfsclowner *owp;
3968         struct nfscllockowner *lp;
3969         struct nfsmount *nmp;
3970         struct ucred *cred;
3971         struct nfsnode *np;
3972         int igotlock = 0, triedrecall = 0, needsrecall, retcnt = 0, islept;
3973
3974         nmp = VFSTONFS(vnode_mount(fvp));
3975         *gotfdp = 0;
3976         *gottdp = 0;
3977         NFSLOCKCLSTATE();
3978         /*
3979          * Loop around waiting for:
3980          * - outstanding I/O operations on delegations to complete
3981          * - for a delegation on fvp that has state, lock the client and
3982          *   do a recall
3983          * - return delegation(s) with no state.
3984          */
3985         while (1) {
3986                 clp = nfscl_findcl(nmp);
3987                 if (clp == NULL) {
3988                         NFSUNLOCKCLSTATE();
3989                         return (retcnt);
3990                 }
3991                 np = VTONFS(fvp);
3992                 dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
3993                     np->n_fhp->nfh_len);
3994                 if (dp != NULL && *gotfdp == 0) {
3995                     /*
3996                      * Wait for outstanding I/O ops to be done.
3997                      */
3998                     if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
3999                         if (igotlock) {
4000                             nfsv4_unlock(&clp->nfsc_lock, 0);
4001                             igotlock = 0;
4002                         }
4003                         dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4004                         (void) nfsmsleep(&dp->nfsdl_rwlock,
4005                             NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4006                         continue;
4007                     }
4008                     needsrecall = 0;
4009                     LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4010                         if (!LIST_EMPTY(&owp->nfsow_open)) {
4011                             needsrecall = 1;
4012                             break;
4013                         }
4014                     }
4015                     if (!needsrecall) {
4016                         LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4017                             if (!LIST_EMPTY(&lp->nfsl_lock)) {
4018                                 needsrecall = 1;
4019                                 break;
4020                             }
4021                         }
4022                     }
4023                     if (needsrecall && !triedrecall) {
4024                         dp->nfsdl_flags |= NFSCLDL_DELEGRET;
4025                         islept = 0;
4026                         while (!igotlock) {
4027                             igotlock = nfsv4_lock(&clp->nfsc_lock, 1,
4028                                 &islept, NFSCLSTATEMUTEXPTR, NULL);
4029                             if (islept)
4030                                 break;
4031                         }
4032                         if (islept)
4033                             continue;
4034                         NFSUNLOCKCLSTATE();
4035                         cred = newnfs_getcred();
4036                         newnfs_copycred(&dp->nfsdl_cred, cred);
4037                         (void) nfscl_recalldeleg(clp, nmp, dp, fvp, cred, p, 0);
4038                         NFSFREECRED(cred);
4039                         triedrecall = 1;
4040                         NFSLOCKCLSTATE();
4041                         nfsv4_unlock(&clp->nfsc_lock, 0);
4042                         igotlock = 0;
4043                         continue;
4044                     }
4045                     *fstp = dp->nfsdl_stateid;
4046                     retcnt++;
4047                     *gotfdp = 1;
4048                     nfscl_cleandeleg(dp);
4049                     nfscl_freedeleg(&clp->nfsc_deleg, dp);
4050                 }
4051                 if (igotlock) {
4052                     nfsv4_unlock(&clp->nfsc_lock, 0);
4053                     igotlock = 0;
4054                 }
4055                 if (tvp != NULL) {
4056                     np = VTONFS(tvp);
4057                     dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
4058                         np->n_fhp->nfh_len);
4059                     if (dp != NULL && *gottdp == 0) {
4060                         /*
4061                          * Wait for outstanding I/O ops to be done.
4062                          */
4063                         if (dp->nfsdl_rwlock.nfslock_usecnt > 0) {
4064                             dp->nfsdl_rwlock.nfslock_lock |= NFSV4LOCK_WANTED;
4065                             (void) nfsmsleep(&dp->nfsdl_rwlock,
4066                                 NFSCLSTATEMUTEXPTR, PZERO, "nfscld", NULL);
4067                             continue;
4068                         }
4069                         LIST_FOREACH(owp, &dp->nfsdl_owner, nfsow_list) {
4070                             if (!LIST_EMPTY(&owp->nfsow_open)) {
4071                                 NFSUNLOCKCLSTATE();
4072                                 return (retcnt);
4073                             }
4074                         }
4075                         LIST_FOREACH(lp, &dp->nfsdl_lock, nfsl_list) {
4076                             if (!LIST_EMPTY(&lp->nfsl_lock)) {
4077                                 NFSUNLOCKCLSTATE();
4078                                 return (retcnt);
4079                             }
4080                         }
4081                         *tstp = dp->nfsdl_stateid;
4082                         retcnt++;
4083                         *gottdp = 1;
4084                         nfscl_cleandeleg(dp);
4085                         nfscl_freedeleg(&clp->nfsc_deleg, dp);
4086                     }
4087                 }
4088                 NFSUNLOCKCLSTATE();
4089                 return (retcnt);
4090         }
4091 }
4092
4093 /*
4094  * Get a reference on the clientid associated with the mount point.
4095  * Return 1 if success, 0 otherwise.
4096  */
4097 APPLESTATIC int
4098 nfscl_getref(struct nfsmount *nmp)
4099 {
4100         struct nfsclclient *clp;
4101
4102         NFSLOCKCLSTATE();
4103         clp = nfscl_findcl(nmp);
4104         if (clp == NULL) {
4105                 NFSUNLOCKCLSTATE();
4106                 return (0);
4107         }
4108         nfsv4_getref(&clp->nfsc_lock, NULL, NFSCLSTATEMUTEXPTR, NULL);
4109         NFSUNLOCKCLSTATE();
4110         return (1);
4111 }
4112
4113 /*
4114  * Release a reference on a clientid acquired with the above call.
4115  */
4116 APPLESTATIC void
4117 nfscl_relref(struct nfsmount *nmp)
4118 {
4119         struct nfsclclient *clp;
4120
4121         NFSLOCKCLSTATE();
4122         clp = nfscl_findcl(nmp);
4123         if (clp == NULL) {
4124                 NFSUNLOCKCLSTATE();
4125                 return;
4126         }
4127         nfsv4_relref(&clp->nfsc_lock);
4128         NFSUNLOCKCLSTATE();
4129 }
4130
4131 /*
4132  * Save the size attribute in the delegation, since the nfsnode
4133  * is going away.
4134  */
4135 APPLESTATIC void
4136 nfscl_reclaimnode(vnode_t vp)
4137 {
4138         struct nfsclclient *clp;
4139         struct nfscldeleg *dp;
4140         struct nfsnode *np = VTONFS(vp);
4141         struct nfsmount *nmp;
4142
4143         nmp = VFSTONFS(vnode_mount(vp));
4144         if (!NFSHASNFSV4(nmp))
4145                 return;
4146         NFSLOCKCLSTATE();
4147         clp = nfscl_findcl(nmp);
4148         if (clp == NULL) {
4149                 NFSUNLOCKCLSTATE();
4150                 return;
4151         }
4152         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4153         if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4154                 dp->nfsdl_size = np->n_size;
4155         NFSUNLOCKCLSTATE();
4156 }
4157
4158 /*
4159  * Get the saved size attribute in the delegation, since it is a
4160  * newly allocated nfsnode.
4161  */
4162 APPLESTATIC void
4163 nfscl_newnode(vnode_t vp)
4164 {
4165         struct nfsclclient *clp;
4166         struct nfscldeleg *dp;
4167         struct nfsnode *np = VTONFS(vp);
4168         struct nfsmount *nmp;
4169
4170         nmp = VFSTONFS(vnode_mount(vp));
4171         if (!NFSHASNFSV4(nmp))
4172                 return;
4173         NFSLOCKCLSTATE();
4174         clp = nfscl_findcl(nmp);
4175         if (clp == NULL) {
4176                 NFSUNLOCKCLSTATE();
4177                 return;
4178         }
4179         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4180         if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE))
4181                 np->n_size = dp->nfsdl_size;
4182         NFSUNLOCKCLSTATE();
4183 }
4184
4185 /*
4186  * If there is a valid write delegation for this file, set the modtime
4187  * to the local clock time.
4188  */
4189 APPLESTATIC void
4190 nfscl_delegmodtime(vnode_t vp)
4191 {
4192         struct nfsclclient *clp;
4193         struct nfscldeleg *dp;
4194         struct nfsnode *np = VTONFS(vp);
4195         struct nfsmount *nmp;
4196
4197         nmp = VFSTONFS(vnode_mount(vp));
4198         if (!NFSHASNFSV4(nmp))
4199                 return;
4200         NFSLOCKCLSTATE();
4201         clp = nfscl_findcl(nmp);
4202         if (clp == NULL) {
4203                 NFSUNLOCKCLSTATE();
4204                 return;
4205         }
4206         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4207         if (dp != NULL && (dp->nfsdl_flags & NFSCLDL_WRITE)) {
4208                 NFSGETNANOTIME(&dp->nfsdl_modtime);
4209                 dp->nfsdl_flags |= NFSCLDL_MODTIMESET;
4210         }
4211         NFSUNLOCKCLSTATE();
4212 }
4213
4214 /*
4215  * If there is a valid write delegation for this file with a modtime set,
4216  * put that modtime in mtime.
4217  */
4218 APPLESTATIC void
4219 nfscl_deleggetmodtime(vnode_t vp, struct timespec *mtime)
4220 {
4221         struct nfsclclient *clp;
4222         struct nfscldeleg *dp;
4223         struct nfsnode *np = VTONFS(vp);
4224         struct nfsmount *nmp;
4225
4226         nmp = VFSTONFS(vnode_mount(vp));
4227         if (!NFSHASNFSV4(nmp))
4228                 return;
4229         NFSLOCKCLSTATE();
4230         clp = nfscl_findcl(nmp);
4231         if (clp == NULL) {
4232                 NFSUNLOCKCLSTATE();
4233                 return;
4234         }
4235         dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh, np->n_fhp->nfh_len);
4236         if (dp != NULL &&
4237             (dp->nfsdl_flags & (NFSCLDL_WRITE | NFSCLDL_MODTIMESET)) ==
4238             (NFSCLDL_WRITE | NFSCLDL_MODTIMESET))
4239                 *mtime = dp->nfsdl_modtime;
4240         NFSUNLOCKCLSTATE();
4241 }
4242
4243 static int
4244 nfscl_errmap(struct nfsrv_descript *nd)
4245 {
4246         short *defaulterrp, *errp;
4247
4248         if (!nd->nd_repstat)
4249                 return (0);
4250         if (nd->nd_procnum == NFSPROC_NOOP)
4251                 return (txdr_unsigned(nd->nd_repstat & 0xffff));
4252         if (nd->nd_repstat == EBADRPC)
4253                 return (txdr_unsigned(NFSERR_BADXDR));
4254         if (nd->nd_repstat == NFSERR_MINORVERMISMATCH ||
4255             nd->nd_repstat == NFSERR_OPILLEGAL)
4256                 return (txdr_unsigned(nd->nd_repstat));
4257         errp = defaulterrp = nfscl_cberrmap[nd->nd_procnum];
4258         while (*++errp)
4259                 if (*errp == (short)nd->nd_repstat)
4260                         return (txdr_unsigned(nd->nd_repstat));
4261         return (txdr_unsigned(*defaulterrp));
4262 }
4263