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