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