2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
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.
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
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 $
69 #include <sys/param.h>
70 #include <sys/kernel.h>
71 #include <sys/systm.h>
73 #include <sys/kauth.h>
74 #include <sys/mount_internal.h>
75 #include <sys/vnode.h>
77 #include <sys/malloc.h>
79 #include <nfs/rpcv2.h>
80 #include <nfs/nfsproto.h>
82 #include <nfs/nfsnode.h>
83 #include <nfs/nfs_gss.h>
84 #include <nfs/nfsmount.h>
86 #define NFSNOHASH(fhsum) \
87 (&nfsnodehashtbl[(fhsum) & nfsnodehash])
88 static LIST_HEAD(nfsnodehashhead
, nfsnode
) *nfsnodehashtbl
;
89 static u_long nfsnodehash
;
91 static lck_grp_t
*nfs_node_hash_lck_grp
;
92 static lck_grp_t
*nfs_node_lck_grp
;
93 static lck_grp_t
*nfs_data_lck_grp
;
94 lck_mtx_t
*nfs_node_hash_mutex
;
97 * Initialize hash links for nfsnodes
98 * and build nfsnode free list.
103 nfs_node_hash_lck_grp
= lck_grp_alloc_init("nfs_node_hash", LCK_GRP_ATTR_NULL
);
104 nfs_node_hash_mutex
= lck_mtx_alloc_init(nfs_node_hash_lck_grp
, LCK_ATTR_NULL
);
105 nfs_node_lck_grp
= lck_grp_alloc_init("nfs_node", LCK_GRP_ATTR_NULL
);
106 nfs_data_lck_grp
= lck_grp_alloc_init("nfs_data", LCK_GRP_ATTR_NULL
);
110 nfs_nhinit_finish(void)
112 lck_mtx_lock(nfs_node_hash_mutex
);
114 nfsnodehashtbl
= hashinit(desiredvnodes
, M_NFSNODE
, &nfsnodehash
);
115 lck_mtx_unlock(nfs_node_hash_mutex
);
119 * Compute an entry in the NFS hash table structure
122 nfs_hash(u_char
*fhp
, int fhsize
)
128 for (i
= 0; i
< fhsize
; i
++)
134 * Look up a vnode/nfsnode by file handle.
135 * Callers must check for mount points!!
136 * In all cases, a pointer to a
137 * nfsnode structure is returned.
143 struct componentname
*cnp
,
146 struct nfs_vattr
*nvap
,
152 struct nfsnodehashhead
*nhpp
;
156 struct vnode_fsparam vfsp
;
159 FSDBG_TOP(263, mp
, dnp
, flags
, npp
);
161 /* Check for unmount in progress */
162 if (!mp
|| (mp
->mnt_kern_flag
& MNTK_FRCUNMOUNT
)) {
165 FSDBG_BOT(263, mp
, dnp
, 0xd1e, error
);
168 nfsvers
= VFSTONFS(mp
)->nm_vers
;
170 nhpp
= NFSNOHASH(nfs_hash(fhp
, fhsize
));
172 lck_mtx_lock(nfs_node_hash_mutex
);
173 for (np
= nhpp
->lh_first
; np
!= 0; np
= np
->n_hash
.le_next
) {
174 mp2
= (np
->n_hflag
& NHINIT
) ? np
->n_mount
: NFSTOMP(np
);
175 if (mp
!= mp2
|| np
->n_fhsize
!= fhsize
||
176 bcmp(fhp
, np
->n_fhp
, fhsize
))
178 FSDBG(263, dnp
, np
, np
->n_flag
, 0xcace0000);
179 /* if the node is locked, sleep on it */
180 if ((np
->n_hflag
& NHLOCKED
) && !(flags
& NG_NOCREATE
)) {
181 np
->n_hflag
|= NHLOCKWANT
;
182 FSDBG(263, dnp
, np
, np
->n_flag
, 0xcace2222);
183 msleep(np
, nfs_node_hash_mutex
, PDROP
| PINOD
, "nfs_nget", NULL
);
184 FSDBG(263, dnp
, np
, np
->n_flag
, 0xcace3333);
189 lck_mtx_unlock(nfs_node_hash_mutex
);
190 if ((error
= vnode_getwithvid(vp
, vid
))) {
192 * If vnode is being reclaimed or has already
193 * changed identity, no need to wait.
195 FSDBG_BOT(263, dnp
, *npp
, 0xcace0d1e, error
);
198 if ((error
= nfs_node_lock(np
))) {
199 /* this only fails if the node is now unhashed */
200 /* so let's see if we can find/create it again */
201 FSDBG(263, dnp
, *npp
, 0xcaced1e2, error
);
203 if (flags
& NG_NOCREATE
) {
205 FSDBG_BOT(263, dnp
, *npp
, 0xcaced1e0, ENOENT
);
210 /* update attributes */
212 error
= nfs_loadattrcache(np
, nvap
, xidp
, 0);
217 if (dnp
&& cnp
&& (flags
& NG_MAKEENTRY
))
218 cache_enter(NFSTOV(dnp
), vp
, cnp
);
221 FSDBG_BOT(263, dnp
, *npp
, 0xcace0000, error
);
225 FSDBG(263, mp
, dnp
, npp
, 0xaaaaaaaa);
227 if (flags
& NG_NOCREATE
) {
228 lck_mtx_unlock(nfs_node_hash_mutex
);
230 FSDBG_BOT(263, dnp
, *npp
, 0x80000001, ENOENT
);
235 * allocate and initialize nfsnode and stick it in the hash
236 * before calling getnewvnode(). Anyone finding it in the
237 * hash before initialization is complete will wait for it.
239 MALLOC_ZONE(np
, nfsnode_t
, sizeof *np
, M_NFSNODE
, M_WAITOK
);
241 lck_mtx_unlock(nfs_node_hash_mutex
);
243 FSDBG_BOT(263, dnp
, *npp
, 0x80000001, ENOMEM
);
246 bzero(np
, sizeof *np
);
247 np
->n_hflag
|= (NHINIT
| NHLOCKED
);
249 TAILQ_INIT(&np
->n_opens
);
250 TAILQ_INIT(&np
->n_lock_owners
);
251 TAILQ_INIT(&np
->n_locks
);
252 np
->n_dlink
.tqe_next
= NFSNOLIST
;
254 if (dnp
&& cnp
&& ((cnp
->cn_namelen
!= 2) ||
255 (cnp
->cn_nameptr
[0] != '.') || (cnp
->cn_nameptr
[1] != '.'))) {
256 vnode_t dvp
= NFSTOV(dnp
);
257 if (!vnode_get(dvp
)) {
264 /* setup node's file handle */
265 if (fhsize
> NFS_SMALLFH
) {
266 MALLOC_ZONE(np
->n_fhp
, u_char
*,
267 fhsize
, M_NFSBIGFH
, M_WAITOK
);
269 lck_mtx_unlock(nfs_node_hash_mutex
);
270 FREE_ZONE(np
, sizeof *np
, M_NFSNODE
);
272 FSDBG_BOT(263, dnp
, *npp
, 0x80000002, ENOMEM
);
276 np
->n_fhp
= &np
->n_fh
[0];
278 bcopy(fhp
, np
->n_fhp
, fhsize
);
279 np
->n_fhsize
= fhsize
;
281 /* Insert the nfsnode in the hash queue for its new file handle */
282 LIST_INSERT_HEAD(nhpp
, np
, n_hash
);
283 np
->n_hflag
|= NHHASHED
;
284 FSDBG(266, 0, np
, np
->n_flag
, np
->n_hflag
);
286 /* lock the new nfsnode */
287 lck_mtx_init(&np
->n_lock
, nfs_node_lck_grp
, LCK_ATTR_NULL
);
288 lck_rw_init(&np
->n_datalock
, nfs_data_lck_grp
, LCK_ATTR_NULL
);
289 lck_mtx_init(&np
->n_openlock
, nfs_open_grp
, LCK_ATTR_NULL
);
290 lck_mtx_lock(&np
->n_lock
);
292 /* release lock on hash table */
293 lck_mtx_unlock(nfs_node_hash_mutex
);
295 /* do initial loading of attributes */
296 error
= nfs_loadattrcache(np
, nvap
, xidp
, 1);
298 FSDBG(266, 0, np
, np
->n_flag
, 0xb1eb1e);
300 lck_mtx_lock(nfs_node_hash_mutex
);
301 LIST_REMOVE(np
, n_hash
);
302 np
->n_hflag
&= ~(NHHASHED
|NHINIT
|NHLOCKED
);
303 if (np
->n_hflag
& NHLOCKWANT
) {
304 np
->n_hflag
&= ~NHLOCKWANT
;
307 lck_mtx_unlock(nfs_node_hash_mutex
);
309 if (!vnode_get(np
->n_parent
)) {
310 vnode_rele(np
->n_parent
);
311 vnode_put(np
->n_parent
);
315 lck_mtx_destroy(&np
->n_lock
, nfs_node_lck_grp
);
316 lck_rw_destroy(&np
->n_datalock
, nfs_data_lck_grp
);
317 lck_mtx_destroy(&np
->n_openlock
, nfs_open_grp
);
318 if (np
->n_fhsize
> NFS_SMALLFH
)
319 FREE_ZONE(np
->n_fhp
, np
->n_fhsize
, M_NFSBIGFH
);
320 FREE_ZONE(np
, sizeof *np
, M_NFSNODE
);
322 FSDBG_BOT(263, dnp
, *npp
, 0x80000003, error
);
325 NFS_CHANGED_UPDATE(nfsvers
, np
, nvap
);
326 if (nvap
->nva_type
== VDIR
)
327 NFS_CHANGED_UPDATE_NC(nfsvers
, np
, nvap
);
330 /* now, attempt to get a new vnode */
332 vfsp
.vnfs_vtype
= nvap
->nva_type
;
333 vfsp
.vnfs_str
= "nfs";
334 vfsp
.vnfs_dvp
= dnp
? NFSTOV(dnp
) : NULL
;
335 vfsp
.vnfs_fsnode
= np
;
336 if (nfsvers
== NFS_VER4
) {
338 if (nvap
->nva_type
== VFIFO
)
339 vfsp
.vnfs_vops
= fifo_nfsv4nodeop_p
;
342 if (nvap
->nva_type
== VBLK
|| nvap
->nva_type
== VCHR
)
343 vfsp
.vnfs_vops
= spec_nfsv4nodeop_p
;
345 vfsp
.vnfs_vops
= nfsv4_vnodeop_p
;
348 if (nvap
->nva_type
== VFIFO
)
349 vfsp
.vnfs_vops
= fifo_nfsv2nodeop_p
;
352 if (nvap
->nva_type
== VBLK
|| nvap
->nva_type
== VCHR
)
353 vfsp
.vnfs_vops
= spec_nfsv2nodeop_p
;
355 vfsp
.vnfs_vops
= nfsv2_vnodeop_p
;
357 vfsp
.vnfs_markroot
= (flags
& NG_MARKROOT
) ? 1 : 0;
358 vfsp
.vnfs_marksystem
= 0;
360 vfsp
.vnfs_filesize
= nvap
->nva_size
;
362 vfsp
.vnfs_flags
= VNFS_ADDFSREF
;
363 if (!dnp
|| !cnp
|| !(flags
& NG_MAKEENTRY
))
364 vfsp
.vnfs_flags
|= VNFS_NOCACHE
;
366 error
= vnode_create(VNCREATE_FLAVOR
, VCREATESIZE
, &vfsp
, &np
->n_vnode
);
368 FSDBG(266, 0, np
, np
->n_flag
, 0xb1eb1e);
370 lck_mtx_lock(nfs_node_hash_mutex
);
371 LIST_REMOVE(np
, n_hash
);
372 np
->n_hflag
&= ~(NHHASHED
|NHINIT
|NHLOCKED
);
373 if (np
->n_hflag
& NHLOCKWANT
) {
374 np
->n_hflag
&= ~NHLOCKWANT
;
377 lck_mtx_unlock(nfs_node_hash_mutex
);
379 if (!vnode_get(np
->n_parent
)) {
380 vnode_rele(np
->n_parent
);
381 vnode_put(np
->n_parent
);
385 lck_mtx_destroy(&np
->n_lock
, nfs_node_lck_grp
);
386 lck_rw_destroy(&np
->n_datalock
, nfs_data_lck_grp
);
387 lck_mtx_destroy(&np
->n_openlock
, nfs_open_grp
);
388 if (np
->n_fhsize
> NFS_SMALLFH
)
389 FREE_ZONE(np
->n_fhp
, np
->n_fhsize
, M_NFSBIGFH
);
390 FREE_ZONE(np
, sizeof *np
, M_NFSNODE
);
392 FSDBG_BOT(263, dnp
, *npp
, 0x80000004, error
);
396 vnode_settag(vp
, VT_NFS
);
397 /* node is now initialized */
399 /* check if anyone's waiting on this node */
400 lck_mtx_lock(nfs_node_hash_mutex
);
401 np
->n_hflag
&= ~(NHINIT
|NHLOCKED
);
402 if (np
->n_hflag
& NHLOCKWANT
) {
403 np
->n_hflag
&= ~NHLOCKWANT
;
406 lck_mtx_unlock(nfs_node_hash_mutex
);
410 FSDBG_BOT(263, dnp
, vp
, *npp
, error
);
416 nfs_vnop_inactive(ap
)
417 struct vnop_inactive_args
/* {
418 struct vnodeop_desc *a_desc;
420 vfs_context_t a_context;
423 vnode_t vp
= ap
->a_vp
;
424 vfs_context_t ctx
= ap
->a_context
;
425 nfsnode_t np
= VTONFS(ap
->a_vp
);
426 struct nfs_sillyrename
*nsp
;
427 struct nfs_vattr nvattr
;
428 int unhash
, attrerr
, busyerror
, error
, inuse
, busied
;
429 struct nfs_open_file
*nofp
;
430 const char *vname
= NULL
;
431 struct componentname cn
;
432 struct nfsmount
*nmp
= NFSTONMP(np
);
436 inuse
= ((nmp
->nm_vers
>= NFS_VER4
) && (nfs_mount_state_in_use_start(nmp
) == 0));
438 /* There shouldn't be any open or lock state at this point */
439 lck_mtx_lock(&np
->n_openlock
);
440 if (np
->n_openrefcnt
) {
441 vname
= vnode_getname(vp
);
442 printf("nfs_vnop_inactive: still open: %d %s\n", np
->n_openrefcnt
, vname
? vname
: "//");
444 TAILQ_FOREACH(nofp
, &np
->n_opens
, nof_link
) {
445 lck_mtx_lock(&nofp
->nof_lock
);
446 if (nofp
->nof_flags
& NFS_OPEN_FILE_BUSY
) {
448 vname
= vnode_getname(vp
);
449 printf("nfs_vnop_inactive: open file busy: %s\n", vname
? vname
: "//");
452 nofp
->nof_flags
|= NFS_OPEN_FILE_BUSY
;
455 lck_mtx_unlock(&nofp
->nof_lock
);
457 * If we just created the file, we already had it open in
458 * anticipation of getting a subsequent open call. If the
459 * node has gone inactive without being open, we need to
460 * clean up (close) the open done in the create.
462 if ((nofp
->nof_flags
& NFS_OPEN_FILE_CREATE
) && nofp
->nof_creator
) {
463 if (nofp
->nof_flags
& NFS_OPEN_FILE_REOPEN
) {
464 lck_mtx_unlock(&np
->n_openlock
);
466 nfs_open_file_clear_busy(nofp
);
468 nfs_mount_state_in_use_end(nmp
, 0);
469 nfs4_reopen(nofp
, vfs_context_thread(ctx
));
472 nofp
->nof_flags
&= ~NFS_OPEN_FILE_CREATE
;
473 lck_mtx_unlock(&np
->n_openlock
);
474 error
= nfs4_close(np
, nofp
, NFS_OPEN_SHARE_ACCESS_BOTH
, NFS_OPEN_SHARE_DENY_NONE
, ctx
);
477 vname
= vnode_getname(vp
);
478 printf("nfs_vnop_inactive: create close error: %d, %s\n", error
, vname
);
479 nofp
->nof_flags
|= NFS_OPEN_FILE_CREATE
;
482 nfs_open_file_clear_busy(nofp
);
484 nfs_mount_state_in_use_end(nmp
, error
);
487 if (nofp
->nof_flags
& NFS_OPEN_FILE_NEEDCLOSE
) {
489 * If the file is marked as needing reopen, but this was the only
490 * open on the file, just drop the open.
492 nofp
->nof_flags
&= ~NFS_OPEN_FILE_NEEDCLOSE
;
493 if ((nofp
->nof_flags
& NFS_OPEN_FILE_REOPEN
) && (nofp
->nof_opencnt
== 1)) {
494 nofp
->nof_flags
&= ~NFS_OPEN_FILE_REOPEN
;
497 nofp
->nof_access
= 0;
499 lck_mtx_unlock(&np
->n_openlock
);
500 if (nofp
->nof_flags
& NFS_OPEN_FILE_REOPEN
) {
502 nfs_open_file_clear_busy(nofp
);
504 nfs_mount_state_in_use_end(nmp
, 0);
505 nfs4_reopen(nofp
, vfs_context_thread(ctx
));
508 error
= nfs4_close(np
, nofp
, NFS_OPEN_SHARE_ACCESS_READ
, NFS_OPEN_SHARE_DENY_NONE
, ctx
);
511 vname
= vnode_getname(vp
);
512 printf("nfs_vnop_inactive: need close error: %d, %s\n", error
, vname
);
513 nofp
->nof_flags
|= NFS_OPEN_FILE_NEEDCLOSE
;
516 nfs_open_file_clear_busy(nofp
);
518 nfs_mount_state_in_use_end(nmp
, error
);
522 if (nofp
->nof_opencnt
) {
524 vname
= vnode_getname(vp
);
525 printf("nfs_vnop_inactive: file still open: %d %s\n", nofp
->nof_opencnt
, vname
? vname
: "//");
527 if (nofp
->nof_access
|| nofp
->nof_deny
||
528 nofp
->nof_mmap_access
|| nofp
->nof_mmap_deny
||
529 nofp
->nof_r
|| nofp
->nof_w
|| nofp
->nof_rw
||
530 nofp
->nof_r_dw
|| nofp
->nof_w_dw
|| nofp
->nof_rw_dw
||
531 nofp
->nof_r_drw
|| nofp
->nof_w_drw
|| nofp
->nof_rw_drw
) {
533 vname
= vnode_getname(vp
);
534 printf("nfs_vnop_inactive: non-zero access: %d %d %d %d # %u %u %u dw %u %u %u drw %u %u %u %s\n",
535 nofp
->nof_access
, nofp
->nof_deny
,
536 nofp
->nof_mmap_access
, nofp
->nof_mmap_deny
,
537 nofp
->nof_r
, nofp
->nof_w
, nofp
->nof_rw
,
538 nofp
->nof_r_dw
, nofp
->nof_w_dw
, nofp
->nof_rw_dw
,
539 nofp
->nof_r_drw
, nofp
->nof_w_drw
, nofp
->nof_rw_drw
,
540 vname
? vname
: "//");
543 nfs_open_file_clear_busy(nofp
);
545 lck_mtx_unlock(&np
->n_openlock
);
547 vnode_putname(vname
);
549 if (inuse
&& nfs_mount_state_in_use_end(nmp
, error
))
552 nfs_node_lock_force(np
);
554 if (vnode_vtype(vp
) != VDIR
) {
555 nsp
= np
->n_sillyrename
;
556 np
->n_sillyrename
= NULL
;
561 FSDBG_TOP(264, vp
, np
, np
->n_flag
, nsp
);
564 /* no silly file to clean up... */
565 /* clear all flags other than these */
566 np
->n_flag
&= (NMODIFIED
);
568 FSDBG_BOT(264, vp
, np
, np
->n_flag
, 0);
573 /* Remove the silly file that was rename'd earlier */
575 /* flush all the buffers */
576 nfs_vinvalbuf2(vp
, V_SAVE
, vfs_context_thread(ctx
), nsp
->nsr_cred
, 1);
578 /* try to get the latest attributes */
579 attrerr
= nfs_getattr(np
, &nvattr
, ctx
, NGA_UNCACHED
);
581 /* Check if we should remove it from the node hash. */
582 /* Leave it if inuse or it has multiple hard links. */
583 if (vnode_isinuse(vp
, 0) || (!attrerr
&& (nvattr
.nva_nlink
> 1))) {
590 /* mark this node and the directory busy while we do the remove */
591 busyerror
= nfs_node_set_busy2(nsp
->nsr_dnp
, np
, vfs_context_thread(ctx
));
593 /* lock the node while we remove the silly file */
594 lck_mtx_lock(nfs_node_hash_mutex
);
595 while (np
->n_hflag
& NHLOCKED
) {
596 np
->n_hflag
|= NHLOCKWANT
;
597 msleep(np
, nfs_node_hash_mutex
, PINOD
, "nfs_inactive", NULL
);
599 np
->n_hflag
|= NHLOCKED
;
600 lck_mtx_unlock(nfs_node_hash_mutex
);
602 /* purge the name cache to deter others from finding it */
603 bzero(&cn
, sizeof(cn
));
604 cn
.cn_nameptr
= nsp
->nsr_name
;
605 cn
.cn_namelen
= nsp
->nsr_namlen
;
606 nfs_name_cache_purge(nsp
->nsr_dnp
, np
, &cn
, ctx
);
608 FSDBG(264, np
, np
->n_size
, np
->n_vattr
.nva_size
, 0xf00d00f1);
610 /* now remove the silly file */
613 /* clear all flags other than these */
614 nfs_node_lock_force(np
);
615 np
->n_flag
&= (NMODIFIED
);
619 nfs_node_clear_busy2(nsp
->nsr_dnp
, np
);
621 if (unhash
&& vnode_isinuse(vp
, 0)) {
622 /* vnode now inuse after silly remove? */
624 ubc_setsize(vp
, np
->n_size
);
627 lck_mtx_lock(nfs_node_hash_mutex
);
630 * remove nfsnode from hash now so we can't accidentally find it
631 * again if another object gets created with the same filehandle
632 * before this vnode gets reclaimed
634 if (np
->n_hflag
& NHHASHED
) {
635 LIST_REMOVE(np
, n_hash
);
636 np
->n_hflag
&= ~NHHASHED
;
637 FSDBG(266, 0, np
, np
->n_flag
, 0xb1eb1e);
641 /* unlock the node */
642 np
->n_hflag
&= ~NHLOCKED
;
643 if (np
->n_hflag
& NHLOCKWANT
) {
644 np
->n_hflag
&= ~NHLOCKWANT
;
647 lck_mtx_unlock(nfs_node_hash_mutex
);
649 /* cleanup sillyrename info */
650 if (nsp
->nsr_cred
!= NOCRED
)
651 kauth_cred_unref(&nsp
->nsr_cred
);
652 vnode_rele(NFSTOV(nsp
->nsr_dnp
));
653 FREE_ZONE(nsp
, sizeof(*nsp
), M_NFSREQ
);
655 FSDBG_BOT(264, vp
, np
, np
->n_flag
, 0);
660 * Reclaim an nfsnode so that it can be used for other purposes.
664 struct vnop_reclaim_args
/* {
665 struct vnodeop_desc *a_desc;
667 vfs_context_t a_context;
670 vnode_t vp
= ap
->a_vp
;
671 nfsnode_t np
= VTONFS(vp
);
672 vfs_context_t ctx
= ap
->a_context
;
673 struct nfs_open_file
*nofp
, *nextnofp
;
674 struct nfs_file_lock
*nflp
, *nextnflp
;
675 struct nfs_lock_owner
*nlop
, *nextnlop
;
676 const char *vname
= NULL
;
677 struct nfsmount
*nmp
= np
->n_mount
? VFSTONFS(np
->n_mount
) : NFSTONMP(np
);
679 FSDBG_TOP(265, vp
, np
, np
->n_flag
, 0);
681 /* There shouldn't be any open or lock state at this point */
682 lck_mtx_lock(&np
->n_openlock
);
684 if (nmp
&& (nmp
->nm_vers
>= NFS_VER4
)) {
685 /* need to drop a delegation */
686 if (np
->n_dlink
.tqe_next
!= NFSNOLIST
) {
687 /* remove this node from the recall list */
688 lck_mtx_lock(&nmp
->nm_lock
);
689 if (np
->n_dlink
.tqe_next
!= NFSNOLIST
) {
690 TAILQ_REMOVE(&nmp
->nm_recallq
, np
, n_dlink
);
691 np
->n_dlink
.tqe_next
= NFSNOLIST
;
693 lck_mtx_unlock(&nmp
->nm_lock
);
695 if (np
->n_openflags
& N_DELEG_MASK
) {
696 np
->n_openflags
&= ~N_DELEG_MASK
;
697 nfs4_delegreturn_rpc(nmp
, np
->n_fhp
, np
->n_fhsize
, &np
->n_dstateid
,
698 vfs_context_thread(ctx
), vfs_context_ucred(ctx
));
702 /* clean up file locks */
703 TAILQ_FOREACH_SAFE(nflp
, &np
->n_locks
, nfl_link
, nextnflp
) {
704 if (!(nflp
->nfl_flags
& NFS_FILE_LOCK_DEAD
)) {
706 vname
= vnode_getname(vp
);
707 printf("nfs_vnop_reclaim: lock 0x%llx 0x%llx 0x%x (bc %d) %s\n",
708 nflp
->nfl_start
, nflp
->nfl_end
, nflp
->nfl_flags
,
709 nflp
->nfl_blockcnt
, vname
? vname
: "//");
711 if (!(nflp
->nfl_flags
& NFS_FILE_LOCK_BLOCKED
)) {
712 lck_mtx_lock(&nflp
->nfl_owner
->nlo_lock
);
713 TAILQ_REMOVE(&nflp
->nfl_owner
->nlo_locks
, nflp
, nfl_lolink
);
714 lck_mtx_unlock(&nflp
->nfl_owner
->nlo_lock
);
716 TAILQ_REMOVE(&np
->n_locks
, nflp
, nfl_link
);
717 nfs_file_lock_destroy(nflp
);
719 /* clean up lock owners */
720 TAILQ_FOREACH_SAFE(nlop
, &np
->n_lock_owners
, nlo_link
, nextnlop
) {
721 if (!TAILQ_EMPTY(&nlop
->nlo_locks
)) {
723 vname
= vnode_getname(vp
);
724 printf("nfs_vnop_reclaim: lock owner with locks %s\n",
725 vname
? vname
: "//");
727 TAILQ_REMOVE(&np
->n_lock_owners
, nlop
, nlo_link
);
728 nfs_lock_owner_destroy(nlop
);
730 /* clean up open state */
731 if (np
->n_openrefcnt
) {
733 vname
= vnode_getname(vp
);
734 printf("nfs_vnop_reclaim: still open: %d %s\n",
735 np
->n_openrefcnt
, vname
? vname
: "//");
737 TAILQ_FOREACH_SAFE(nofp
, &np
->n_opens
, nof_link
, nextnofp
) {
738 if (nofp
->nof_flags
& NFS_OPEN_FILE_BUSY
) {
740 vname
= vnode_getname(vp
);
741 printf("nfs_vnop_reclaim: open file busy: %s\n",
742 vname
? vname
: "//");
744 if (nofp
->nof_opencnt
) {
746 vname
= vnode_getname(vp
);
747 printf("nfs_vnop_reclaim: file still open: %d %s\n",
748 nofp
->nof_opencnt
, vname
? vname
: "//");
750 if (nofp
->nof_access
|| nofp
->nof_deny
||
751 nofp
->nof_mmap_access
|| nofp
->nof_mmap_deny
||
752 nofp
->nof_r
|| nofp
->nof_w
|| nofp
->nof_rw
||
753 nofp
->nof_r_dw
|| nofp
->nof_w_dw
|| nofp
->nof_rw_dw
||
754 nofp
->nof_r_drw
|| nofp
->nof_w_drw
|| nofp
->nof_rw_drw
) {
756 vname
= vnode_getname(vp
);
757 printf("nfs_vnop_reclaim: non-zero access: %d %d %d %d # %u %u %u dw %u %u %u drw %u %u %u %s\n",
758 nofp
->nof_access
, nofp
->nof_deny
,
759 nofp
->nof_mmap_access
, nofp
->nof_mmap_deny
,
760 nofp
->nof_r
, nofp
->nof_w
, nofp
->nof_rw
,
761 nofp
->nof_r_dw
, nofp
->nof_w_dw
, nofp
->nof_rw_dw
,
762 nofp
->nof_r_drw
, nofp
->nof_w_drw
, nofp
->nof_rw_drw
,
763 vname
? vname
: "//");
765 TAILQ_REMOVE(&np
->n_opens
, nofp
, nof_link
);
766 nfs_open_file_destroy(nofp
);
768 lck_mtx_unlock(&np
->n_openlock
);
770 lck_mtx_lock(nfs_buf_mutex
);
771 if (!LIST_EMPTY(&np
->n_dirtyblkhd
) || !LIST_EMPTY(&np
->n_cleanblkhd
)) {
773 vname
= vnode_getname(vp
);
774 printf("nfs_reclaim: dropping %s buffers for file %s\n",
775 (!LIST_EMPTY(&np
->n_dirtyblkhd
) ? "dirty" : "clean"),
776 (vname
? vname
: "//"));
778 lck_mtx_unlock(nfs_buf_mutex
);
780 vnode_putname(vname
);
781 nfs_vinvalbuf(vp
, V_IGNORE_WRITEERR
, ap
->a_context
, 0);
783 lck_mtx_lock(nfs_node_hash_mutex
);
785 if ((vnode_vtype(vp
) != VDIR
) && np
->n_sillyrename
) {
786 printf("nfs_reclaim: leaving unlinked file %s\n", np
->n_sillyrename
->nsr_name
);
787 if (np
->n_sillyrename
->nsr_cred
!= NOCRED
)
788 kauth_cred_unref(&np
->n_sillyrename
->nsr_cred
);
789 vnode_rele(NFSTOV(np
->n_sillyrename
->nsr_dnp
));
790 FREE_ZONE(np
->n_sillyrename
, sizeof(*np
->n_sillyrename
), M_NFSREQ
);
793 vnode_removefsref(vp
);
795 if (np
->n_hflag
& NHHASHED
) {
796 LIST_REMOVE(np
, n_hash
);
797 np
->n_hflag
&= ~NHHASHED
;
798 FSDBG(266, 0, np
, np
->n_flag
, 0xb1eb1e);
800 lck_mtx_unlock(nfs_node_hash_mutex
);
803 * Free up any directory cookie structures and large file handle
804 * structures that might be associated with this nfs node.
806 nfs_node_lock_force(np
);
807 if ((vnode_vtype(vp
) == VDIR
) && np
->n_cookiecache
)
808 FREE_ZONE(np
->n_cookiecache
, sizeof(struct nfsdmap
), M_NFSDIROFF
);
809 if (np
->n_fhsize
> NFS_SMALLFH
)
810 FREE_ZONE(np
->n_fhp
, np
->n_fhsize
, M_NFSBIGFH
);
812 vnode_clearfsnode(vp
);
815 if (!vnode_get(np
->n_parent
)) {
816 vnode_rele(np
->n_parent
);
817 vnode_put(np
->n_parent
);
822 lck_mtx_destroy(&np
->n_lock
, nfs_node_lck_grp
);
823 lck_rw_destroy(&np
->n_datalock
, nfs_data_lck_grp
);
824 lck_mtx_destroy(&np
->n_openlock
, nfs_open_grp
);
826 FSDBG_BOT(265, vp
, np
, np
->n_flag
, 0xd1ed1e);
827 FREE_ZONE(np
, sizeof(struct nfsnode
), M_NFSNODE
);
832 * Acquire an NFS node lock
836 nfs_node_lock_internal(nfsnode_t np
, int force
)
838 FSDBG_TOP(268, np
, force
, 0, 0);
839 lck_mtx_lock(&np
->n_lock
);
840 if (!force
&& !(np
->n_hflag
&& NHHASHED
)) {
841 FSDBG_BOT(268, np
, 0xdead, 0, 0);
842 lck_mtx_unlock(&np
->n_lock
);
845 FSDBG_BOT(268, np
, force
, 0, 0);
850 nfs_node_lock(nfsnode_t np
)
852 return nfs_node_lock_internal(np
, 0);
856 nfs_node_lock_force(nfsnode_t np
)
858 nfs_node_lock_internal(np
, 1);
862 * Release an NFS node lock
865 nfs_node_unlock(nfsnode_t np
)
867 FSDBG(269, np
, current_thread(), 0, 0);
868 lck_mtx_unlock(&np
->n_lock
);
872 * Acquire 2 NFS node locks
873 * - locks taken in reverse address order
874 * - both or neither of the locks are taken
875 * - only one lock taken per node (dup nodes are skipped)
878 nfs_node_lock2(nfsnode_t np1
, nfsnode_t np2
)
880 nfsnode_t first
, second
;
883 first
= (np1
> np2
) ? np1
: np2
;
884 second
= (np1
> np2
) ? np2
: np1
;
885 if ((error
= nfs_node_lock(first
)))
889 if ((error
= nfs_node_lock(second
)))
890 nfs_node_unlock(first
);
895 nfs_node_unlock2(nfsnode_t np1
, nfsnode_t np2
)
897 nfs_node_unlock(np1
);
899 nfs_node_unlock(np2
);
903 * Manage NFS node busy state.
904 * (Similar to NFS node locks above)
907 nfs_node_set_busy(nfsnode_t np
, thread_t thd
)
909 struct timespec ts
= { 2, 0 };
912 if ((error
= nfs_node_lock(np
)))
914 while (ISSET(np
->n_flag
, NBUSY
)) {
915 SET(np
->n_flag
, NBUSYWANT
);
916 msleep(np
, &np
->n_lock
, PZERO
-1, "nfsbusywant", &ts
);
917 if ((error
= nfs_sigintr(NFSTONMP(np
), NULL
, thd
, 0)))
921 SET(np
->n_flag
, NBUSY
);
927 nfs_node_clear_busy(nfsnode_t np
)
931 nfs_node_lock_force(np
);
932 wanted
= ISSET(np
->n_flag
, NBUSYWANT
);
933 CLR(np
->n_flag
, NBUSY
|NBUSYWANT
);
940 nfs_node_set_busy2(nfsnode_t np1
, nfsnode_t np2
, thread_t thd
)
942 nfsnode_t first
, second
;
945 first
= (np1
> np2
) ? np1
: np2
;
946 second
= (np1
> np2
) ? np2
: np1
;
947 if ((error
= nfs_node_set_busy(first
, thd
)))
951 if ((error
= nfs_node_set_busy(second
, thd
)))
952 nfs_node_clear_busy(first
);
957 nfs_node_clear_busy2(nfsnode_t np1
, nfsnode_t np2
)
959 nfs_node_clear_busy(np1
);
961 nfs_node_clear_busy(np2
);
964 /* helper function to sort four nodes in reverse address order (no dupes) */
966 nfs_node_sort4(nfsnode_t np1
, nfsnode_t np2
, nfsnode_t np3
, nfsnode_t np4
, nfsnode_t
*list
, int *lcntp
)
968 nfsnode_t na
[2], nb
[2];
971 /* sort pairs then merge */
972 na
[0] = (np1
> np2
) ? np1
: np2
;
973 na
[1] = (np1
> np2
) ? np2
: np1
;
974 nb
[0] = (np3
> np4
) ? np3
: np4
;
975 nb
[1] = (np3
> np4
) ? np4
: np3
;
976 for (a
= b
= i
= lcnt
= 0; i
< 4; i
++) {
978 list
[lcnt
] = nb
[b
++];
979 else if ((b
>= 2) || (na
[a
] >= nb
[b
]))
980 list
[lcnt
] = na
[a
++];
982 list
[lcnt
] = nb
[b
++];
983 if ((lcnt
<= 0) || (list
[lcnt
] != list
[lcnt
-1]))
984 lcnt
++; /* omit dups */
986 if (list
[lcnt
-1] == NULL
)
992 nfs_node_set_busy4(nfsnode_t np1
, nfsnode_t np2
, nfsnode_t np3
, nfsnode_t np4
, thread_t thd
)
997 nfs_node_sort4(np1
, np2
, np3
, np4
, list
, &lcnt
);
999 /* Now we can lock using list[0 - lcnt-1] */
1000 for (i
= 0; i
< lcnt
; ++i
)
1001 if ((error
= nfs_node_set_busy(list
[i
], thd
))) {
1002 /* Drop any locks we acquired. */
1004 nfs_node_clear_busy(list
[i
]);
1011 nfs_node_clear_busy4(nfsnode_t np1
, nfsnode_t np2
, nfsnode_t np3
, nfsnode_t np4
)
1016 nfs_node_sort4(np1
, np2
, np3
, np4
, list
, &lcnt
);
1018 nfs_node_clear_busy(list
[lcnt
]);
1022 * Acquire an NFS node data lock
1025 nfs_data_lock(nfsnode_t np
, int locktype
)
1027 nfs_data_lock_internal(np
, locktype
, 1);
1030 nfs_data_lock_noupdate(nfsnode_t np
, int locktype
)
1032 nfs_data_lock_internal(np
, locktype
, 0);
1035 nfs_data_lock_internal(nfsnode_t np
, int locktype
, int updatesize
)
1037 FSDBG_TOP(270, np
, locktype
, np
->n_datalockowner
, 0);
1038 if (locktype
== NFS_DATA_LOCK_SHARED
) {
1039 if (updatesize
&& ISSET(np
->n_flag
, NUPDATESIZE
))
1040 nfs_data_update_size(np
, 0);
1041 lck_rw_lock_shared(&np
->n_datalock
);
1043 lck_rw_lock_exclusive(&np
->n_datalock
);
1044 np
->n_datalockowner
= current_thread();
1045 if (updatesize
&& ISSET(np
->n_flag
, NUPDATESIZE
))
1046 nfs_data_update_size(np
, 1);
1048 FSDBG_BOT(270, np
, locktype
, np
->n_datalockowner
, 0);
1052 * Release an NFS node data lock
1055 nfs_data_unlock(nfsnode_t np
)
1057 nfs_data_unlock_internal(np
, 1);
1060 nfs_data_unlock_noupdate(nfsnode_t np
)
1062 nfs_data_unlock_internal(np
, 0);
1065 nfs_data_unlock_internal(nfsnode_t np
, int updatesize
)
1067 int mine
= (np
->n_datalockowner
== current_thread());
1068 FSDBG_TOP(271, np
, np
->n_datalockowner
, current_thread(), 0);
1069 if (updatesize
&& mine
&& ISSET(np
->n_flag
, NUPDATESIZE
))
1070 nfs_data_update_size(np
, 1);
1071 np
->n_datalockowner
= NULL
;
1072 lck_rw_done(&np
->n_datalock
);
1073 if (updatesize
&& !mine
&& ISSET(np
->n_flag
, NUPDATESIZE
))
1074 nfs_data_update_size(np
, 0);
1075 FSDBG_BOT(271, np
, np
->n_datalockowner
, current_thread(), 0);
1080 * update an NFS node's size
1083 nfs_data_update_size(nfsnode_t np
, int datalocked
)
1087 FSDBG_TOP(272, np
, np
->n_flag
, np
->n_size
, np
->n_newsize
);
1089 nfs_data_lock(np
, NFS_DATA_LOCK_EXCLUSIVE
);
1090 /* grabbing data lock will automatically update size */
1091 nfs_data_unlock(np
);
1092 FSDBG_BOT(272, np
, np
->n_flag
, np
->n_size
, np
->n_newsize
);
1095 error
= nfs_node_lock(np
);
1096 if (error
|| !ISSET(np
->n_flag
, NUPDATESIZE
)) {
1098 nfs_node_unlock(np
);
1099 FSDBG_BOT(272, np
, np
->n_flag
, np
->n_size
, np
->n_newsize
);
1102 CLR(np
->n_flag
, NUPDATESIZE
);
1103 np
->n_size
= np
->n_newsize
;
1104 /* make sure we invalidate buffers the next chance we get */
1105 SET(np
->n_flag
, NNEEDINVALIDATE
);
1106 nfs_node_unlock(np
);
1107 ubc_setsize(NFSTOV(np
), (off_t
)np
->n_size
); /* XXX error? */
1108 FSDBG_BOT(272, np
, np
->n_flag
, np
->n_size
, np
->n_newsize
);