]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/nfs/nfs_node.c
xnu-1228.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_node.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)nfs_node.c 8.6 (Berkeley) 5/22/95
65 * FreeBSD-Id: nfs_node.c,v 1.22 1997/10/28 14:06:20 bde Exp $
66 */
67
68
69#include <sys/param.h>
70#include <sys/systm.h>
71#include <sys/proc.h>
72#include <sys/kauth.h>
73#include <sys/mount_internal.h>
74#include <sys/vnode.h>
75#include <sys/ubc.h>
76#include <sys/malloc.h>
77
78#include <nfs/rpcv2.h>
79#include <nfs/nfsproto.h>
80#include <nfs/nfs.h>
81#include <nfs/nfsnode.h>
82#include <nfs/nfs_gss.h>
83#include <nfs/nfsmount.h>
84
85#define NFSNOHASH(fhsum) \
86 (&nfsnodehashtbl[(fhsum) & nfsnodehash])
87static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
88static u_long nfsnodehash;
89
90static lck_grp_t *nfs_node_hash_lck_grp;
91static lck_grp_t *nfs_node_lck_grp;
92lck_mtx_t *nfs_node_hash_mutex;
93
94/*
95 * Initialize hash links for nfsnodes
96 * and build nfsnode free list.
97 */
98void
99nfs_nhinit(void)
100{
101 nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", LCK_GRP_ATTR_NULL);
102 nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, LCK_ATTR_NULL);
103 nfs_node_lck_grp = lck_grp_alloc_init("nfs_node", LCK_GRP_ATTR_NULL);
104}
105
106void
107nfs_nhinit_finish(void)
108{
109 lck_mtx_lock(nfs_node_hash_mutex);
110 if (!nfsnodehashtbl)
111 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
112 lck_mtx_unlock(nfs_node_hash_mutex);
113}
114
115/*
116 * Compute an entry in the NFS hash table structure
117 */
118u_long
119nfs_hash(u_char *fhp, int fhsize)
120{
121 u_long fhsum;
122 int i;
123
124 fhsum = 0;
125 for (i = 0; i < fhsize; i++)
126 fhsum += *fhp++;
127 return (fhsum);
128}
129
130/*
131 * Look up a vnode/nfsnode by file handle.
132 * Callers must check for mount points!!
133 * In all cases, a pointer to a
134 * nfsnode structure is returned.
135 */
136int
137nfs_nget(
138 mount_t mp,
139 nfsnode_t dnp,
140 struct componentname *cnp,
141 u_char *fhp,
142 int fhsize,
143 struct nfs_vattr *nvap,
144 u_int64_t *xidp,
145 int flags,
146 nfsnode_t *npp)
147{
148 nfsnode_t np;
149 struct nfsnodehashhead *nhpp;
150 vnode_t vp;
151 int error, nfsvers;
152 mount_t mp2;
153 struct vnode_fsparam vfsp;
154 uint32_t vid;
155
156 FSDBG_TOP(263, mp, dnp, flags, npp);
157
158 /* Check for unmount in progress */
159 if (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)) {
160 *npp = NULL;
161 error = ENXIO;
162 FSDBG_BOT(263, mp, dnp, 0xd1e, error);
163 return (error);
164 }
165 nfsvers = VFSTONFS(mp)->nm_vers;
166
167 nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
168loop:
169 lck_mtx_lock(nfs_node_hash_mutex);
170 for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
171 mp2 = (np->n_hflag & NHINIT) ? np->n_mount : NFSTOMP(np);
172 if (mp != mp2 || np->n_fhsize != fhsize ||
173 bcmp(fhp, np->n_fhp, fhsize))
174 continue;
175 FSDBG(263, dnp, np, np->n_flag, 0xcace0000);
176 /* if the node is locked, sleep on it */
177 if (np->n_hflag & NHLOCKED) {
178 np->n_hflag |= NHLOCKWANT;
179 FSDBG(263, dnp, np, np->n_flag, 0xcace2222);
180 msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", NULL);
181 FSDBG(263, dnp, np, np->n_flag, 0xcace3333);
182 goto loop;
183 }
184 vp = NFSTOV(np);
185 vid = vnode_vid(vp);
186 lck_mtx_unlock(nfs_node_hash_mutex);
187 if ((error = vnode_getwithvid(vp, vid))) {
188 /*
189 * If vnode is being reclaimed or has already
190 * changed identity, no need to wait.
191 */
192 FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error);
193 return (error);
194 }
195 if ((error = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE))) {
196 /* this only fails if the node is now unhashed */
197 /* so let's see if we can find/create it again */
198 FSDBG(263, dnp, *npp, 0xcaced1e2, error);
199 vnode_put(vp);
200 goto loop;
201 }
202 /* update attributes */
203 error = nfs_loadattrcache(np, nvap, xidp, 0);
204 if (error) {
205 nfs_unlock(np);
206 vnode_put(vp);
207 } else {
208 if (dnp && cnp && (flags & NG_MAKEENTRY))
209 cache_enter(NFSTOV(dnp), vp, cnp);
210 *npp = np;
211 }
212 FSDBG_BOT(263, dnp, *npp, 0xcace0000, error);
213 return(error);
214 }
215
216 FSDBG(263, mp, dnp, npp, 0xaaaaaaaa);
217
218 /*
219 * allocate and initialize nfsnode and stick it in the hash
220 * before calling getnewvnode(). Anyone finding it in the
221 * hash before initialization is complete will wait for it.
222 */
223 MALLOC_ZONE(np, nfsnode_t, sizeof *np, M_NFSNODE, M_WAITOK);
224 if (!np) {
225 lck_mtx_unlock(nfs_node_hash_mutex);
226 *npp = 0;
227 FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM);
228 return (ENOMEM);
229 }
230 bzero(np, sizeof *np);
231 np->n_hflag |= (NHINIT | NHLOCKED);
232 np->n_mount = mp;
233
234 if (dnp && cnp && ((cnp->cn_namelen != 2) ||
235 (cnp->cn_nameptr[0] != '.') || (cnp->cn_nameptr[1] != '.'))) {
236 vnode_t dvp = NFSTOV(dnp);
237 if (!vnode_get(dvp)) {
238 if (!vnode_ref(dvp))
239 np->n_parent = dvp;
240 vnode_put(dvp);
241 }
242 }
243
244 /* setup node's file handle */
245 if (fhsize > NFS_SMALLFH) {
246 MALLOC_ZONE(np->n_fhp, u_char *,
247 fhsize, M_NFSBIGFH, M_WAITOK);
248 if (!np->n_fhp) {
249 lck_mtx_unlock(nfs_node_hash_mutex);
250 FREE_ZONE(np, sizeof *np, M_NFSNODE);
251 *npp = 0;
252 FSDBG_BOT(263, dnp, *npp, 0x80000002, ENOMEM);
253 return (ENOMEM);
254 }
255 } else {
256 np->n_fhp = &np->n_fh[0];
257 }
258 bcopy(fhp, np->n_fhp, fhsize);
259 np->n_fhsize = fhsize;
260
261 /* Insert the nfsnode in the hash queue for its new file handle */
262 LIST_INSERT_HEAD(nhpp, np, n_hash);
263 np->n_hflag |= NHHASHED;
264 FSDBG(266, 0, np, np->n_flag, np->n_hflag);
265
266 /* lock the new nfsnode */
267 lck_rw_init(&np->n_lock, nfs_node_lck_grp, LCK_ATTR_NULL);
268 lck_rw_init(&np->n_datalock, nfs_node_lck_grp, LCK_ATTR_NULL);
269 nfs_lock(np, NFS_NODE_LOCK_FORCE);
270
271 /* release lock on hash table */
272 lck_mtx_unlock(nfs_node_hash_mutex);
273
274 /* do initial loading of attributes */
275 error = nfs_loadattrcache(np, nvap, xidp, 1);
276 if (error) {
277 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
278 nfs_unlock(np);
279 lck_mtx_lock(nfs_node_hash_mutex);
280 LIST_REMOVE(np, n_hash);
281 np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
282 if (np->n_hflag & NHLOCKWANT) {
283 np->n_hflag &= ~NHLOCKWANT;
284 wakeup(np);
285 }
286 lck_mtx_unlock(nfs_node_hash_mutex);
287 if (np->n_parent) {
288 if (!vnode_get(np->n_parent)) {
289 vnode_rele(np->n_parent);
290 vnode_put(np->n_parent);
291 }
292 np->n_parent = NULL;
293 }
294 lck_rw_destroy(&np->n_lock, nfs_node_lck_grp);
295 lck_rw_destroy(&np->n_datalock, nfs_node_lck_grp);
296 if (np->n_fhsize > NFS_SMALLFH)
297 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
298 FREE_ZONE(np, sizeof *np, M_NFSNODE);
299 *npp = 0;
300 FSDBG_BOT(263, dnp, *npp, 0x80000003, error);
301 return (error);
302 }
303 NFS_CHANGED_UPDATE(nfsvers, np, nvap);
304 if (nvap->nva_type == VDIR)
305 NFS_CHANGED_UPDATE_NC(nfsvers, np, nvap);
306 NMODEINVALIDATE(np);
307
308 /* now, attempt to get a new vnode */
309 vfsp.vnfs_mp = mp;
310 vfsp.vnfs_vtype = nvap->nva_type;
311 vfsp.vnfs_str = "nfs";
312 vfsp.vnfs_dvp = dnp ? NFSTOV(dnp) : NULL;
313 vfsp.vnfs_fsnode = np;
314 if (nfsvers == NFS_VER4) {
315#if FIFO
316 if (nvap->nva_type == VFIFO)
317 vfsp.vnfs_vops = fifo_nfsv4nodeop_p;
318 else
319#endif /* FIFO */
320 if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
321 vfsp.vnfs_vops = spec_nfsv4nodeop_p;
322 else
323 vfsp.vnfs_vops = nfsv4_vnodeop_p;
324 } else {
325#if FIFO
326 if (nvap->nva_type == VFIFO)
327 vfsp.vnfs_vops = fifo_nfsv2nodeop_p;
328 else
329#endif /* FIFO */
330 if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
331 vfsp.vnfs_vops = spec_nfsv2nodeop_p;
332 else
333 vfsp.vnfs_vops = nfsv2_vnodeop_p;
334 }
335 vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0;
336 vfsp.vnfs_marksystem = 0;
337 vfsp.vnfs_rdev = 0;
338 vfsp.vnfs_filesize = nvap->nva_size;
339 vfsp.vnfs_cnp = cnp;
340 vfsp.vnfs_flags = VNFS_ADDFSREF;
341 if (!dnp || !cnp || !(flags & NG_MAKEENTRY))
342 vfsp.vnfs_flags |= VNFS_NOCACHE;
343
344 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &np->n_vnode);
345 if (error) {
346 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
347 nfs_unlock(np);
348 lck_mtx_lock(nfs_node_hash_mutex);
349 LIST_REMOVE(np, n_hash);
350 np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
351 if (np->n_hflag & NHLOCKWANT) {
352 np->n_hflag &= ~NHLOCKWANT;
353 wakeup(np);
354 }
355 lck_mtx_unlock(nfs_node_hash_mutex);
356 if (np->n_parent) {
357 if (!vnode_get(np->n_parent)) {
358 vnode_rele(np->n_parent);
359 vnode_put(np->n_parent);
360 }
361 np->n_parent = NULL;
362 }
363 lck_rw_destroy(&np->n_lock, nfs_node_lck_grp);
364 lck_rw_destroy(&np->n_datalock, nfs_node_lck_grp);
365 if (np->n_fhsize > NFS_SMALLFH)
366 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
367 FREE_ZONE(np, sizeof *np, M_NFSNODE);
368 *npp = 0;
369 FSDBG_BOT(263, dnp, *npp, 0x80000004, error);
370 return (error);
371 }
372 vp = np->n_vnode;
373 vnode_settag(vp, VT_NFS);
374 /* node is now initialized */
375
376 /* check if anyone's waiting on this node */
377 lck_mtx_lock(nfs_node_hash_mutex);
378 np->n_hflag &= ~(NHINIT|NHLOCKED);
379 if (np->n_hflag & NHLOCKWANT) {
380 np->n_hflag &= ~NHLOCKWANT;
381 wakeup(np);
382 }
383 lck_mtx_unlock(nfs_node_hash_mutex);
384
385 *npp = np;
386
387 FSDBG_BOT(263, dnp, vp, *npp, error);
388 return (error);
389}
390
391
392int
393nfs_vnop_inactive(ap)
394 struct vnop_inactive_args /* {
395 struct vnodeop_desc *a_desc;
396 vnode_t a_vp;
397 vfs_context_t a_context;
398 } */ *ap;
399{
400 vnode_t vp;
401 nfsnode_t np;
402 struct nfs_sillyrename *nsp;
403 struct nfs_vattr nvattr;
404 int unhash, attrerr;
405
406 vp = ap->a_vp;
407 np = VTONFS(ap->a_vp);
408
409 nfs_lock(np, NFS_NODE_LOCK_FORCE);
410
411 if (vnode_vtype(vp) != VDIR) {
412 nsp = np->n_sillyrename;
413 np->n_sillyrename = NULL;
414 } else
415 nsp = NULL;
416
417 FSDBG_TOP(264, vp, np, np->n_flag, nsp);
418
419 if (!nsp) {
420 /* no silly file to clean up... */
421 /* clear all flags other than these */
422 np->n_flag &= (NMODIFIED);
423 nfs_unlock(np);
424 FSDBG_BOT(264, vp, np, np->n_flag, 0);
425 return (0);
426 }
427
428 /* Remove the silly file that was rename'd earlier */
429
430 /* flush all the buffers */
431 nfs_unlock(np);
432 nfs_vinvalbuf2(vp, V_SAVE, vfs_context_thread(ap->a_context), nsp->nsr_cred, 1);
433
434 /* purge the name cache to deter others from finding it */
435 cache_purge(vp);
436
437 /* try to get the latest attributes */
438 attrerr = nfs_getattr(np, &nvattr, ap->a_context, 0);
439
440 /* Check if we should remove it from the node hash. */
441 /* Leave it if inuse or it has multiple hard links. */
442 if (vnode_isinuse(vp, 0) || (!attrerr && (nvattr.nva_nlink > 1))) {
443 unhash = 0;
444 } else {
445 unhash = 1;
446 ubc_setsize(vp, 0);
447 }
448
449 /* grab node lock on this node and the directory */
450 nfs_lock2(nsp->nsr_dnp, np, NFS_NODE_LOCK_FORCE);
451
452 /* lock the node while we remove the silly file */
453 lck_mtx_lock(nfs_node_hash_mutex);
454 while (np->n_hflag & NHLOCKED) {
455 np->n_hflag |= NHLOCKWANT;
456 msleep(np, nfs_node_hash_mutex, PINOD, "nfs_inactive", NULL);
457 }
458 np->n_hflag |= NHLOCKED;
459 lck_mtx_unlock(nfs_node_hash_mutex);
460
461 /* purge again in case it was looked up while we were locking */
462 cache_purge(vp);
463
464 FSDBG(264, np, np->n_size, np->n_vattr.nva_size, 0xf00d00f1);
465
466 /* now remove the silly file */
467 nfs_removeit(nsp);
468
469 /* clear all flags other than these */
470 np->n_flag &= (NMODIFIED);
471 nfs_unlock2(nsp->nsr_dnp, np);
472
473 if (unhash && vnode_isinuse(vp, 0)) {
474 /* vnode now inuse after silly remove? */
475 unhash = 0;
476 ubc_setsize(vp, np->n_size);
477 }
478
479 lck_mtx_lock(nfs_node_hash_mutex);
480 if (unhash) {
481 /*
482 * remove nfsnode from hash now so we can't accidentally find it
483 * again if another object gets created with the same filehandle
484 * before this vnode gets reclaimed
485 */
486 if (np->n_hflag & NHHASHED) {
487 LIST_REMOVE(np, n_hash);
488 np->n_hflag &= ~NHHASHED;
489 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
490 }
491 vnode_recycle(vp);
492 }
493 /* unlock the node */
494 np->n_hflag &= ~NHLOCKED;
495 if (np->n_hflag & NHLOCKWANT) {
496 np->n_hflag &= ~NHLOCKWANT;
497 wakeup(np);
498 }
499 lck_mtx_unlock(nfs_node_hash_mutex);
500
501 /* cleanup sillyrename info */
502 if (nsp->nsr_cred != NOCRED)
503 kauth_cred_unref(&nsp->nsr_cred);
504 vnode_rele(NFSTOV(nsp->nsr_dnp));
505 FREE_ZONE(nsp, sizeof(*nsp), M_NFSREQ);
506
507 FSDBG_BOT(264, vp, np, np->n_flag, 0);
508 return (0);
509}
510
511/*
512 * Reclaim an nfsnode so that it can be used for other purposes.
513 */
514int
515nfs_vnop_reclaim(ap)
516 struct vnop_reclaim_args /* {
517 struct vnodeop_desc *a_desc;
518 vnode_t a_vp;
519 vfs_context_t a_context;
520 } */ *ap;
521{
522 vnode_t vp = ap->a_vp;
523 nfsnode_t np = VTONFS(vp);
524 struct nfsdmap *dp, *dp2;
525
526 FSDBG_TOP(265, vp, np, np->n_flag, 0);
527
528 lck_mtx_lock(nfs_node_hash_mutex);
529
530 if ((vnode_vtype(vp) != VDIR) && np->n_sillyrename)
531 printf("nfs_reclaim: leaving unlinked file %s\n", np->n_sillyrename->nsr_name);
532
533 vnode_removefsref(vp);
534
535 if (np->n_hflag & NHHASHED) {
536 LIST_REMOVE(np, n_hash);
537 np->n_hflag &= ~NHHASHED;
538 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
539 }
540 lck_mtx_unlock(nfs_node_hash_mutex);
541
542 /*
543 * Free up any directory cookie structures and
544 * large file handle structures that might be associated with
545 * this nfs node.
546 */
547 nfs_lock(np, NFS_NODE_LOCK_FORCE);
548 if (vnode_vtype(vp) == VDIR) {
549 dp = np->n_cookies.lh_first;
550 while (dp) {
551 dp2 = dp;
552 dp = dp->ndm_list.le_next;
553 FREE_ZONE((caddr_t)dp2,
554 sizeof (struct nfsdmap), M_NFSDIROFF);
555 }
556 }
557 if (np->n_fhsize > NFS_SMALLFH) {
558 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
559 }
560
561 nfs_unlock(np);
562 vnode_clearfsnode(vp);
563
564 if (np->n_parent) {
565 if (!vnode_get(np->n_parent)) {
566 vnode_rele(np->n_parent);
567 vnode_put(np->n_parent);
568 }
569 np->n_parent = NULL;
570 }
571
572 lck_rw_destroy(&np->n_lock, nfs_node_lck_grp);
573 lck_rw_destroy(&np->n_datalock, nfs_node_lck_grp);
574
575 FSDBG_BOT(265, vp, np, np->n_flag, 0xd1ed1e);
576 FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE);
577 return (0);
578}
579
580/*
581 * Acquire an NFS node lock
582 */
583int
584nfs_lock(nfsnode_t np, int locktype)
585{
586 FSDBG_TOP(268, np, locktype, np->n_lockowner, 0);
587 if (locktype == NFS_NODE_LOCK_SHARED) {
588 lck_rw_lock_shared(&np->n_lock);
589 } else {
590 lck_rw_lock_exclusive(&np->n_lock);
591 np->n_lockowner = current_thread();
592 }
593 if ((locktype != NFS_NODE_LOCK_FORCE) && !(np->n_hflag && NHHASHED)) {
594 FSDBG_BOT(268, np, 0xdead, np->n_lockowner, 0);
595 nfs_unlock(np);
596 return (ENOENT);
597 }
598 FSDBG_BOT(268, np, locktype, np->n_lockowner, 0);
599 return (0);
600}
601
602/*
603 * Release an NFS node lock
604 */
605void
606nfs_unlock(nfsnode_t np)
607{
608 FSDBG(269, np, np->n_lockowner, current_thread(), 0);
609 np->n_lockowner = NULL;
610 lck_rw_done(&np->n_lock);
611}
612
613/*
614 * Acquire 2 NFS node locks
615 * - locks taken in order given (assumed to be parent-child order)
616 * - both or neither of the locks are taken
617 * - only one lock taken per node (dup nodes are skipped)
618 */
619int
620nfs_lock2(nfsnode_t np1, nfsnode_t np2, int locktype)
621{
622 int error;
623
624 if ((error = nfs_lock(np1, locktype)))
625 return (error);
626 if (np1 == np2)
627 return (error);
628 if ((error = nfs_lock(np2, locktype)))
629 nfs_unlock(np1);
630 return (error);
631}
632
633/*
634 * Unlock a couple of NFS nodes
635 */
636void
637nfs_unlock2(nfsnode_t np1, nfsnode_t np2)
638{
639 nfs_unlock(np1);
640 if (np1 != np2)
641 nfs_unlock(np2);
642}
643
644/*
645 * Acquire 4 NFS node locks
646 * - fdnp/fnp and tdnp/tnp locks taken in order given
647 * - otherwise locks taken in node address order.
648 * - all or none of the locks are taken
649 * - only one lock taken per node (dup nodes are skipped)
650 * - some of the node pointers may be null
651 */
652int
653nfs_lock4(nfsnode_t fdnp, nfsnode_t fnp, nfsnode_t tdnp, nfsnode_t tnp, int locktype)
654{
655 nfsnode_t list[4];
656 int i, lcnt = 0, error;
657
658 if (fdnp == tdnp) {
659 list[lcnt++] = fdnp;
660 } else if (fdnp->n_parent && (tdnp == VTONFS(fdnp->n_parent))) {
661 list[lcnt++] = tdnp;
662 list[lcnt++] = fdnp;
663 } else if (tdnp->n_parent && (fdnp == VTONFS(tdnp->n_parent))) {
664 list[lcnt++] = fdnp;
665 list[lcnt++] = tdnp;
666 } else if (fdnp < tdnp) {
667 list[lcnt++] = fdnp;
668 list[lcnt++] = tdnp;
669 } else {
670 list[lcnt++] = tdnp;
671 list[lcnt++] = fdnp;
672 }
673
674 if (!tnp || (fnp == tnp) || (tnp == fdnp)) {
675 list[lcnt++] = fnp;
676 } else if (fnp < tnp) {
677 list[lcnt++] = fnp;
678 list[lcnt++] = tnp;
679 } else {
680 list[lcnt++] = tnp;
681 list[lcnt++] = fnp;
682 }
683
684 /* Now we can lock using list[0 - lcnt-1] */
685 for (i = 0; i < lcnt; ++i) {
686 if (list[i])
687 if ((error = nfs_lock(list[i], locktype))) {
688 /* Drop any locks we acquired. */
689 while (--i >= 0) {
690 if (list[i])
691 nfs_unlock(list[i]);
692 }
693 return (error);
694 }
695 }
696 return (0);
697}
698
699/*
700 * Unlock a group of NFS nodes
701 */
702void
703nfs_unlock4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4)
704{
705 nfsnode_t list[4];
706 int i, k = 0;
707
708 if (np1) {
709 nfs_unlock(np1);
710 list[k++] = np1;
711 }
712 if (np2) {
713 for (i = 0; i < k; ++i)
714 if (list[i] == np2)
715 goto skip2;
716 nfs_unlock(np2);
717 list[k++] = np2;
718 }
719skip2:
720 if (np3) {
721 for (i = 0; i < k; ++i)
722 if (list[i] == np3)
723 goto skip3;
724 nfs_unlock(np3);
725 list[k++] = np3;
726 }
727skip3:
728 if (np4) {
729 for (i = 0; i < k; ++i)
730 if (list[i] == np4)
731 return;
732 nfs_unlock(np4);
733 }
734}
735
736/*
737 * Acquire an NFS node data lock
738 */
739void
740nfs_data_lock(nfsnode_t np, int locktype)
741{
742 nfs_data_lock2(np, locktype, 1);
743}
744void
745nfs_data_lock2(nfsnode_t np, int locktype, int updatesize)
746{
747 FSDBG_TOP(270, np, locktype, np->n_datalockowner, 0);
748 if (locktype == NFS_NODE_LOCK_SHARED) {
749 if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
750 nfs_data_update_size(np, 0);
751 lck_rw_lock_shared(&np->n_datalock);
752 } else {
753 lck_rw_lock_exclusive(&np->n_datalock);
754 np->n_datalockowner = current_thread();
755 if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
756 nfs_data_update_size(np, 1);
757 }
758 FSDBG_BOT(270, np, locktype, np->n_datalockowner, 0);
759}
760
761/*
762 * Release an NFS node data lock
763 */
764void
765nfs_data_unlock(nfsnode_t np)
766{
767 nfs_data_unlock2(np, 1);
768}
769void
770nfs_data_unlock2(nfsnode_t np, int updatesize)
771{
772 int mine = (np->n_datalockowner == current_thread());
773 FSDBG_TOP(271, np, np->n_datalockowner, current_thread(), 0);
774 if (updatesize && mine && ISSET(np->n_flag, NUPDATESIZE))
775 nfs_data_update_size(np, 1);
776 np->n_datalockowner = NULL;
777 lck_rw_done(&np->n_datalock);
778 if (updatesize && !mine && ISSET(np->n_flag, NUPDATESIZE))
779 nfs_data_update_size(np, 0);
780 FSDBG_BOT(271, np, np->n_datalockowner, current_thread(), 0);
781}
782
783
784/*
785 * update an NFS node's size
786 */
787void
788nfs_data_update_size(nfsnode_t np, int datalocked)
789{
790 int error;
791
792 FSDBG_TOP(272, np, np->n_flag, np->n_size, np->n_newsize);
793 if (!datalocked) {
794 nfs_data_lock(np, NFS_NODE_LOCK_EXCLUSIVE);
795 /* grabbing data lock will automatically update size */
796 nfs_data_unlock(np);
797 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
798 return;
799 }
800 error = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE);
801 if (error || !ISSET(np->n_flag, NUPDATESIZE)) {
802 if (!error)
803 nfs_unlock(np);
804 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
805 return;
806 }
807 CLR(np->n_flag, NUPDATESIZE);
808 np->n_size = np->n_newsize;
809 /* make sure we invalidate buffers the next chance we get */
810 SET(np->n_flag, NNEEDINVALIDATE);
811 nfs_unlock(np);
812 ubc_setsize(NFSTOV(np), (off_t)np->n_size); /* XXX error? */
813 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
814}
815