]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/nfs/nfs_node.c
xnu-2050.48.11.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_node.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2000-2013 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/kernel.h>
71#include <sys/systm.h>
72#include <sys/proc.h>
73#include <sys/kauth.h>
74#include <sys/mount_internal.h>
75#include <sys/vnode_internal.h>
76#include <sys/vnode.h>
77#include <sys/ubc.h>
78#include <sys/malloc.h>
79#include <sys/fcntl.h>
80#include <sys/time.h>
81
82#include <nfs/rpcv2.h>
83#include <nfs/nfsproto.h>
84#include <nfs/nfs.h>
85#include <nfs/nfsnode.h>
86#include <nfs/nfs_gss.h>
87#include <nfs/nfsmount.h>
88
89#define NFSNOHASH(fhsum) \
90 (&nfsnodehashtbl[(fhsum) & nfsnodehash])
91static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
92static u_long nfsnodehash;
93
94static lck_grp_t *nfs_node_hash_lck_grp;
95static lck_grp_t *nfs_node_lck_grp;
96static lck_grp_t *nfs_data_lck_grp;
97lck_mtx_t *nfs_node_hash_mutex;
98
99/*
100 * Initialize hash links for nfsnodes
101 * and build nfsnode free list.
102 */
103void
104nfs_nhinit(void)
105{
106 nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", LCK_GRP_ATTR_NULL);
107 nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, LCK_ATTR_NULL);
108 nfs_node_lck_grp = lck_grp_alloc_init("nfs_node", LCK_GRP_ATTR_NULL);
109 nfs_data_lck_grp = lck_grp_alloc_init("nfs_data", LCK_GRP_ATTR_NULL);
110}
111
112void
113nfs_nhinit_finish(void)
114{
115 lck_mtx_lock(nfs_node_hash_mutex);
116 if (!nfsnodehashtbl)
117 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
118 lck_mtx_unlock(nfs_node_hash_mutex);
119}
120
121/*
122 * Compute an entry in the NFS hash table structure
123 */
124u_long
125nfs_hash(u_char *fhp, int fhsize)
126{
127 u_long fhsum;
128 int i;
129
130 fhsum = 0;
131 for (i = 0; i < fhsize; i++)
132 fhsum += *fhp++;
133 return (fhsum);
134}
135
136
137int nfs_case_insensitive(mount_t);
138
139int
140nfs_case_insensitive(mount_t mp)
141{
142 struct nfsmount *nmp = VFSTONFS(mp);
143 int answer = 0;
144 int skip = 0;
145
146 if (nmp == NULL) {
147 return (0);
148 }
149
150 if (nmp->nm_vers == NFS_VER2) {
151 /* V2 has no way to know */
152 return (0);
153 }
154
155 lck_mtx_lock(&nmp->nm_lock);
156 if (nmp->nm_vers == NFS_VER3) {
157 if (!(nmp->nm_state & NFSSTA_GOTPATHCONF)) {
158 /* We're holding the node lock so we just return
159 * with answer as case sensitive. Is very rare
160 * for file systems not to be homogenous w.r.t. pathconf
161 */
162 skip = 1;
163 }
164 } else if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_HOMOGENEOUS)) {
165 /* no pathconf info cached */
166 skip = 1;
167 }
168
169 if (!skip && NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CASE_INSENSITIVE))
170 answer = 1;
171
172 lck_mtx_unlock(&nmp->nm_lock);
173
174 return (answer);
175}
176
177
178/*
179 * Look up a vnode/nfsnode by file handle.
180 * Callers must check for mount points!!
181 * In all cases, a pointer to a
182 * nfsnode structure is returned.
183 */
184int
185nfs_nget(
186 mount_t mp,
187 nfsnode_t dnp,
188 struct componentname *cnp,
189 u_char *fhp,
190 int fhsize,
191 struct nfs_vattr *nvap,
192 u_int64_t *xidp,
193 uint32_t auth,
194 int flags,
195 nfsnode_t *npp)
196{
197 nfsnode_t np;
198 struct nfsnodehashhead *nhpp;
199 vnode_t vp;
200 int error, nfsvers;
201 mount_t mp2;
202 struct vnode_fsparam vfsp;
203 uint32_t vid;
204
205 FSDBG_TOP(263, mp, dnp, flags, npp);
206
207 /* Check for unmount in progress */
208 if (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)) {
209 *npp = NULL;
210 error = ENXIO;
211 FSDBG_BOT(263, mp, dnp, 0xd1e, error);
212 return (error);
213 }
214 nfsvers = VFSTONFS(mp)->nm_vers;
215
216 nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
217loop:
218 lck_mtx_lock(nfs_node_hash_mutex);
219 for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
220 mp2 = (np->n_hflag & NHINIT) ? np->n_mount : NFSTOMP(np);
221 if (mp != mp2 || np->n_fhsize != fhsize ||
222 bcmp(fhp, np->n_fhp, fhsize))
223 continue;
224 if (nvap && (nvap->nva_flags & NFS_FFLAG_TRIGGER_REFERRAL) &&
225 cnp && (cnp->cn_namelen > (fhsize - (int)sizeof(dnp)))) {
226 /* The name was too long to fit in the file handle. Check it against the node's name. */
227 int namecmp = 0;
228 const char *vname = vnode_getname(NFSTOV(np));
229 if (vname) {
230 if (cnp->cn_namelen != (int)strlen(vname))
231 namecmp = 1;
232 else
233 namecmp = strncmp(vname, cnp->cn_nameptr, cnp->cn_namelen);
234 vnode_putname(vname);
235 }
236 if (namecmp) /* full name didn't match */
237 continue;
238 }
239 FSDBG(263, dnp, np, np->n_flag, 0xcace0000);
240 /* if the node is locked, sleep on it */
241 if ((np->n_hflag & NHLOCKED) && !(flags & NG_NOCREATE)) {
242 np->n_hflag |= NHLOCKWANT;
243 FSDBG(263, dnp, np, np->n_flag, 0xcace2222);
244 msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", NULL);
245 FSDBG(263, dnp, np, np->n_flag, 0xcace3333);
246 goto loop;
247 }
248 vp = NFSTOV(np);
249 vid = vnode_vid(vp);
250 lck_mtx_unlock(nfs_node_hash_mutex);
251 if ((error = vnode_getwithvid(vp, vid))) {
252 /*
253 * If vnode is being reclaimed or has already
254 * changed identity, no need to wait.
255 */
256 FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error);
257 return (error);
258 }
259 if ((error = nfs_node_lock(np))) {
260 /* this only fails if the node is now unhashed */
261 /* so let's see if we can find/create it again */
262 FSDBG(263, dnp, *npp, 0xcaced1e2, error);
263 vnode_put(vp);
264 if (flags & NG_NOCREATE) {
265 *npp = 0;
266 FSDBG_BOT(263, dnp, *npp, 0xcaced1e0, ENOENT);
267 return (ENOENT);
268 }
269 goto loop;
270 }
271 /* update attributes */
272 if (nvap)
273 error = nfs_loadattrcache(np, nvap, xidp, 0);
274 if (error) {
275 nfs_node_unlock(np);
276 vnode_put(vp);
277 } else {
278 if (dnp && cnp && (flags & NG_MAKEENTRY))
279 cache_enter(NFSTOV(dnp), vp, cnp);
280 /*
281 * Update the vnode if the name/and or the parent has
282 * changed. We need to do this so that if getattrlist is
283 * called asking for ATTR_CMN_NAME, that the "most"
284 * correct name is being returned if we're not making an
285 * entry. In addition for monitored vnodes we need to
286 * kick the vnode out of the name cache. We do this so
287 * that if there are hard links in the same directory
288 * the link will not be found and a lookup will get us
289 * here to return the name of the current link. In
290 * addition by removing the name from the name cache the
291 * old name will not be found after a rename done on
292 * another client or the server. The principle reason
293 * to do this is because Finder is asking for
294 * notifications on a directory. The directory changes,
295 * Finder gets notified, reads the directory (which we
296 * have purged) and for each entry returned calls
297 * getattrlist with the name returned from
298 * readdir. gettattrlist has to call namei/lookup to
299 * resolve the name, because its not in the cache we end
300 * up here. We need to update the name so Finder will
301 * get the name it called us with.
302 *
303 * We had an imperfect solution with respect to case
304 * sensitivity. There is a test that is run in
305 * FileBuster that does renames from some name to
306 * another name differing only in case. It then reads
307 * the directory looking for the new name, after it
308 * finds that new name, it ask gettattrlist to verify
309 * that the name is the new name. Usually that works,
310 * but renames generate fsevents and fseventsd will do a
311 * lookup on the name via lstat. Since that test renames
312 * old name to new name back and forth there is a race
313 * that an fsevent will be behind and will access the
314 * file by the old name, on a case insensitive file
315 * system that will work. Problem is if we do a case
316 * sensitive compare, we're going to change the name,
317 * which the test's getattrlist verification step is
318 * going to fail. So we will check the case sensitivity
319 * of the file system and do the appropriate compare. In
320 * a rare instance for non homogeneous file systems
321 * w.r.t. pathconf we will use case sensitive compares.
322 * That could break if the file system is actually case
323 * insensitive.
324 *
325 * Note that V2 does not know the case, so we just
326 * assume case sensitivity.
327 *
328 * This is clearly not perfect due to races, but this is
329 * as good as its going to get. You can defeat the
330 * handling of hard links simply by doing:
331 *
332 * while :; do ls -l > /dev/null; done
333 *
334 * in a terminal window. Even a single ls -l can cause a
335 * race.
336 *
337 * <rant>What we really need is for the caller, that
338 * knows the name being used is valid since it got it
339 * from a readdir to use that name and not ask for the
340 * ATTR_CMN_NAME</rant>
341 */
342 if (dnp && cnp && (vp != NFSTOV(dnp))) {
343 int update_flags = vnode_ismonitored((NFSTOV(dnp))) ? VNODE_UPDATE_CACHE : 0;
344 int (*cmp)(const char *s1, const char *s2, size_t n);
345
346 cmp = nfs_case_insensitive(mp) ? strncasecmp : strncmp;
347
348 if (vp->v_name && cnp->cn_namelen && (*cmp)(cnp->cn_nameptr, vp->v_name, cnp->cn_namelen))
349 update_flags |= VNODE_UPDATE_NAME;
350 if ((vp->v_name == NULL && cnp->cn_namelen != 0) || (vp->v_name != NULL && cnp->cn_namelen == 0))
351 update_flags |= VNODE_UPDATE_NAME;
352 if (vnode_parent(vp) != NFSTOV(dnp))
353 update_flags |= VNODE_UPDATE_PARENT;
354 if (update_flags)
355 vnode_update_identity(vp, NFSTOV(dnp), cnp->cn_nameptr, cnp->cn_namelen, 0, update_flags);
356 }
357
358 *npp = np;
359 }
360 FSDBG_BOT(263, dnp, *npp, 0xcace0000, error);
361 return(error);
362 }
363
364 FSDBG(263, mp, dnp, npp, 0xaaaaaaaa);
365
366 if (flags & NG_NOCREATE) {
367 lck_mtx_unlock(nfs_node_hash_mutex);
368 *npp = 0;
369 FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOENT);
370 return (ENOENT);
371 }
372
373 /*
374 * allocate and initialize nfsnode and stick it in the hash
375 * before calling getnewvnode(). Anyone finding it in the
376 * hash before initialization is complete will wait for it.
377 */
378 MALLOC_ZONE(np, nfsnode_t, sizeof *np, M_NFSNODE, M_WAITOK);
379 if (!np) {
380 lck_mtx_unlock(nfs_node_hash_mutex);
381 *npp = 0;
382 FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM);
383 return (ENOMEM);
384 }
385 bzero(np, sizeof *np);
386 np->n_hflag |= (NHINIT | NHLOCKED);
387 np->n_mount = mp;
388 np->n_auth = auth;
389 TAILQ_INIT(&np->n_opens);
390 TAILQ_INIT(&np->n_lock_owners);
391 TAILQ_INIT(&np->n_locks);
392 np->n_dlink.tqe_next = NFSNOLIST;
393 np->n_dreturn.tqe_next = NFSNOLIST;
394 np->n_monlink.le_next = NFSNOLIST;
395
396 /* ugh... need to keep track of ".zfs" directories to workaround server bugs */
397 if ((nvap->nva_type == VDIR) && cnp && (cnp->cn_namelen == 4) &&
398 (cnp->cn_nameptr[0] == '.') && (cnp->cn_nameptr[1] == 'z') &&
399 (cnp->cn_nameptr[2] == 'f') && (cnp->cn_nameptr[3] == 's'))
400 np->n_flag |= NISDOTZFS;
401 if (dnp && (dnp->n_flag & NISDOTZFS))
402 np->n_flag |= NISDOTZFSCHILD;
403
404 if (dnp && cnp && ((cnp->cn_namelen != 2) ||
405 (cnp->cn_nameptr[0] != '.') || (cnp->cn_nameptr[1] != '.'))) {
406 vnode_t dvp = NFSTOV(dnp);
407 if (!vnode_get(dvp)) {
408 if (!vnode_ref(dvp))
409 np->n_parent = dvp;
410 vnode_put(dvp);
411 }
412 }
413
414 /* setup node's file handle */
415 if (fhsize > NFS_SMALLFH) {
416 MALLOC_ZONE(np->n_fhp, u_char *,
417 fhsize, M_NFSBIGFH, M_WAITOK);
418 if (!np->n_fhp) {
419 lck_mtx_unlock(nfs_node_hash_mutex);
420 FREE_ZONE(np, sizeof *np, M_NFSNODE);
421 *npp = 0;
422 FSDBG_BOT(263, dnp, *npp, 0x80000002, ENOMEM);
423 return (ENOMEM);
424 }
425 } else {
426 np->n_fhp = &np->n_fh[0];
427 }
428 bcopy(fhp, np->n_fhp, fhsize);
429 np->n_fhsize = fhsize;
430
431 /* Insert the nfsnode in the hash queue for its new file handle */
432 LIST_INSERT_HEAD(nhpp, np, n_hash);
433 np->n_hflag |= NHHASHED;
434 FSDBG(266, 0, np, np->n_flag, np->n_hflag);
435
436 /* lock the new nfsnode */
437 lck_mtx_init(&np->n_lock, nfs_node_lck_grp, LCK_ATTR_NULL);
438 lck_rw_init(&np->n_datalock, nfs_data_lck_grp, LCK_ATTR_NULL);
439 lck_mtx_init(&np->n_openlock, nfs_open_grp, LCK_ATTR_NULL);
440 lck_mtx_lock(&np->n_lock);
441
442 /* release lock on hash table */
443 lck_mtx_unlock(nfs_node_hash_mutex);
444
445 /* do initial loading of attributes */
446 NACLINVALIDATE(np);
447 NACCESSINVALIDATE(np);
448 error = nfs_loadattrcache(np, nvap, xidp, 1);
449 if (error) {
450 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
451 nfs_node_unlock(np);
452 lck_mtx_lock(nfs_node_hash_mutex);
453 LIST_REMOVE(np, n_hash);
454 np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
455 if (np->n_hflag & NHLOCKWANT) {
456 np->n_hflag &= ~NHLOCKWANT;
457 wakeup(np);
458 }
459 lck_mtx_unlock(nfs_node_hash_mutex);
460 if (np->n_parent) {
461 if (!vnode_get(np->n_parent)) {
462 vnode_rele(np->n_parent);
463 vnode_put(np->n_parent);
464 }
465 np->n_parent = NULL;
466 }
467 lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp);
468 lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp);
469 lck_mtx_destroy(&np->n_openlock, nfs_open_grp);
470 if (np->n_fhsize > NFS_SMALLFH)
471 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
472 FREE_ZONE(np, sizeof *np, M_NFSNODE);
473 *npp = 0;
474 FSDBG_BOT(263, dnp, *npp, 0x80000003, error);
475 return (error);
476 }
477 NFS_CHANGED_UPDATE(nfsvers, np, nvap);
478 if (nvap->nva_type == VDIR)
479 NFS_CHANGED_UPDATE_NC(nfsvers, np, nvap);
480
481 /* now, attempt to get a new vnode */
482 vfsp.vnfs_mp = mp;
483 vfsp.vnfs_vtype = nvap->nva_type;
484 vfsp.vnfs_str = "nfs";
485 vfsp.vnfs_dvp = dnp ? NFSTOV(dnp) : NULL;
486 vfsp.vnfs_fsnode = np;
487 if (nfsvers == NFS_VER4) {
488#if FIFO
489 if (nvap->nva_type == VFIFO)
490 vfsp.vnfs_vops = fifo_nfsv4nodeop_p;
491 else
492#endif /* FIFO */
493 if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
494 vfsp.vnfs_vops = spec_nfsv4nodeop_p;
495 else
496 vfsp.vnfs_vops = nfsv4_vnodeop_p;
497 } else {
498#if FIFO
499 if (nvap->nva_type == VFIFO)
500 vfsp.vnfs_vops = fifo_nfsv2nodeop_p;
501 else
502#endif /* FIFO */
503 if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
504 vfsp.vnfs_vops = spec_nfsv2nodeop_p;
505 else
506 vfsp.vnfs_vops = nfsv2_vnodeop_p;
507 }
508 vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0;
509 vfsp.vnfs_marksystem = 0;
510 vfsp.vnfs_rdev = 0;
511 vfsp.vnfs_filesize = nvap->nva_size;
512 vfsp.vnfs_cnp = cnp;
513 vfsp.vnfs_flags = VNFS_ADDFSREF;
514 if (!dnp || !cnp || !(flags & NG_MAKEENTRY))
515 vfsp.vnfs_flags |= VNFS_NOCACHE;
516
517#if CONFIG_TRIGGERS
518 if ((nfsvers >= NFS_VER4) && (nvap->nva_type == VDIR) && (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER)) {
519 struct vnode_trigger_param vtp;
520 bzero(&vtp, sizeof(vtp));
521 bcopy(&vfsp, &vtp.vnt_params, sizeof(vfsp));
522 vtp.vnt_resolve_func = nfs_mirror_mount_trigger_resolve;
523 vtp.vnt_unresolve_func = nfs_mirror_mount_trigger_unresolve;
524 vtp.vnt_rearm_func = nfs_mirror_mount_trigger_rearm;
525 vtp.vnt_flags = VNT_AUTO_REARM;
526 error = vnode_create(VNCREATE_TRIGGER, VNCREATE_TRIGGER_SIZE, &vtp, &np->n_vnode);
527 } else
528#endif
529 {
530 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &np->n_vnode);
531 }
532 if (error) {
533 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
534 nfs_node_unlock(np);
535 lck_mtx_lock(nfs_node_hash_mutex);
536 LIST_REMOVE(np, n_hash);
537 np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
538 if (np->n_hflag & NHLOCKWANT) {
539 np->n_hflag &= ~NHLOCKWANT;
540 wakeup(np);
541 }
542 lck_mtx_unlock(nfs_node_hash_mutex);
543 if (np->n_parent) {
544 if (!vnode_get(np->n_parent)) {
545 vnode_rele(np->n_parent);
546 vnode_put(np->n_parent);
547 }
548 np->n_parent = NULL;
549 }
550 lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp);
551 lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp);
552 lck_mtx_destroy(&np->n_openlock, nfs_open_grp);
553 if (np->n_fhsize > NFS_SMALLFH)
554 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
555 FREE_ZONE(np, sizeof *np, M_NFSNODE);
556 *npp = 0;
557 FSDBG_BOT(263, dnp, *npp, 0x80000004, error);
558 return (error);
559 }
560 vp = np->n_vnode;
561 vnode_settag(vp, VT_NFS);
562 /* node is now initialized */
563
564 /* check if anyone's waiting on this node */
565 lck_mtx_lock(nfs_node_hash_mutex);
566 np->n_hflag &= ~(NHINIT|NHLOCKED);
567 if (np->n_hflag & NHLOCKWANT) {
568 np->n_hflag &= ~NHLOCKWANT;
569 wakeup(np);
570 }
571 lck_mtx_unlock(nfs_node_hash_mutex);
572
573 *npp = np;
574
575 FSDBG_BOT(263, dnp, vp, *npp, error);
576 return (error);
577}
578
579
580int
581nfs_vnop_inactive(ap)
582 struct vnop_inactive_args /* {
583 struct vnodeop_desc *a_desc;
584 vnode_t a_vp;
585 vfs_context_t a_context;
586 } */ *ap;
587{
588 vnode_t vp = ap->a_vp;
589 vfs_context_t ctx = ap->a_context;
590 nfsnode_t np = VTONFS(ap->a_vp);
591 struct nfs_sillyrename *nsp;
592 struct nfs_vattr nvattr;
593 int unhash, attrerr, busyerror, error, inuse, busied, force;
594 struct nfs_open_file *nofp;
595 struct componentname cn;
596 struct nfsmount *nmp = NFSTONMP(np);
597 mount_t mp = vnode_mount(vp);
598
599restart:
600 force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT));
601 error = 0;
602 inuse = (nfs_mount_state_in_use_start(nmp, NULL) == 0);
603
604 /* There shouldn't be any open or lock state at this point */
605 lck_mtx_lock(&np->n_openlock);
606 if (np->n_openrefcnt && !force)
607 NP(np, "nfs_vnop_inactive: still open: %d", np->n_openrefcnt);
608 TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
609 lck_mtx_lock(&nofp->nof_lock);
610 if (nofp->nof_flags & NFS_OPEN_FILE_BUSY) {
611 if (!force)
612 NP(np, "nfs_vnop_inactive: open file busy");
613 busied = 0;
614 } else {
615 nofp->nof_flags |= NFS_OPEN_FILE_BUSY;
616 busied = 1;
617 }
618 lck_mtx_unlock(&nofp->nof_lock);
619 if ((np->n_flag & NREVOKE) || (nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
620 if (busied)
621 nfs_open_file_clear_busy(nofp);
622 continue;
623 }
624 /*
625 * If we just created the file, we already had it open in
626 * anticipation of getting a subsequent open call. If the
627 * node has gone inactive without being open, we need to
628 * clean up (close) the open done in the create.
629 */
630 if ((nofp->nof_flags & NFS_OPEN_FILE_CREATE) && nofp->nof_creator && !force) {
631 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
632 lck_mtx_unlock(&np->n_openlock);
633 if (busied)
634 nfs_open_file_clear_busy(nofp);
635 if (inuse)
636 nfs_mount_state_in_use_end(nmp, 0);
637 if (!nfs4_reopen(nofp, NULL))
638 goto restart;
639 }
640 nofp->nof_flags &= ~NFS_OPEN_FILE_CREATE;
641 lck_mtx_unlock(&np->n_openlock);
642 error = nfs_close(np, nofp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_NONE, ctx);
643 if (error) {
644 NP(np, "nfs_vnop_inactive: create close error: %d", error);
645 nofp->nof_flags |= NFS_OPEN_FILE_CREATE;
646 }
647 if (busied)
648 nfs_open_file_clear_busy(nofp);
649 if (inuse)
650 nfs_mount_state_in_use_end(nmp, error);
651 goto restart;
652 }
653 if (nofp->nof_flags & NFS_OPEN_FILE_NEEDCLOSE) {
654 /*
655 * If the file is marked as needing reopen, but this was the only
656 * open on the file, just drop the open.
657 */
658 nofp->nof_flags &= ~NFS_OPEN_FILE_NEEDCLOSE;
659 if ((nofp->nof_flags & NFS_OPEN_FILE_REOPEN) && (nofp->nof_opencnt == 1)) {
660 nofp->nof_flags &= ~NFS_OPEN_FILE_REOPEN;
661 nofp->nof_r--;
662 nofp->nof_opencnt--;
663 nofp->nof_access = 0;
664 } else if (!force) {
665 lck_mtx_unlock(&np->n_openlock);
666 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
667 if (busied)
668 nfs_open_file_clear_busy(nofp);
669 if (inuse)
670 nfs_mount_state_in_use_end(nmp, 0);
671 if (!nfs4_reopen(nofp, NULL))
672 goto restart;
673 }
674 error = nfs_close(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx);
675 if (error) {
676 NP(np, "nfs_vnop_inactive: need close error: %d", error);
677 nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE;
678 }
679 if (busied)
680 nfs_open_file_clear_busy(nofp);
681 if (inuse)
682 nfs_mount_state_in_use_end(nmp, error);
683 goto restart;
684 }
685 }
686 if (nofp->nof_opencnt && !force)
687 NP(np, "nfs_vnop_inactive: file still open: %d", nofp->nof_opencnt);
688 if (!force && (nofp->nof_access || nofp->nof_deny ||
689 nofp->nof_mmap_access || nofp->nof_mmap_deny ||
690 nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
691 nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
692 nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw ||
693 nofp->nof_d_r || nofp->nof_d_w || nofp->nof_d_rw ||
694 nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw ||
695 nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) {
696 NP(np, "nfs_vnop_inactive: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u",
697 nofp->nof_access, nofp->nof_deny,
698 nofp->nof_mmap_access, nofp->nof_mmap_deny,
699 nofp->nof_r, nofp->nof_d_r,
700 nofp->nof_w, nofp->nof_d_w,
701 nofp->nof_rw, nofp->nof_d_rw,
702 nofp->nof_r_dw, nofp->nof_d_r_dw,
703 nofp->nof_w_dw, nofp->nof_d_w_dw,
704 nofp->nof_rw_dw, nofp->nof_d_rw_dw,
705 nofp->nof_r_drw, nofp->nof_d_r_drw,
706 nofp->nof_w_drw, nofp->nof_d_w_drw,
707 nofp->nof_rw_drw, nofp->nof_d_rw_drw);
708 }
709 if (busied)
710 nfs_open_file_clear_busy(nofp);
711 }
712 lck_mtx_unlock(&np->n_openlock);
713
714 if (inuse && nfs_mount_state_in_use_end(nmp, error))
715 goto restart;
716
717 nfs_node_lock_force(np);
718
719 if (vnode_vtype(vp) != VDIR) {
720 nsp = np->n_sillyrename;
721 np->n_sillyrename = NULL;
722 } else {
723 nsp = NULL;
724 }
725
726 FSDBG_TOP(264, vp, np, np->n_flag, nsp);
727
728 if (!nsp) {
729 /* no silly file to clean up... */
730 /* clear all flags other than these */
731 np->n_flag &= (NMODIFIED);
732 nfs_node_unlock(np);
733 FSDBG_BOT(264, vp, np, np->n_flag, 0);
734 return (0);
735 }
736 nfs_node_unlock(np);
737
738 /* Remove the silly file that was rename'd earlier */
739
740 /* flush all the buffers */
741 nfs_vinvalbuf2(vp, V_SAVE, vfs_context_thread(ctx), nsp->nsr_cred, 1);
742
743 /* try to get the latest attributes */
744 attrerr = nfs_getattr(np, &nvattr, ctx, NGA_UNCACHED);
745
746 /* Check if we should remove it from the node hash. */
747 /* Leave it if inuse or it has multiple hard links. */
748 if (vnode_isinuse(vp, 0) || (!attrerr && (nvattr.nva_nlink > 1))) {
749 unhash = 0;
750 } else {
751 unhash = 1;
752 ubc_setsize(vp, 0);
753 }
754
755 /* mark this node and the directory busy while we do the remove */
756 busyerror = nfs_node_set_busy2(nsp->nsr_dnp, np, vfs_context_thread(ctx));
757
758 /* lock the node while we remove the silly file */
759 lck_mtx_lock(nfs_node_hash_mutex);
760 while (np->n_hflag & NHLOCKED) {
761 np->n_hflag |= NHLOCKWANT;
762 msleep(np, nfs_node_hash_mutex, PINOD, "nfs_inactive", NULL);
763 }
764 np->n_hflag |= NHLOCKED;
765 lck_mtx_unlock(nfs_node_hash_mutex);
766
767 /* purge the name cache to deter others from finding it */
768 bzero(&cn, sizeof(cn));
769 cn.cn_nameptr = nsp->nsr_name;
770 cn.cn_namelen = nsp->nsr_namlen;
771 nfs_name_cache_purge(nsp->nsr_dnp, np, &cn, ctx);
772
773 FSDBG(264, np, np->n_size, np->n_vattr.nva_size, 0xf00d00f1);
774
775 /* now remove the silly file */
776 nfs_removeit(nsp);
777
778 /* clear all flags other than these */
779 nfs_node_lock_force(np);
780 np->n_flag &= (NMODIFIED);
781 nfs_node_unlock(np);
782
783 if (!busyerror)
784 nfs_node_clear_busy2(nsp->nsr_dnp, np);
785
786 if (unhash && vnode_isinuse(vp, 0)) {
787 /* vnode now inuse after silly remove? */
788 unhash = 0;
789 ubc_setsize(vp, np->n_size);
790 }
791
792 lck_mtx_lock(nfs_node_hash_mutex);
793 if (unhash) {
794 /*
795 * remove nfsnode from hash now so we can't accidentally find it
796 * again if another object gets created with the same filehandle
797 * before this vnode gets reclaimed
798 */
799 if (np->n_hflag & NHHASHED) {
800 LIST_REMOVE(np, n_hash);
801 np->n_hflag &= ~NHHASHED;
802 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
803 }
804 vnode_recycle(vp);
805 }
806 /* unlock the node */
807 np->n_hflag &= ~NHLOCKED;
808 if (np->n_hflag & NHLOCKWANT) {
809 np->n_hflag &= ~NHLOCKWANT;
810 wakeup(np);
811 }
812 lck_mtx_unlock(nfs_node_hash_mutex);
813
814 /* cleanup sillyrename info */
815 if (nsp->nsr_cred != NOCRED)
816 kauth_cred_unref(&nsp->nsr_cred);
817 vnode_rele(NFSTOV(nsp->nsr_dnp));
818 FREE_ZONE(nsp, sizeof(*nsp), M_NFSREQ);
819
820 FSDBG_BOT(264, vp, np, np->n_flag, 0);
821 return (0);
822}
823
824/*
825 * Reclaim an nfsnode so that it can be used for other purposes.
826 */
827int
828nfs_vnop_reclaim(ap)
829 struct vnop_reclaim_args /* {
830 struct vnodeop_desc *a_desc;
831 vnode_t a_vp;
832 vfs_context_t a_context;
833 } */ *ap;
834{
835 vnode_t vp = ap->a_vp;
836 nfsnode_t np = VTONFS(vp);
837 vfs_context_t ctx = ap->a_context;
838 struct nfs_open_file *nofp, *nextnofp;
839 struct nfs_file_lock *nflp, *nextnflp;
840 struct nfs_lock_owner *nlop, *nextnlop;
841 struct nfsmount *nmp = np->n_mount ? VFSTONFS(np->n_mount) : NFSTONMP(np);
842 mount_t mp = vnode_mount(vp);
843 int force;
844
845 FSDBG_TOP(265, vp, np, np->n_flag, 0);
846 force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT));
847
848 /* There shouldn't be any open or lock state at this point */
849 lck_mtx_lock(&np->n_openlock);
850
851 if (nmp && (nmp->nm_vers >= NFS_VER4)) {
852 /* need to drop a delegation */
853 if (np->n_dreturn.tqe_next != NFSNOLIST) {
854 /* remove this node from the delegation return list */
855 lck_mtx_lock(&nmp->nm_lock);
856 if (np->n_dreturn.tqe_next != NFSNOLIST) {
857 TAILQ_REMOVE(&nmp->nm_dreturnq, np, n_dreturn);
858 np->n_dreturn.tqe_next = NFSNOLIST;
859 }
860 lck_mtx_unlock(&nmp->nm_lock);
861 }
862 if (np->n_dlink.tqe_next != NFSNOLIST) {
863 /* remove this node from the delegation list */
864 lck_mtx_lock(&nmp->nm_lock);
865 if (np->n_dlink.tqe_next != NFSNOLIST) {
866 TAILQ_REMOVE(&nmp->nm_delegations, np, n_dlink);
867 np->n_dlink.tqe_next = NFSNOLIST;
868 }
869 lck_mtx_unlock(&nmp->nm_lock);
870 }
871 if ((np->n_openflags & N_DELEG_MASK) && !force) {
872 /* try to return the delegation */
873 np->n_openflags &= ~N_DELEG_MASK;
874 nfs4_delegreturn_rpc(nmp, np->n_fhp, np->n_fhsize, &np->n_dstateid,
875 R_RECOVER, vfs_context_thread(ctx), vfs_context_ucred(ctx));
876 }
877 if (np->n_attrdirfh) {
878 FREE(np->n_attrdirfh, M_TEMP);
879 np->n_attrdirfh = NULL;
880 }
881 }
882
883 /* clean up file locks */
884 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
885 if (!(nflp->nfl_flags & NFS_FILE_LOCK_DEAD) && !force) {
886 NP(np, "nfs_vnop_reclaim: lock 0x%llx 0x%llx 0x%x (bc %d)",
887 nflp->nfl_start, nflp->nfl_end, nflp->nfl_flags, nflp->nfl_blockcnt);
888 }
889 if (!(nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))) {
890 /* try sending an unlock RPC if it wasn't delegated */
891 if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED) && !force)
892 nmp->nm_funcs->nf_unlock_rpc(np, nflp->nfl_owner, F_WRLCK, nflp->nfl_start, nflp->nfl_end, R_RECOVER,
893 NULL, nflp->nfl_owner->nlo_open_owner->noo_cred);
894 lck_mtx_lock(&nflp->nfl_owner->nlo_lock);
895 TAILQ_REMOVE(&nflp->nfl_owner->nlo_locks, nflp, nfl_lolink);
896 lck_mtx_unlock(&nflp->nfl_owner->nlo_lock);
897 }
898 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
899 nfs_file_lock_destroy(nflp);
900 }
901 /* clean up lock owners */
902 TAILQ_FOREACH_SAFE(nlop, &np->n_lock_owners, nlo_link, nextnlop) {
903 if (!TAILQ_EMPTY(&nlop->nlo_locks) && !force)
904 NP(np, "nfs_vnop_reclaim: lock owner with locks");
905 TAILQ_REMOVE(&np->n_lock_owners, nlop, nlo_link);
906 nfs_lock_owner_destroy(nlop);
907 }
908 /* clean up open state */
909 if (np->n_openrefcnt && !force)
910 NP(np, "nfs_vnop_reclaim: still open: %d", np->n_openrefcnt);
911 TAILQ_FOREACH_SAFE(nofp, &np->n_opens, nof_link, nextnofp) {
912 if (nofp->nof_flags & NFS_OPEN_FILE_BUSY)
913 NP(np, "nfs_vnop_reclaim: open file busy");
914 if (!(np->n_flag & NREVOKE) && !(nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
915 if (nofp->nof_opencnt && !force)
916 NP(np, "nfs_vnop_reclaim: file still open: %d", nofp->nof_opencnt);
917 if (!force && (nofp->nof_access || nofp->nof_deny ||
918 nofp->nof_mmap_access || nofp->nof_mmap_deny ||
919 nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
920 nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
921 nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw ||
922 nofp->nof_d_r || nofp->nof_d_w || nofp->nof_d_rw ||
923 nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw ||
924 nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) {
925 NP(np, "nfs_vnop_reclaim: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u",
926 nofp->nof_access, nofp->nof_deny,
927 nofp->nof_mmap_access, nofp->nof_mmap_deny,
928 nofp->nof_r, nofp->nof_d_r,
929 nofp->nof_w, nofp->nof_d_w,
930 nofp->nof_rw, nofp->nof_d_rw,
931 nofp->nof_r_dw, nofp->nof_d_r_dw,
932 nofp->nof_w_dw, nofp->nof_d_w_dw,
933 nofp->nof_rw_dw, nofp->nof_d_rw_dw,
934 nofp->nof_r_drw, nofp->nof_d_r_drw,
935 nofp->nof_w_drw, nofp->nof_d_w_drw,
936 nofp->nof_rw_drw, nofp->nof_d_rw_drw);
937 /* try sending a close RPC if it wasn't delegated */
938 if (nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
939 nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
940 nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw)
941 nfs4_close_rpc(np, nofp, NULL, nofp->nof_owner->noo_cred, R_RECOVER);
942 }
943 }
944 TAILQ_REMOVE(&np->n_opens, nofp, nof_link);
945 nfs_open_file_destroy(nofp);
946 }
947 lck_mtx_unlock(&np->n_openlock);
948
949 if (np->n_monlink.le_next != NFSNOLIST) {
950 /* Wait for any in-progress getattr to complete, */
951 /* then remove this node from the monitored node list. */
952 lck_mtx_lock(&nmp->nm_lock);
953 while (np->n_mflag & NMMONSCANINPROG) {
954 struct timespec ts = { 1, 0 };
955 np->n_mflag |= NMMONSCANWANT;
956 msleep(&np->n_mflag, &nmp->nm_lock, PZERO-1, "nfswaitmonscan", &ts);
957 }
958 if (np->n_monlink.le_next != NFSNOLIST) {
959 LIST_REMOVE(np, n_monlink);
960 np->n_monlink.le_next = NFSNOLIST;
961 }
962 lck_mtx_unlock(&nmp->nm_lock);
963 }
964
965 lck_mtx_lock(nfs_buf_mutex);
966 if (!force && (!LIST_EMPTY(&np->n_dirtyblkhd) || !LIST_EMPTY(&np->n_cleanblkhd)))
967 NP(np, "nfs_reclaim: dropping %s buffers", (!LIST_EMPTY(&np->n_dirtyblkhd) ? "dirty" : "clean"));
968 lck_mtx_unlock(nfs_buf_mutex);
969 nfs_vinvalbuf(vp, V_IGNORE_WRITEERR, ap->a_context, 0);
970
971 lck_mtx_lock(nfs_node_hash_mutex);
972
973 if ((vnode_vtype(vp) != VDIR) && np->n_sillyrename) {
974 if (!force)
975 NP(np, "nfs_reclaim: leaving unlinked file %s", np->n_sillyrename->nsr_name);
976 if (np->n_sillyrename->nsr_cred != NOCRED)
977 kauth_cred_unref(&np->n_sillyrename->nsr_cred);
978 vnode_rele(NFSTOV(np->n_sillyrename->nsr_dnp));
979 FREE_ZONE(np->n_sillyrename, sizeof(*np->n_sillyrename), M_NFSREQ);
980 }
981
982 vnode_removefsref(vp);
983
984 if (np->n_hflag & NHHASHED) {
985 LIST_REMOVE(np, n_hash);
986 np->n_hflag &= ~NHHASHED;
987 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
988 }
989 lck_mtx_unlock(nfs_node_hash_mutex);
990
991 /*
992 * Free up any directory cookie structures and large file handle
993 * structures that might be associated with this nfs node.
994 */
995 nfs_node_lock_force(np);
996 if ((vnode_vtype(vp) == VDIR) && np->n_cookiecache)
997 FREE_ZONE(np->n_cookiecache, sizeof(struct nfsdmap), M_NFSDIROFF);
998 if (np->n_fhsize > NFS_SMALLFH)
999 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
1000 if (np->n_vattr.nva_acl)
1001 kauth_acl_free(np->n_vattr.nva_acl);
1002 nfs_node_unlock(np);
1003 vnode_clearfsnode(vp);
1004
1005 if (np->n_parent) {
1006 if (!vnode_get(np->n_parent)) {
1007 vnode_rele(np->n_parent);
1008 vnode_put(np->n_parent);
1009 }
1010 np->n_parent = NULL;
1011 }
1012
1013 lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp);
1014 lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp);
1015 lck_mtx_destroy(&np->n_openlock, nfs_open_grp);
1016
1017 FSDBG_BOT(265, vp, np, np->n_flag, 0xd1ed1e);
1018 FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE);
1019 return (0);
1020}
1021
1022/*
1023 * Acquire an NFS node lock
1024 */
1025
1026int
1027nfs_node_lock_internal(nfsnode_t np, int force)
1028{
1029 FSDBG_TOP(268, np, force, 0, 0);
1030 lck_mtx_lock(&np->n_lock);
1031 if (!force && !(np->n_hflag && NHHASHED)) {
1032 FSDBG_BOT(268, np, 0xdead, 0, 0);
1033 lck_mtx_unlock(&np->n_lock);
1034 return (ENOENT);
1035 }
1036 FSDBG_BOT(268, np, force, 0, 0);
1037 return (0);
1038}
1039
1040int
1041nfs_node_lock(nfsnode_t np)
1042{
1043 return nfs_node_lock_internal(np, 0);
1044}
1045
1046void
1047nfs_node_lock_force(nfsnode_t np)
1048{
1049 nfs_node_lock_internal(np, 1);
1050}
1051
1052/*
1053 * Release an NFS node lock
1054 */
1055void
1056nfs_node_unlock(nfsnode_t np)
1057{
1058 FSDBG(269, np, current_thread(), 0, 0);
1059 lck_mtx_unlock(&np->n_lock);
1060}
1061
1062/*
1063 * Acquire 2 NFS node locks
1064 * - locks taken in reverse address order
1065 * - both or neither of the locks are taken
1066 * - only one lock taken per node (dup nodes are skipped)
1067 */
1068int
1069nfs_node_lock2(nfsnode_t np1, nfsnode_t np2)
1070{
1071 nfsnode_t first, second;
1072 int error;
1073
1074 first = (np1 > np2) ? np1 : np2;
1075 second = (np1 > np2) ? np2 : np1;
1076 if ((error = nfs_node_lock(first)))
1077 return (error);
1078 if (np1 == np2)
1079 return (error);
1080 if ((error = nfs_node_lock(second)))
1081 nfs_node_unlock(first);
1082 return (error);
1083}
1084
1085void
1086nfs_node_unlock2(nfsnode_t np1, nfsnode_t np2)
1087{
1088 nfs_node_unlock(np1);
1089 if (np1 != np2)
1090 nfs_node_unlock(np2);
1091}
1092
1093/*
1094 * Manage NFS node busy state.
1095 * (Similar to NFS node locks above)
1096 */
1097int
1098nfs_node_set_busy(nfsnode_t np, thread_t thd)
1099{
1100 struct timespec ts = { 2, 0 };
1101 int error;
1102
1103 if ((error = nfs_node_lock(np)))
1104 return (error);
1105 while (ISSET(np->n_flag, NBUSY)) {
1106 SET(np->n_flag, NBUSYWANT);
1107 msleep(np, &np->n_lock, PZERO-1, "nfsbusywant", &ts);
1108 if ((error = nfs_sigintr(NFSTONMP(np), NULL, thd, 0)))
1109 break;
1110 }
1111 if (!error)
1112 SET(np->n_flag, NBUSY);
1113 nfs_node_unlock(np);
1114 return (error);
1115}
1116
1117void
1118nfs_node_clear_busy(nfsnode_t np)
1119{
1120 int wanted;
1121
1122 nfs_node_lock_force(np);
1123 wanted = ISSET(np->n_flag, NBUSYWANT);
1124 CLR(np->n_flag, NBUSY|NBUSYWANT);
1125 nfs_node_unlock(np);
1126 if (wanted)
1127 wakeup(np);
1128}
1129
1130int
1131nfs_node_set_busy2(nfsnode_t np1, nfsnode_t np2, thread_t thd)
1132{
1133 nfsnode_t first, second;
1134 int error;
1135
1136 first = (np1 > np2) ? np1 : np2;
1137 second = (np1 > np2) ? np2 : np1;
1138 if ((error = nfs_node_set_busy(first, thd)))
1139 return (error);
1140 if (np1 == np2)
1141 return (error);
1142 if ((error = nfs_node_set_busy(second, thd)))
1143 nfs_node_clear_busy(first);
1144 return (error);
1145}
1146
1147void
1148nfs_node_clear_busy2(nfsnode_t np1, nfsnode_t np2)
1149{
1150 nfs_node_clear_busy(np1);
1151 if (np1 != np2)
1152 nfs_node_clear_busy(np2);
1153}
1154
1155/* helper function to sort four nodes in reverse address order (no dupes) */
1156static void
1157nfs_node_sort4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4, nfsnode_t *list, int *lcntp)
1158{
1159 nfsnode_t na[2], nb[2];
1160 int a, b, i, lcnt;
1161
1162 /* sort pairs then merge */
1163 na[0] = (np1 > np2) ? np1 : np2;
1164 na[1] = (np1 > np2) ? np2 : np1;
1165 nb[0] = (np3 > np4) ? np3 : np4;
1166 nb[1] = (np3 > np4) ? np4 : np3;
1167 for (a = b = i = lcnt = 0; i < 4; i++) {
1168 if (a >= 2)
1169 list[lcnt] = nb[b++];
1170 else if ((b >= 2) || (na[a] >= nb[b]))
1171 list[lcnt] = na[a++];
1172 else
1173 list[lcnt] = nb[b++];
1174 if ((lcnt <= 0) || (list[lcnt] != list[lcnt-1]))
1175 lcnt++; /* omit dups */
1176 }
1177 if (list[lcnt-1] == NULL)
1178 lcnt--;
1179 *lcntp = lcnt;
1180}
1181
1182int
1183nfs_node_set_busy4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4, thread_t thd)
1184{
1185 nfsnode_t list[4];
1186 int i, lcnt, error;
1187
1188 nfs_node_sort4(np1, np2, np3, np4, list, &lcnt);
1189
1190 /* Now we can lock using list[0 - lcnt-1] */
1191 for (i = 0; i < lcnt; ++i)
1192 if ((error = nfs_node_set_busy(list[i], thd))) {
1193 /* Drop any locks we acquired. */
1194 while (--i >= 0)
1195 nfs_node_clear_busy(list[i]);
1196 return (error);
1197 }
1198 return (0);
1199}
1200
1201void
1202nfs_node_clear_busy4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4)
1203{
1204 nfsnode_t list[4];
1205 int lcnt;
1206
1207 nfs_node_sort4(np1, np2, np3, np4, list, &lcnt);
1208 while (--lcnt >= 0)
1209 nfs_node_clear_busy(list[lcnt]);
1210}
1211
1212/*
1213 * Acquire an NFS node data lock
1214 */
1215void
1216nfs_data_lock(nfsnode_t np, int locktype)
1217{
1218 nfs_data_lock_internal(np, locktype, 1);
1219}
1220void
1221nfs_data_lock_noupdate(nfsnode_t np, int locktype)
1222{
1223 nfs_data_lock_internal(np, locktype, 0);
1224}
1225void
1226nfs_data_lock_internal(nfsnode_t np, int locktype, int updatesize)
1227{
1228 FSDBG_TOP(270, np, locktype, np->n_datalockowner, 0);
1229 if (locktype == NFS_DATA_LOCK_SHARED) {
1230 if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
1231 nfs_data_update_size(np, 0);
1232 lck_rw_lock_shared(&np->n_datalock);
1233 } else {
1234 lck_rw_lock_exclusive(&np->n_datalock);
1235 np->n_datalockowner = current_thread();
1236 if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
1237 nfs_data_update_size(np, 1);
1238 }
1239 FSDBG_BOT(270, np, locktype, np->n_datalockowner, 0);
1240}
1241
1242/*
1243 * Release an NFS node data lock
1244 */
1245void
1246nfs_data_unlock(nfsnode_t np)
1247{
1248 nfs_data_unlock_internal(np, 1);
1249}
1250void
1251nfs_data_unlock_noupdate(nfsnode_t np)
1252{
1253 nfs_data_unlock_internal(np, 0);
1254}
1255void
1256nfs_data_unlock_internal(nfsnode_t np, int updatesize)
1257{
1258 int mine = (np->n_datalockowner == current_thread());
1259 FSDBG_TOP(271, np, np->n_datalockowner, current_thread(), 0);
1260 if (updatesize && mine && ISSET(np->n_flag, NUPDATESIZE))
1261 nfs_data_update_size(np, 1);
1262 np->n_datalockowner = NULL;
1263 lck_rw_done(&np->n_datalock);
1264 if (updatesize && !mine && ISSET(np->n_flag, NUPDATESIZE))
1265 nfs_data_update_size(np, 0);
1266 FSDBG_BOT(271, np, np->n_datalockowner, current_thread(), 0);
1267}
1268
1269
1270/*
1271 * update an NFS node's size
1272 */
1273void
1274nfs_data_update_size(nfsnode_t np, int datalocked)
1275{
1276 int error;
1277
1278 FSDBG_TOP(272, np, np->n_flag, np->n_size, np->n_newsize);
1279 if (!datalocked) {
1280 nfs_data_lock(np, NFS_DATA_LOCK_EXCLUSIVE);
1281 /* grabbing data lock will automatically update size */
1282 nfs_data_unlock(np);
1283 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
1284 return;
1285 }
1286 error = nfs_node_lock(np);
1287 if (error || !ISSET(np->n_flag, NUPDATESIZE)) {
1288 if (!error)
1289 nfs_node_unlock(np);
1290 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
1291 return;
1292 }
1293 CLR(np->n_flag, NUPDATESIZE);
1294 np->n_size = np->n_newsize;
1295 /* make sure we invalidate buffers the next chance we get */
1296 SET(np->n_flag, NNEEDINVALIDATE);
1297 nfs_node_unlock(np);
1298 ubc_setsize(NFSTOV(np), (off_t)np->n_size); /* XXX error? */
1299 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
1300}
1301
1302#define DODEBUG 1
1303int
1304nfs_mount_is_dirty(mount_t mp)
1305{
1306 u_long i;
1307 nfsnode_t np;
1308#ifdef DODEBUG
1309 struct timeval now, then, diff;
1310 u_long ncnt = 0;
1311 microuptime(&now);
1312#endif
1313 lck_mtx_lock(nfs_node_hash_mutex);
1314 for (i = 0; i <= nfsnodehash; i++) {
1315 LIST_FOREACH(np, &nfsnodehashtbl[i], n_hash) {
1316#ifdef DODEBUG
1317 ncnt++;
1318#endif
1319 if (np->n_mount == mp && !LIST_EMPTY(&np->n_dirtyblkhd))
1320 goto out;
1321 }
1322 }
1323out:
1324 lck_mtx_unlock(nfs_node_hash_mutex);
1325#ifdef DODEBUG
1326 microuptime(&then);
1327 timersub(&then, &now, &diff);
1328
1329 printf("nfs_mount_is_dirty took %lld mics for %ld slots and %ld nodes return %d\n",
1330 (uint64_t)diff.tv_sec * 1000000LL + diff.tv_usec, i, ncnt, (i <= nfsnodehash));
1331#endif
1332
1333 return (i <= nfsnodehash);
1334}