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