1 /* $NetBSD: nfs_export.c,v 1.47 2009/05/23 18:19:19 ad Exp $ */
4 * Copyright (c) 1997, 1998, 2004, 2005, 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Charles M. Hannum.
12 * This code is derived from software contributed to The NetBSD Foundation
13 * by Julio M. Merino Vidal.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
38 * Copyright (c) 1989, 1993
39 * The Regents of the University of California. All rights reserved.
40 * (c) UNIX System Laboratories, Inc.
41 * All or some portions of this file are derived from material licensed
42 * to the University of California by American Telephone and Telegraph
43 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
44 * the permission of UNIX System Laboratories, Inc.
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
49 * 1. Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 * notice, this list of conditions and the following disclaimer in the
53 * documentation and/or other materials provided with the distribution.
54 * 3. Neither the name of the University nor the names of its contributors
55 * may be used to endorse or promote products derived from this software
56 * without specific prior written permission.
58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
70 * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94
74 * VFS exports list management.
76 * Lock order: vfs_busy -> mnt_updating -> netexport_lock.
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: nfs_export.c,v 1.47 2009/05/23 18:19:19 ad Exp $");
82 #include <sys/param.h>
83 #include <sys/systm.h>
84 #include <sys/kernel.h>
85 #include <sys/queue.h>
87 #include <sys/mount.h>
88 #include <sys/vnode.h>
89 #include <sys/namei.h>
90 #include <sys/errno.h>
91 #include <sys/malloc.h>
92 #include <sys/domain.h>
94 #include <sys/dirent.h>
95 #include <sys/socket.h> /* XXX for AF_MAX */
96 #include <sys/kauth.h>
98 #include <net/radix.h>
100 #include <netinet/in.h>
102 #include <nfs/rpcv2.h>
103 #include <nfs/nfsproto.h>
105 #include <nfs/nfs_var.h>
108 * Network address lookup element.
111 struct radix_node netc_rnodes
[2];
114 kauth_cred_t netc_anon
;
118 * Network export information.
121 CIRCLEQ_ENTRY(netexport
) ne_list
;
122 struct mount
*ne_mount
;
123 struct netcred ne_defexported
; /* Default export */
124 struct radix_node_head
*ne_rtable
[AF_MAX
+1]; /* Individual exports */
126 CIRCLEQ_HEAD(, netexport
) netexport_list
=
127 CIRCLEQ_HEAD_INITIALIZER(netexport_list
);
129 /* Publicly exported file system. */
130 struct nfs_public nfs_pub
;
135 static int init_exports(struct mount
*, struct netexport
**);
136 static int hang_addrlist(struct mount
*, struct netexport
*,
137 const struct export_args
*);
138 static int sacheck(struct sockaddr
*);
139 static int free_netcred(struct radix_node
*, void *);
140 static int export(struct netexport
*, const struct export_args
*);
141 static int setpublicfs(struct mount
*, struct netexport
*,
142 const struct export_args
*);
143 static struct netcred
*netcred_lookup(struct netexport
*, struct mbuf
*);
144 static struct netexport
*netexport_lookup(const struct mount
*);
145 static struct netexport
*netexport_lookup_byfsid(const fsid_t
*);
146 static void netexport_clear(struct netexport
*);
147 static void netexport_insert(struct netexport
*);
148 static void netexport_remove(struct netexport
*);
149 static void netexport_wrlock(void);
150 static void netexport_wrunlock(void);
151 static int nfs_export_update_30(struct mount
*mp
, const char *path
, void *);
153 static krwlock_t netexport_lock
;
160 * Declare and initialize the file system export hooks.
162 static void netexport_unmount(struct mount
*);
164 struct vfs_hooks nfs_export_hooks
= {
166 .vh_unmount
= netexport_unmount
,
167 .vh_reexport
= nfs_export_update_30
,
171 * VFS unmount hook for NFS exports.
173 * Releases NFS exports list resources if the given mount point has some.
174 * As allocation happens lazily, it may be that it doesn't has this
175 * information, although it theorically should.
178 netexport_unmount(struct mount
*mp
)
180 struct netexport
*ne
;
185 ne
= netexport_lookup(mp
);
187 netexport_wrunlock();
191 netexport_remove(ne
);
192 netexport_wrunlock();
193 kmem_free(ne
, sizeof(*ne
));
200 rw_init(&netexport_lock
);
206 struct netexport
*ne
;
210 while (!CIRCLEQ_EMPTY(&netexport_list
)) {
212 ne
= CIRCLEQ_FIRST(&netexport_list
);
214 error
= vfs_busy(mp
, NULL
);
215 netexport_wrunlock();
217 kpause("nfsfini", false, hz
, NULL
);
220 mutex_enter(&mp
->mnt_updating
); /* mnt_flag */
221 netexport_unmount(mp
);
222 mutex_exit(&mp
->mnt_updating
); /* mnt_flag */
223 vfs_unbusy(mp
, false, NULL
);
225 rw_destroy(&netexport_lock
);
230 * Atomically set the NFS exports list of the given file system, replacing
231 * it with a new list of entries.
233 * Returns zero on success or an appropriate error code otherwise.
235 * Helper function for the nfssvc(2) system call (NFSSVC_SETEXPORTSLIST
239 mountd_set_exports_list(const struct mountd_exports_list
*mel
, struct lwp
*l
,
244 /* XXX: See below to see the reason why this is disabled. */
248 struct netexport
*ne
;
253 if (kauth_authorize_network(l
->l_cred
, KAUTH_NETWORK_NFS
,
254 KAUTH_REQ_NETWORK_NFS_EXPORT
, NULL
, NULL
, NULL
) != 0)
257 /* Lookup the file system path. */
258 NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
, mel
->mel_path
);
264 KASSERT(nmp
== NULL
|| nmp
== mp
);
267 * Make sure the file system can do vptofh. If the file system
268 * knows the handle's size, just trust it's able to do the
269 * actual translation also (otherwise we should check fhtovp
270 * also, and that's getting a wee bit ridiculous).
273 if ((error
= VFS_VPTOFH(vp
, NULL
, &fid_size
)) != E2BIG
) {
278 /* Mark the file system busy. */
279 error
= vfs_busy(mp
, NULL
);
284 mutex_enter(&mp
->mnt_updating
); /* mnt_flag */
286 ne
= netexport_lookup(mp
);
288 error
= init_exports(mp
, &ne
);
295 KASSERT(ne
->ne_mount
== mp
);
298 * XXX: The part marked as 'notyet' works fine from the kernel's
299 * point of view, in the sense that it is able to atomically update
300 * the complete exports list for a file system. However, supporting
301 * this in mountd(8) requires a lot of work; so, for now, keep the
302 * old behavior of updating a single entry per call.
304 * When mountd(8) is fixed, just remove the second branch of this
305 * preprocessor conditional and enable the first one.
309 for (i
= 0; error
== 0 && i
< mel
->mel_nexports
; i
++)
310 error
= export(ne
, &mel
->mel_exports
[i
]);
312 if (mel
->mel_nexports
== 0)
314 else if (mel
->mel_nexports
== 1)
315 error
= export(ne
, &mel
->mel_exports
[0]);
317 printf("%s: Cannot set more than one "
318 "entry at once (unimplemented)\n", __func__
);
324 netexport_wrunlock();
326 mutex_exit(&mp
->mnt_updating
); /* mnt_flag */
327 vfs_unbusy(mp
, false, NULL
);
332 netexport_insert(struct netexport
*ne
)
335 CIRCLEQ_INSERT_HEAD(&netexport_list
, ne
, ne_list
);
339 netexport_remove(struct netexport
*ne
)
342 CIRCLEQ_REMOVE(&netexport_list
, ne
, ne_list
);
345 static struct netexport
*
346 netexport_lookup(const struct mount
*mp
)
348 struct netexport
*ne
;
350 CIRCLEQ_FOREACH(ne
, &netexport_list
, ne_list
) {
351 if (ne
->ne_mount
== mp
) {
360 static struct netexport
*
361 netexport_lookup_byfsid(const fsid_t
*fsid
)
363 struct netexport
*ne
;
365 CIRCLEQ_FOREACH(ne
, &netexport_list
, ne_list
) {
366 const struct mount
*mp
= ne
->ne_mount
;
368 if (mp
->mnt_stat
.f_fsidx
.__fsid_val
[0] == fsid
->__fsid_val
[0] &&
369 mp
->mnt_stat
.f_fsidx
.__fsid_val
[1] == fsid
->__fsid_val
[1]) {
380 * Check if the file system specified by the 'mp' mount structure is
381 * exported to a client with 'anon' anonymous credentials. The 'mb'
382 * argument is an mbuf containing the network address of the client.
383 * The return parameters for the export flags for the client are returned
384 * in the address specified by 'wh'.
386 * This function is used exclusively by the NFS server. It is generally
387 * invoked before VFS_FHTOVP to validate that client has access to the
392 netexport_check(const fsid_t
*fsid
, struct mbuf
*mb
, struct mount
**mpp
,
393 int *wh
, kauth_cred_t
*anon
)
395 struct netexport
*ne
;
398 ne
= netexport_lookup_byfsid(fsid
);
402 np
= netcred_lookup(ne
, mb
);
408 *wh
= np
->netc_exflags
;
409 *anon
= np
->netc_anon
;
415 * Handles legacy export requests. In this case, the export information
416 * is hardcoded in a specific place of the mount arguments structure (given
417 * in data); the request for an update is given through the fspec field
418 * (also in a known location), which must be a null pointer.
420 * Returns EJUSTRETURN if the given command was not a export request.
421 * Otherwise, returns 0 on success or an appropriate error code otherwise.
424 nfs_export_update_30(struct mount
*mp
, const char *path
, void *data
)
426 struct mountd_exports_list mel
;
427 struct mnt_export_args30
*args
;
432 if (args
->fspec
!= NULL
)
435 if (args
->eargs
.ex_flags
& 0x00020000) {
436 /* Request to delete exports. The mask above holds the
437 * value that used to be in MNT_DELEXPORT. */
438 mel
.mel_nexports
= 0;
440 /* The following assumes export_args has not changed since
441 * export_args30 - typedef checks sizes. */
442 typedef char x
[sizeof args
->eargs
== sizeof *mel
.mel_exports
? 1 : -1];
444 mel
.mel_nexports
= 1;
445 mel
.mel_exports
= (void *)&args
->eargs
;
448 return mountd_set_exports_list(&mel
, curlwp
, mp
);
456 * Initializes NFS exports for the mountpoint given in 'mp'.
457 * If successful, returns 0 and sets *nep to the address of the new
458 * netexport item; otherwise returns an appropriate error code
459 * and *nep remains unmodified.
462 init_exports(struct mount
*mp
, struct netexport
**nep
)
465 struct export_args ea
;
466 struct netexport
*ne
;
470 /* Ensure that we do not already have this mount point. */
471 KASSERT(netexport_lookup(mp
) == NULL
);
473 ne
= kmem_zalloc(sizeof(*ne
), KM_SLEEP
);
476 /* Set the default export entry. Handled internally by export upon
478 memset(&ea
, 0, sizeof(ea
));
480 if (mp
->mnt_flag
& MNT_RDONLY
)
481 ea
.ex_flags
|= MNT_EXRDONLY
;
482 error
= export(ne
, &ea
);
484 kmem_free(ne
, sizeof(*ne
));
486 netexport_insert(ne
);
494 * Build hash lists of net addresses and hang them off the mount point.
495 * Called by export() to set up a new entry in the lists of export
499 hang_addrlist(struct mount
*mp
, struct netexport
*nep
,
500 const struct export_args
*argp
)
503 struct netcred
*np
, *enp
;
504 struct radix_node_head
*rnh
;
505 struct sockaddr
*saddr
, *smask
;
510 if (argp
->ex_addrlen
== 0) {
511 if (mp
->mnt_flag
& MNT_DEFEXPORTED
)
513 np
= &nep
->ne_defexported
;
514 KASSERT(np
->netc_anon
== NULL
);
515 np
->netc_anon
= kauth_cred_alloc();
516 np
->netc_exflags
= argp
->ex_flags
;
517 kauth_uucred_to_cred(np
->netc_anon
, &argp
->ex_anon
);
518 mp
->mnt_flag
|= MNT_DEFEXPORTED
;
522 if (argp
->ex_addrlen
> MLEN
|| argp
->ex_masklen
> MLEN
)
525 i
= sizeof(struct netcred
) + argp
->ex_addrlen
+ argp
->ex_masklen
;
526 np
= malloc(i
, M_NETADDR
, M_WAITOK
| M_ZERO
);
527 np
->netc_anon
= kauth_cred_alloc();
528 saddr
= (struct sockaddr
*)(np
+ 1);
529 error
= copyin(argp
->ex_addr
, saddr
, argp
->ex_addrlen
);
532 if (saddr
->sa_len
> argp
->ex_addrlen
)
533 saddr
->sa_len
= argp
->ex_addrlen
;
534 if (sacheck(saddr
) == -1)
536 if (argp
->ex_masklen
) {
537 smask
= (struct sockaddr
*)((char *)saddr
+ argp
->ex_addrlen
);
538 error
= copyin(argp
->ex_mask
, smask
, argp
->ex_masklen
);
541 if (smask
->sa_len
> argp
->ex_masklen
)
542 smask
->sa_len
= argp
->ex_masklen
;
543 if (smask
->sa_family
!= saddr
->sa_family
)
545 if (sacheck(smask
) == -1)
548 i
= saddr
->sa_family
;
549 if ((rnh
= nep
->ne_rtable
[i
]) == 0) {
551 * Seems silly to initialize every AF when most are not
552 * used, do so on demand here
554 DOMAIN_FOREACH(dom
) {
555 if (dom
->dom_family
== i
&& dom
->dom_rtattach
) {
556 dom
->dom_rtattach((void **)&nep
->ne_rtable
[i
],
561 if ((rnh
= nep
->ne_rtable
[i
]) == 0) {
567 enp
= (struct netcred
*)(*rnh
->rnh_addaddr
)(saddr
, smask
, rnh
,
571 enp
= (struct netcred
*)(*rnh
->rnh_lookup
)(saddr
,
582 enp
->netc_refcnt
= 1;
584 np
->netc_exflags
= argp
->ex_flags
;
585 kauth_uucred_to_cred(np
->netc_anon
, &argp
->ex_anon
);
588 if (enp
->netc_exflags
!= argp
->ex_flags
||
589 kauth_cred_uucmp(enp
->netc_anon
, &argp
->ex_anon
) != 0)
594 KASSERT(np
->netc_anon
!= NULL
);
595 kauth_cred_free(np
->netc_anon
);
601 * Ensure that the address stored in 'sa' is valid.
602 * Returns zero on success, otherwise -1.
605 sacheck(struct sockaddr
*sa
)
608 switch (sa
->sa_family
) {
610 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
611 char *p
= (char *)sin
->sin_zero
;
614 if (sin
->sin_len
!= sizeof(*sin
))
616 if (sin
->sin_port
!= 0)
618 for (i
= 0; i
< sizeof(sin
->sin_zero
); i
++)
624 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
626 if (sin6
->sin6_len
!= sizeof(*sin6
))
628 if (sin6
->sin6_port
!= 0)
638 * Free the netcred object pointed to by the 'rn' radix node.
639 * 'w' holds a pointer to the radix tree head.
642 free_netcred(struct radix_node
*rn
, void *w
)
644 struct radix_node_head
*rnh
= (struct radix_node_head
*)w
;
645 struct netcred
*np
= (struct netcred
*)(void *)rn
;
647 (*rnh
->rnh_deladdr
)(rn
->rn_key
, rn
->rn_mask
, rnh
);
648 if (--(np
->netc_refcnt
) <= 0) {
649 KASSERT(np
->netc_anon
!= NULL
);
650 kauth_cred_free(np
->netc_anon
);
657 * Clears the exports list for a given file system.
660 netexport_clear(struct netexport
*ne
)
662 struct radix_node_head
*rnh
;
663 struct mount
*mp
= ne
->ne_mount
;
666 if (mp
->mnt_flag
& MNT_EXPUBLIC
) {
667 setpublicfs(NULL
, NULL
, NULL
);
668 mp
->mnt_flag
&= ~MNT_EXPUBLIC
;
671 for (i
= 0; i
<= AF_MAX
; i
++) {
672 if ((rnh
= ne
->ne_rtable
[i
]) != NULL
) {
673 rn_walktree(rnh
, free_netcred
, rnh
);
675 ne
->ne_rtable
[i
] = NULL
;
679 if ((mp
->mnt_flag
& MNT_DEFEXPORTED
) != 0) {
680 struct netcred
*np
= &ne
->ne_defexported
;
682 KASSERT(np
->netc_anon
!= NULL
);
683 kauth_cred_free(np
->netc_anon
);
684 np
->netc_anon
= NULL
;
686 KASSERT(ne
->ne_defexported
.netc_anon
== NULL
);
689 mp
->mnt_flag
&= ~(MNT_EXPORTED
| MNT_DEFEXPORTED
);
693 * Add a new export entry (described by an export_args structure) to the
697 export(struct netexport
*nep
, const struct export_args
*argp
)
699 struct mount
*mp
= nep
->ne_mount
;
702 if (argp
->ex_flags
& MNT_EXPORTED
) {
703 if (argp
->ex_flags
& MNT_EXPUBLIC
) {
704 if ((error
= setpublicfs(mp
, nep
, argp
)) != 0)
706 mp
->mnt_flag
|= MNT_EXPUBLIC
;
708 if ((error
= hang_addrlist(mp
, nep
, argp
)) != 0)
710 mp
->mnt_flag
|= MNT_EXPORTED
;
716 * Set the publicly exported filesystem (WebNFS). Currently, only
717 * one public filesystem is possible in the spec (RFC 2054 and 2055)
720 setpublicfs(struct mount
*mp
, struct netexport
*nep
,
721 const struct export_args
*argp
)
729 * mp == NULL -> invalidate the current info, the FS is
730 * no longer exported. May be called from either export
731 * or unmount, so check if it hasn't already been done.
734 if (nfs_pub
.np_valid
) {
735 nfs_pub
.np_valid
= 0;
736 if (nfs_pub
.np_handle
!= NULL
) {
737 free(nfs_pub
.np_handle
, M_TEMP
);
738 nfs_pub
.np_handle
= NULL
;
740 if (nfs_pub
.np_index
!= NULL
) {
741 free(nfs_pub
.np_index
, M_TEMP
);
742 nfs_pub
.np_index
= NULL
;
749 * Only one allowed at a time.
751 if (nfs_pub
.np_valid
!= 0 && mp
!= nfs_pub
.np_mount
)
755 * Get real filehandle for root of exported FS.
757 if ((error
= VFS_ROOT(mp
, &rvp
)))
761 error
= vfs_composefh(rvp
, NULL
, &fhsize
);
764 nfs_pub
.np_handle
= malloc(fhsize
, M_TEMP
, M_NOWAIT
);
765 if (nfs_pub
.np_handle
== NULL
)
768 error
= vfs_composefh(rvp
, nfs_pub
.np_handle
, &fhsize
);
775 * If an indexfile was specified, pull it in.
777 if (argp
->ex_indexfile
!= NULL
) {
778 nfs_pub
.np_index
= malloc(MAXNAMLEN
+ 1, M_TEMP
, M_WAITOK
);
779 error
= copyinstr(argp
->ex_indexfile
, nfs_pub
.np_index
,
780 MAXNAMLEN
, (size_t *)0);
783 * Check for illegal filenames.
785 for (cp
= nfs_pub
.np_index
; *cp
; cp
++) {
793 free(nfs_pub
.np_index
, M_TEMP
);
798 nfs_pub
.np_mount
= mp
;
799 nfs_pub
.np_valid
= 1;
804 * Lookup an export entry in the exports list that matches the address
805 * stored in 'nam'. If no entry is found, the default one is used instead
808 static struct netcred
*
809 netcred_lookup(struct netexport
*ne
, struct mbuf
*nam
)
812 struct radix_node_head
*rnh
;
813 struct sockaddr
*saddr
;
815 if ((ne
->ne_mount
->mnt_flag
& MNT_EXPORTED
) == 0) {
820 * Lookup in the export list first.
824 saddr
= mtod(nam
, struct sockaddr
*);
825 rnh
= ne
->ne_rtable
[saddr
->sa_family
];
827 np
= (struct netcred
*)
828 (*rnh
->rnh_matchaddr
)((void *)saddr
,
830 if (np
&& np
->netc_rnodes
->rn_flags
& RNF_ROOT
)
835 * If no address match, use the default if it exists.
837 if (np
== NULL
&& ne
->ne_mount
->mnt_flag
& MNT_DEFEXPORTED
)
838 np
= &ne
->ne_defexported
;
844 netexport_rdlock(void)
847 rw_enter(&netexport_lock
, RW_READER
);
851 netexport_rdunlock(void)
854 rw_exit(&netexport_lock
);
858 netexport_wrlock(void)
861 rw_enter(&netexport_lock
, RW_WRITER
);
865 netexport_wrunlock(void)
868 rw_exit(&netexport_lock
);