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}
1335