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