xref: /netbsd-src/sys/nfs/nfs_export.c (revision e5548b402ae4c44fb816de42c7bba9581ce23ef5)
1 /*	$NetBSD: nfs_export.c,v 1.7 2005/12/15 21:46:31 yamt Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997, 1998, 2004, 2005 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
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.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
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.
23  * 3. All advertising materials mentioning features or use of this software
24  *    must display the following acknowledgement:
25  *	This product includes software developed by the NetBSD
26  *	Foundation, Inc. and its contributors.
27  * 4. Neither the name of The NetBSD Foundation nor the names of its
28  *    contributors may be used to endorse or promote products derived
29  *    from this software without specific prior written permission.
30  *
31  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
32  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
33  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
35  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
39  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41  * POSSIBILITY OF SUCH DAMAGE.
42  */
43 
44 /*
45  * Copyright (c) 1989, 1993
46  *	The Regents of the University of California.  All rights reserved.
47  * (c) UNIX System Laboratories, Inc.
48  * All or some portions of this file are derived from material licensed
49  * to the University of California by American Telephone and Telegraph
50  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
51  * the permission of UNIX System Laboratories, Inc.
52  *
53  * Redistribution and use in source and binary forms, with or without
54  * modification, are permitted provided that the following conditions
55  * are met:
56  * 1. Redistributions of source code must retain the above copyright
57  *    notice, this list of conditions and the following disclaimer.
58  * 2. Redistributions in binary form must reproduce the above copyright
59  *    notice, this list of conditions and the following disclaimer in the
60  *    documentation and/or other materials provided with the distribution.
61  * 3. Neither the name of the University nor the names of its contributors
62  *    may be used to endorse or promote products derived from this software
63  *    without specific prior written permission.
64  *
65  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
66  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
69  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
70  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
71  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
72  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
73  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
74  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
75  * SUCH DAMAGE.
76  *
77  *	@(#)vfs_subr.c	8.13 (Berkeley) 4/18/94
78  */
79 
80 /*
81  * VFS exports list management.
82  */
83 
84 #include <sys/cdefs.h>
85 __KERNEL_RCSID(0, "$NetBSD: nfs_export.c,v 1.7 2005/12/15 21:46:31 yamt Exp $");
86 
87 #include "opt_compat_netbsd.h"
88 #include "opt_inet.h"
89 
90 #include <sys/param.h>
91 #include <sys/systm.h>
92 #include <sys/queue.h>
93 #include <sys/proc.h>
94 #include <sys/mount.h>
95 #include <sys/vnode.h>
96 #include <sys/namei.h>
97 #include <sys/errno.h>
98 #include <sys/malloc.h>
99 #include <sys/domain.h>
100 #include <sys/mbuf.h>
101 #include <sys/dirent.h>
102 #include <sys/socket.h>		/* XXX for AF_MAX */
103 
104 #include <net/radix.h>
105 
106 #include <netinet/in.h>
107 
108 #include <nfs/rpcv2.h>
109 #include <nfs/nfsproto.h>
110 #include <nfs/nfs.h>
111 #include <nfs/nfs_var.h>
112 
113 /*
114  * Network address lookup element.
115  */
116 struct netcred {
117 	struct	radix_node netc_rnodes[2];
118 	int	netc_refcnt;
119 	int	netc_exflags;
120 	struct	ucred netc_anon;
121 };
122 
123 /*
124  * Network export information.
125  */
126 struct netexport {
127 	CIRCLEQ_ENTRY(netexport) ne_list;
128 	struct mount *ne_mount;
129 	struct netcred ne_defexported;		      /* Default export */
130 	struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
131 };
132 CIRCLEQ_HEAD(, netexport) netexport_list =
133     CIRCLEQ_HEAD_INITIALIZER(netexport_list);
134 
135 /* Malloc type used by the mount<->netexport map. */
136 MALLOC_DEFINE(M_NFS_EXPORT, "nfs_export", "NFS export data");
137 
138 /* Publicly exported file system. */
139 struct nfs_public nfs_pub;
140 
141 /*
142  * Local prototypes.
143  */
144 static int init_exports(struct mount *, struct netexport **);
145 static int hang_addrlist(struct mount *, struct netexport *,
146     const struct export_args *);
147 static int sacheck(struct sockaddr *);
148 static int free_netcred(struct radix_node *, void *);
149 static void clear_exports(struct netexport *);
150 static int export(struct netexport *, const struct export_args *);
151 static int setpublicfs(struct mount *, struct netexport *,
152     const struct export_args *);
153 static struct netcred *netcred_lookup(struct netexport *, struct mbuf *);
154 static struct netexport *netexport_lookup(const struct mount *);
155 static struct netexport *netexport_lookup_byfsid(const fsid_t *);
156 static void netexport_insert(struct netexport *);
157 static void netexport_remove(struct netexport *);
158 
159 /*
160  * PUBLIC INTERFACE
161  */
162 
163 /*
164  * Declare and initialize the file system export hooks.
165  */
166 static void nfs_export_unmount(struct mount *);
167 
168 struct vfs_hooks nfs_export_hooks = {
169 	nfs_export_unmount
170 };
171 VFS_HOOKS_ATTACH(nfs_export_hooks);
172 
173 /*
174  * VFS unmount hook for NFS exports.
175  *
176  * Releases NFS exports list resources if the given mount point has some.
177  * As allocation happens lazily, it may be that it doesn't has this
178  * information, although it theorically should.
179  */
180 static void
181 nfs_export_unmount(struct mount *mp)
182 {
183 	struct netexport *ne;
184 
185 	KASSERT(mp != NULL);
186 
187 	ne = netexport_lookup(mp);
188 	if (ne == NULL) {
189 		return;
190 	}
191 
192 	KASSERT(mp->mnt_op->vfs_vptofh != NULL &&
193 	    mp->mnt_op->vfs_fhtovp != NULL);
194 
195 	if (mp->mnt_flag & MNT_EXPUBLIC) {
196 		setpublicfs(NULL, NULL, NULL);
197 	}
198 
199 	netexport_remove(ne);
200 	free(ne, M_NFS_EXPORT);
201 }
202 
203 /*
204  * Atomically set the NFS exports list of the given file system, replacing
205  * it with a new list of entries.
206  *
207  * Returns zero on success or an appropriate error code otherwise.
208  *
209  * Helper function for the nfssvc(2) system call (NFSSVC_SETEXPORTSLIST
210  * command).
211  */
212 int
213 mountd_set_exports_list(const struct mountd_exports_list *mel, struct lwp *l)
214 {
215 	int error;
216 #ifdef notyet
217 	/* XXX: See below to see the reason why this is disabled. */
218 	size_t i;
219 #endif
220 	struct mount *mp;
221 	struct netexport *ne;
222 	struct nameidata nd;
223 	struct vnode *vp;
224 
225 	if (suser(l->l_proc->p_ucred, &l->l_proc->p_acflag) != 0)
226 		return EPERM;
227 
228 	/* Lookup the file system path. */
229 	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, mel->mel_path, l);
230 	error = namei(&nd);
231 	if (error != 0)
232 		return error;
233 	vp = nd.ni_vp;
234 	mp = vp->v_mount;
235 
236 	/* The selected file system may not support NFS exports, so ensure
237 	 * it does. */
238 	if (mp->mnt_op->vfs_vptofh == NULL && mp->mnt_op->vfs_fhtovp == NULL) {
239 		error = EOPNOTSUPP;
240 		goto out_locked;
241 	}
242 	KASSERT(mp->mnt_op->vfs_vptofh != NULL &&
243 	    mp->mnt_op->vfs_fhtovp != NULL);
244 
245 	/* Mark the file system busy. */
246 	error = vfs_busy(mp, LK_NOWAIT, NULL);
247 	if (error != 0)
248 		goto out_locked;
249 
250 	ne = netexport_lookup(mp);
251 	if (ne == NULL) {
252 		error = init_exports(mp, &ne);
253 		if (error != 0) {
254 			vfs_unbusy(mp);
255 			goto out_locked;
256 		}
257 	}
258 
259 	KASSERT(ne != NULL);
260 	KASSERT(ne->ne_mount == mp);
261 
262 	/*
263 	 * XXX: The part marked as 'notyet' works fine from the kernel's
264 	 * point of view, in the sense that it is able to atomically update
265 	 * the complete exports list for a file system.  However, supporting
266 	 * this in mountd(8) requires a lot of work; so, for now, keep the
267 	 * old behavior of updating a single entry per call.
268 	 *
269 	 * When mountd(8) is fixed, just remove the second branch of this
270 	 * preprocessor conditional and enable the first one.
271 	 */
272 #ifdef notyet
273 	clear_exports(ne);
274 	for (i = 0; error == 0 && i < mel->mel_nexports; i++)
275 		error = export(ne, &mel->mel_exports[i]);
276 #else
277 	if (mel->mel_nexports == 0)
278 		clear_exports(ne);
279 	else if (mel->mel_nexports == 1)
280 		error = export(ne, &mel->mel_exports[0]);
281 	else {
282 		printf("mountd_set_exports_list: Cannot set more than one "
283 		    "entry at once (unimplemented)\n");
284 		error = EOPNOTSUPP;
285 	}
286 #endif
287 
288 	vfs_unbusy(mp);
289 
290 out_locked:
291 	vput(vp);
292 
293 	return error;
294 }
295 
296 static void
297 netexport_insert(struct netexport *ne)
298 {
299 
300 	CIRCLEQ_INSERT_HEAD(&netexport_list, ne, ne_list);
301 }
302 
303 static void
304 netexport_remove(struct netexport *ne)
305 {
306 
307 	CIRCLEQ_REMOVE(&netexport_list, ne, ne_list);
308 }
309 
310 static struct netexport *
311 netexport_lookup(const struct mount *mp)
312 {
313 	struct netexport *ne;
314 
315 	CIRCLEQ_FOREACH(ne, &netexport_list, ne_list) {
316 		if (ne->ne_mount == mp) {
317 			goto done;
318 		}
319 	}
320 	ne = NULL;
321 done:
322 	return ne;
323 }
324 
325 static struct netexport *
326 netexport_lookup_byfsid(const fsid_t *fsid)
327 {
328 	struct netexport *ne;
329 
330 	CIRCLEQ_FOREACH(ne, &netexport_list, ne_list) {
331 		const struct mount *mp = ne->ne_mount;
332 
333 		if (mp->mnt_stat.f_fsidx.__fsid_val[0] == fsid->__fsid_val[0] &&
334 		    mp->mnt_stat.f_fsidx.__fsid_val[1] == fsid->__fsid_val[1]) {
335 			goto done;
336 		}
337 	}
338 	ne = NULL;
339 done:
340 
341 	return ne;
342 }
343 
344 /*
345  * Check if the file system specified by the 'mp' mount structure is
346  * exported to a client with 'anon' anonymous credentials.  The 'mb'
347  * argument is an mbuf containing the network address of the client.
348  * The return parameters for the export flags for the client are returned
349  * in the address specified by 'wh'.
350  *
351  * This function is used exclusively by the NFS server.  It is generally
352  * invoked before VFS_FHTOVP to validate that client has access to the
353  * file system.
354  */
355 
356 int
357 netexport_check(const fsid_t *fsid, struct mbuf *mb, struct mount **mpp,
358     int *wh, struct ucred **anon)
359 {
360 	struct netexport *ne;
361 	struct netcred *np;
362 
363 	ne = netexport_lookup_byfsid(fsid);
364 	if (ne == NULL) {
365 		return EACCES;
366 	}
367 	np = netcred_lookup(ne, mb);
368 	if (np == NULL) {
369 		return EACCES;
370 	}
371 
372 	*mpp = ne->ne_mount;
373 	*wh = np->netc_exflags;
374 	*anon = &np->netc_anon;
375 
376 	return 0;
377 }
378 
379 #ifdef COMPAT_30
380 /*
381  * Handles legacy export requests.  In this case, the export information
382  * is hardcoded in a specific place of the mount arguments structure (given
383  * in data); the request for an update is given through the fspec field
384  * (also in a known location), which must be a null pointer.
385  *
386  * Returns EJUSTRETURN if the given command was not a export request.
387  * Otherwise, returns 0 on success or an appropriate error code otherwise.
388  */
389 int
390 nfs_update_exports_30(struct mount *mp, const char *path, void *data,
391     struct lwp *l)
392 {
393 	int error;
394 	struct {
395 		const char *fspec;
396 		struct export_args30 eargs;
397 	} args;
398 	struct mountd_exports_list mel;
399 
400 	mel.mel_path = path;
401 
402 	error = copyin(data, &args, sizeof(args));
403 	if (error != 0)
404 		return EJUSTRETURN;
405 
406 	if (args.fspec != NULL)
407 		return EJUSTRETURN;
408 
409 	if (args.eargs.ex_flags & 0x00020000) {
410 		/* Request to delete exports.  The mask above holds the
411 		 * value that used to be in MNT_DELEXPORT. */
412 		mel.mel_nexports = 0;
413 	} else {
414 		struct export_args eargs;
415 
416 		/* The following assumes export_args has not changed since
417 		 * export_args30. */
418 		memcpy(&eargs, &args.eargs, sizeof(struct export_args));
419 
420 		mel.mel_nexports = 1;
421 		mel.mel_exports = &eargs;
422 	}
423 
424 	return mountd_set_exports_list(&mel, l);
425 }
426 #endif
427 
428 /*
429  * INTERNAL FUNCTIONS
430  */
431 
432 /*
433  * Initializes NFS exports for the file system given in 'mp' if it supports
434  * file handles; this is determined by checking whether mp's vfs_vptofh and
435  * vfs_fhtovp operations are NULL or not.
436  *
437  * If successful, returns 0 and sets *mnpp to the address of the new
438  * mount_netexport_pair item; otherwise returns and appropriate error code
439  * and *mnpp remains unmodified.
440  */
441 static int
442 init_exports(struct mount *mp, struct netexport **nep)
443 {
444 	int error;
445 	struct export_args ea;
446 	struct netexport *ne;
447 
448 	KASSERT(mp != NULL);
449 	KASSERT(mp->mnt_op->vfs_vptofh != NULL &&
450 	    mp->mnt_op->vfs_fhtovp != NULL);
451 
452 	/* Ensure that we do not already have this mount point. */
453 	KASSERT(netexport_lookup(mp) == NULL);
454 
455 	ne = malloc(sizeof(*ne), M_NFS_EXPORT, M_WAITOK | M_ZERO);
456 	ne->ne_mount = mp;
457 
458 	/* Set the default export entry.  Handled internally by export upon
459 	 * first call. */
460 	memset(&ea, 0, sizeof(ea));
461 	ea.ex_root = -2;
462 	if (mp->mnt_flag & MNT_RDONLY)
463 		ea.ex_flags |= MNT_EXRDONLY;
464 	error = export(ne, &ea);
465 	if (error != 0) {
466 		free(ne, M_NFS_EXPORT);
467 	} else {
468 		netexport_insert(ne);
469 		*nep = ne;
470 	}
471 
472 	return error;
473 }
474 
475 /*
476  * Build hash lists of net addresses and hang them off the mount point.
477  * Called by export() to set up a new entry in the lists of export
478  * addresses.
479  */
480 static int
481 hang_addrlist(struct mount *mp, struct netexport *nep,
482     const struct export_args *argp)
483 {
484 	int error, i;
485 	struct netcred *np, *enp;
486 	struct radix_node_head *rnh;
487 	struct sockaddr *saddr, *smask;
488 	struct domain *dom;
489 
490 	smask = NULL;
491 
492 	if (argp->ex_addrlen == 0) {
493 		if (mp->mnt_flag & MNT_DEFEXPORTED)
494 			return EPERM;
495 		np = &nep->ne_defexported;
496 		np->netc_exflags = argp->ex_flags;
497 		crcvt(&np->netc_anon, &argp->ex_anon);
498 		np->netc_anon.cr_ref = 1;
499 		mp->mnt_flag |= MNT_DEFEXPORTED;
500 		return 0;
501 	}
502 
503 	if (argp->ex_addrlen > MLEN || argp->ex_masklen > MLEN)
504 		return EINVAL;
505 
506 	i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
507 	np = malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
508 	saddr = (struct sockaddr *)(np + 1);
509 	error = copyin(argp->ex_addr, saddr, argp->ex_addrlen);
510 	if (error)
511 		goto out;
512 	if (saddr->sa_len > argp->ex_addrlen)
513 		saddr->sa_len = argp->ex_addrlen;
514 	if (sacheck(saddr) == -1)
515 		return EINVAL;
516 	if (argp->ex_masklen) {
517 		smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
518 		error = copyin(argp->ex_mask, smask, argp->ex_masklen);
519 		if (error)
520 			goto out;
521 		if (smask->sa_len > argp->ex_masklen)
522 			smask->sa_len = argp->ex_masklen;
523 		if (smask->sa_family != saddr->sa_family)
524 			return EINVAL;
525 		if (sacheck(smask) == -1)
526 			return EINVAL;
527 	}
528 	i = saddr->sa_family;
529 	if ((rnh = nep->ne_rtable[i]) == 0) {
530 		/*
531 		 * Seems silly to initialize every AF when most are not
532 		 * used, do so on demand here
533 		 */
534 		DOMAIN_FOREACH(dom) {
535 			if (dom->dom_family == i && dom->dom_rtattach) {
536 				dom->dom_rtattach((void **)&nep->ne_rtable[i],
537 					dom->dom_rtoffset);
538 				break;
539 			}
540 		}
541 		if ((rnh = nep->ne_rtable[i]) == 0) {
542 			error = ENOBUFS;
543 			goto out;
544 		}
545 	}
546 
547 	enp = (struct netcred *)(*rnh->rnh_addaddr)(saddr, smask, rnh,
548 	    np->netc_rnodes);
549 	if (enp != np) {
550 		if (enp == NULL) {
551 			enp = (struct netcred *)(*rnh->rnh_lookup)(saddr,
552 			    smask, rnh);
553 			if (enp == NULL) {
554 				error = EPERM;
555 				goto out;
556 			}
557 		} else
558 			enp->netc_refcnt++;
559 
560 		goto check;
561 	} else
562 		enp->netc_refcnt = 1;
563 
564 	np->netc_exflags = argp->ex_flags;
565 	crcvt(&np->netc_anon, &argp->ex_anon);
566 	np->netc_anon.cr_ref = 1;
567 	return 0;
568 check:
569 	if (enp->netc_exflags != argp->ex_flags ||
570 	    crcmp(&enp->netc_anon, &argp->ex_anon) != 0)
571 		error = EPERM;
572 	else
573 		error = 0;
574 out:
575 	free(np, M_NETADDR);
576 	return error;
577 }
578 
579 /*
580  * Ensure that the address stored in 'sa' is valid.
581  * Returns zero on success, otherwise -1.
582  */
583 static int
584 sacheck(struct sockaddr *sa)
585 {
586 
587 	switch (sa->sa_family) {
588 #ifdef INET
589 	case AF_INET: {
590 		struct sockaddr_in *sin = (struct sockaddr_in *)sa;
591 		char *p = (char *)sin->sin_zero;
592 		size_t i;
593 
594 		if (sin->sin_len != sizeof(*sin))
595 			return -1;
596 		if (sin->sin_port != 0)
597 			return -1;
598 		for (i = 0; i < sizeof(sin->sin_zero); i++)
599 			if (*p++ != '\0')
600 				return -1;
601 		return 0;
602 	}
603 #endif
604 #ifdef INET6
605 	case AF_INET6: {
606 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
607 
608 		if (sin6->sin6_len != sizeof(*sin6))
609 			return -1;
610 		if (sin6->sin6_port != 0)
611 			return -1;
612 		return 0;
613 	}
614 #endif
615 	default:
616 		return -1;
617 	}
618 }
619 
620 /*
621  * Free the netcred object pointed to by the 'rn' radix node.
622  * 'w' holds a pointer to the radix tree head.
623  */
624 static int
625 free_netcred(struct radix_node *rn, void *w)
626 {
627 	struct radix_node_head *rnh = (struct radix_node_head *)w;
628 	struct netcred *np = (struct netcred *)(void *)rn;
629 
630 	(*rnh->rnh_deladdr)(rn->rn_key, rn->rn_mask, rnh);
631 	if (--(np->netc_refcnt) <= 0)
632 		free(np, M_NETADDR);
633 	return 0;
634 }
635 
636 /*
637  * Clears the exports list for a given file system.
638  */
639 static void
640 clear_exports(struct netexport *ne)
641 {
642 	struct radix_node_head *rnh;
643 	struct mount *mp = ne->ne_mount;
644 	int i;
645 
646 	if (mp->mnt_flag & MNT_EXPUBLIC) {
647 		setpublicfs(NULL, NULL, NULL);
648 		mp->mnt_flag &= ~MNT_EXPUBLIC;
649 	}
650 
651 	for (i = 0; i <= AF_MAX; i++) {
652 		if ((rnh = ne->ne_rtable[i]) != NULL) {
653 			(*rnh->rnh_walktree)(rnh, free_netcred, rnh);
654 			free(rnh, M_RTABLE);
655 			ne->ne_rtable[i] = NULL;
656 		}
657 	}
658 
659 	mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
660 }
661 
662 /*
663  * Add a new export entry (described by an export_args structure) to the
664  * given file system.
665  */
666 static int
667 export(struct netexport *nep, const struct export_args *argp)
668 {
669 	struct mount *mp = nep->ne_mount;
670 	int error;
671 
672 	if (argp->ex_flags & MNT_EXPORTED) {
673 		if (argp->ex_flags & MNT_EXPUBLIC) {
674 			if ((error = setpublicfs(mp, nep, argp)) != 0)
675 				return error;
676 			mp->mnt_flag |= MNT_EXPUBLIC;
677 		}
678 		if ((error = hang_addrlist(mp, nep, argp)) != 0)
679 			return error;
680 		mp->mnt_flag |= MNT_EXPORTED;
681 	}
682 	return 0;
683 }
684 
685 /*
686  * Set the publicly exported filesystem (WebNFS).  Currently, only
687  * one public filesystem is possible in the spec (RFC 2054 and 2055)
688  */
689 static int
690 setpublicfs(struct mount *mp, struct netexport *nep,
691     const struct export_args *argp)
692 {
693 	char *cp;
694 	int error;
695 	struct vnode *rvp;
696 
697 	/*
698 	 * mp == NULL -> invalidate the current info, the FS is
699 	 * no longer exported. May be called from either export
700 	 * or unmount, so check if it hasn't already been done.
701 	 */
702 	if (mp == NULL) {
703 		if (nfs_pub.np_valid) {
704 			nfs_pub.np_valid = 0;
705 			if (nfs_pub.np_index != NULL) {
706 				FREE(nfs_pub.np_index, M_TEMP);
707 				nfs_pub.np_index = NULL;
708 			}
709 		}
710 		return 0;
711 	}
712 
713 	/*
714 	 * Only one allowed at a time.
715 	 */
716 	if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
717 		return EBUSY;
718 
719 	/*
720 	 * Get real filehandle for root of exported FS.
721 	 */
722 	memset((caddr_t)&nfs_pub.np_handle, 0, sizeof(nfs_pub.np_handle));
723 	nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsidx;
724 
725 	if ((error = VFS_ROOT(mp, &rvp)))
726 		return error;
727 
728 	if ((error = VFS_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
729 		return error;
730 
731 	vput(rvp);
732 
733 	/*
734 	 * If an indexfile was specified, pull it in.
735 	 */
736 	if (argp->ex_indexfile != NULL) {
737 		MALLOC(nfs_pub.np_index, char *, MAXNAMLEN + 1, M_TEMP,
738 		    M_WAITOK);
739 		error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
740 		    MAXNAMLEN, (size_t *)0);
741 		if (!error) {
742 			/*
743 			 * Check for illegal filenames.
744 			 */
745 			for (cp = nfs_pub.np_index; *cp; cp++) {
746 				if (*cp == '/') {
747 					error = EINVAL;
748 					break;
749 				}
750 			}
751 		}
752 		if (error) {
753 			FREE(nfs_pub.np_index, M_TEMP);
754 			return error;
755 		}
756 	}
757 
758 	nfs_pub.np_mount = mp;
759 	nfs_pub.np_valid = 1;
760 	return 0;
761 }
762 
763 /*
764  * Lookup an export entry in the exports list that matches the address
765  * stored in 'nam'.  If no entry is found, the default one is used instead
766  * (if available).
767  */
768 static struct netcred *
769 netcred_lookup(struct netexport *ne, struct mbuf *nam)
770 {
771 	struct netcred *np;
772 	struct radix_node_head *rnh;
773 	struct sockaddr *saddr;
774 
775 	if ((ne->ne_mount->mnt_flag & MNT_EXPORTED) == 0) {
776 		return NULL;
777 	}
778 
779 	/*
780 	 * Lookup in the export list first.
781 	 */
782 	np = NULL;
783 	if (nam != NULL) {
784 		saddr = mtod(nam, struct sockaddr *);
785 		rnh = ne->ne_rtable[saddr->sa_family];
786 		if (rnh != NULL) {
787 			np = (struct netcred *)
788 				(*rnh->rnh_matchaddr)((caddr_t)saddr,
789 						      rnh);
790 			if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
791 				np = NULL;
792 		}
793 	}
794 	/*
795 	 * If no address match, use the default if it exists.
796 	 */
797 	if (np == NULL && ne->ne_mount->mnt_flag & MNT_DEFEXPORTED)
798 		np = &ne->ne_defexported;
799 
800 	return np;
801 }
802