1 /* $NetBSD: nfs_export.c,v 1.30 2007/07/12 19:35:35 dsl Exp $ */
4 * Copyright (c) 1997, 1998, 2004, 2005 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.
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.
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.
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.
53 * Redistribution and use in source and binary forms, with or without
54 * modification, are permitted provided that the following conditions
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.
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
77 * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94
81 * VFS exports list management.
84 #include <sys/cdefs.h>
85 __KERNEL_RCSID(0, "$NetBSD: nfs_export.c,v 1.30 2007/07/12 19:35:35 dsl Exp $");
87 #include "opt_compat_netbsd.h"
90 #include <sys/param.h>
91 #include <sys/systm.h>
92 #include <sys/queue.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 #include <sys/kauth.h>
105 #include <net/radix.h>
107 #include <netinet/in.h>
109 #include <nfs/rpcv2.h>
110 #include <nfs/nfsproto.h>
112 #include <nfs/nfs_var.h>
115 * Network address lookup element.
118 struct radix_node netc_rnodes
[2];
121 kauth_cred_t netc_anon
;
125 * Network export information.
128 CIRCLEQ_ENTRY(netexport
) ne_list
;
129 struct mount
*ne_mount
;
130 struct netcred ne_defexported
; /* Default export */
131 struct radix_node_head
*ne_rtable
[AF_MAX
+1]; /* Individual exports */
133 CIRCLEQ_HEAD(, netexport
) netexport_list
=
134 CIRCLEQ_HEAD_INITIALIZER(netexport_list
);
136 /* Malloc type used by the mount<->netexport map. */
137 MALLOC_DEFINE(M_NFS_EXPORT
, "nfs_export", "NFS export data");
139 /* Publicly exported file system. */
140 struct nfs_public nfs_pub
;
145 static int init_exports(struct mount
*, struct netexport
**);
146 static int hang_addrlist(struct mount
*, struct netexport
*,
147 const struct export_args
*);
148 static int sacheck(struct sockaddr
*);
149 static int free_netcred(struct radix_node
*, void *);
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_clear(struct netexport
*);
157 static void netexport_insert(struct netexport
*);
158 static void netexport_remove(struct netexport
*);
159 static void netexport_wrlock(void);
160 static void netexport_wrunlock(void);
167 * Declare and initialize the file system export hooks.
169 static void nfs_export_unmount(struct mount
*);
171 struct vfs_hooks nfs_export_hooks
= {
174 VFS_HOOKS_ATTACH(nfs_export_hooks
);
177 * VFS unmount hook for NFS exports.
179 * Releases NFS exports list resources if the given mount point has some.
180 * As allocation happens lazily, it may be that it doesn't has this
181 * information, although it theorically should.
184 nfs_export_unmount(struct mount
*mp
)
186 struct netexport
*ne
;
191 ne
= netexport_lookup(mp
);
193 netexport_wrunlock();
197 netexport_remove(ne
);
198 netexport_wrunlock();
199 free(ne
, M_NFS_EXPORT
);
203 * Atomically set the NFS exports list of the given file system, replacing
204 * it with a new list of entries.
206 * Returns zero on success or an appropriate error code otherwise.
208 * Helper function for the nfssvc(2) system call (NFSSVC_SETEXPORTSLIST
212 mountd_set_exports_list(const struct mountd_exports_list
*mel
, struct lwp
*l
)
216 /* XXX: See below to see the reason why this is disabled. */
220 struct netexport
*ne
;
226 if (kauth_authorize_generic(l
->l_cred
, KAUTH_GENERIC_ISSUSER
,
230 /* Lookup the file system path. */
231 NDINIT(&nd
, LOOKUP
, FOLLOW
| LOCKLEAF
, UIO_USERSPACE
, mel
->mel_path
);
239 if ((error
= VFS_VPTOFH(vp
, NULL
, &fid_size
)) == E2BIG
) {
240 fid
= malloc(fid_size
, M_TEMP
, M_NOWAIT
);
242 error
= VFS_VPTOFH(vp
, fid
, &fid_size
);
251 /* Mark the file system busy. */
252 error
= vfs_busy(mp
, LK_NOWAIT
, NULL
);
258 ne
= netexport_lookup(mp
);
260 error
= init_exports(mp
, &ne
);
267 KASSERT(ne
->ne_mount
== mp
);
270 * XXX: The part marked as 'notyet' works fine from the kernel's
271 * point of view, in the sense that it is able to atomically update
272 * the complete exports list for a file system. However, supporting
273 * this in mountd(8) requires a lot of work; so, for now, keep the
274 * old behavior of updating a single entry per call.
276 * When mountd(8) is fixed, just remove the second branch of this
277 * preprocessor conditional and enable the first one.
281 for (i
= 0; error
== 0 && i
< mel
->mel_nexports
; i
++)
282 error
= export(ne
, &mel
->mel_exports
[i
]);
284 if (mel
->mel_nexports
== 0)
286 else if (mel
->mel_nexports
== 1)
287 error
= export(ne
, &mel
->mel_exports
[0]);
289 printf("mountd_set_exports_list: Cannot set more than one "
290 "entry at once (unimplemented)\n");
296 netexport_wrunlock();
302 netexport_insert(struct netexport
*ne
)
305 CIRCLEQ_INSERT_HEAD(&netexport_list
, ne
, ne_list
);
309 netexport_remove(struct netexport
*ne
)
312 CIRCLEQ_REMOVE(&netexport_list
, ne
, ne_list
);
315 static struct netexport
*
316 netexport_lookup(const struct mount
*mp
)
318 struct netexport
*ne
;
320 CIRCLEQ_FOREACH(ne
, &netexport_list
, ne_list
) {
321 if (ne
->ne_mount
== mp
) {
330 static struct netexport
*
331 netexport_lookup_byfsid(const fsid_t
*fsid
)
333 struct netexport
*ne
;
335 CIRCLEQ_FOREACH(ne
, &netexport_list
, ne_list
) {
336 const struct mount
*mp
= ne
->ne_mount
;
338 if (mp
->mnt_stat
.f_fsidx
.__fsid_val
[0] == fsid
->__fsid_val
[0] &&
339 mp
->mnt_stat
.f_fsidx
.__fsid_val
[1] == fsid
->__fsid_val
[1]) {
350 * Check if the file system specified by the 'mp' mount structure is
351 * exported to a client with 'anon' anonymous credentials. The 'mb'
352 * argument is an mbuf containing the network address of the client.
353 * The return parameters for the export flags for the client are returned
354 * in the address specified by 'wh'.
356 * This function is used exclusively by the NFS server. It is generally
357 * invoked before VFS_FHTOVP to validate that client has access to the
362 netexport_check(const fsid_t
*fsid
, struct mbuf
*mb
, struct mount
**mpp
,
363 int *wh
, kauth_cred_t
*anon
)
365 struct netexport
*ne
;
368 ne
= netexport_lookup_byfsid(fsid
);
372 np
= netcred_lookup(ne
, mb
);
378 *wh
= np
->netc_exflags
;
379 *anon
= np
->netc_anon
;
386 * Handles legacy export requests. In this case, the export information
387 * is hardcoded in a specific place of the mount arguments structure (given
388 * in data); the request for an update is given through the fspec field
389 * (also in a known location), which must be a null pointer.
391 * Returns EJUSTRETURN if the given command was not a export request.
392 * Otherwise, returns 0 on success or an appropriate error code otherwise.
395 nfs_update_exports_30(struct mount
*mp
, const char *path
,
396 struct mnt_export_args30
*args
, struct lwp
*l
)
398 struct mountd_exports_list mel
;
402 if (args
->fspec
!= NULL
)
405 if (args
->eargs
.ex_flags
& 0x00020000) {
406 /* Request to delete exports. The mask above holds the
407 * value that used to be in MNT_DELEXPORT. */
408 mel
.mel_nexports
= 0;
410 /* The following assumes export_args has not changed since
411 * export_args30 - typedef checks sizes. */
412 typedef char x
[sizeof args
->eargs
== sizeof *mel
.mel_exports
? 1 : -1];
414 mel
.mel_nexports
= 1;
415 mel
.mel_exports
= (void *)&args
->eargs
;
418 return mountd_set_exports_list(&mel
, l
);
427 * Initializes NFS exports for the file system given in 'mp' if it supports
428 * file handles; this is determined by checking whether mp's vfs_vptofh and
429 * vfs_fhtovp operations are NULL or not.
431 * If successful, returns 0 and sets *mnpp to the address of the new
432 * mount_netexport_pair item; otherwise returns an appropriate error code
433 * and *mnpp remains unmodified.
436 init_exports(struct mount
*mp
, struct netexport
**nep
)
439 struct export_args ea
;
440 struct netexport
*ne
;
444 /* Ensure that we do not already have this mount point. */
445 KASSERT(netexport_lookup(mp
) == NULL
);
447 ne
= malloc(sizeof(*ne
), M_NFS_EXPORT
, M_WAITOK
| M_ZERO
);
450 /* Set the default export entry. Handled internally by export upon
452 memset(&ea
, 0, sizeof(ea
));
454 if (mp
->mnt_flag
& MNT_RDONLY
)
455 ea
.ex_flags
|= MNT_EXRDONLY
;
456 error
= export(ne
, &ea
);
458 free(ne
, M_NFS_EXPORT
);
460 netexport_insert(ne
);
468 * Build hash lists of net addresses and hang them off the mount point.
469 * Called by export() to set up a new entry in the lists of export
473 hang_addrlist(struct mount
*mp
, struct netexport
*nep
,
474 const struct export_args
*argp
)
477 struct netcred
*np
, *enp
;
478 struct radix_node_head
*rnh
;
479 struct sockaddr
*saddr
, *smask
;
484 if (argp
->ex_addrlen
== 0) {
485 if (mp
->mnt_flag
& MNT_DEFEXPORTED
)
487 np
= &nep
->ne_defexported
;
488 KASSERT(np
->netc_anon
== NULL
);
489 np
->netc_anon
= kauth_cred_alloc();
490 np
->netc_exflags
= argp
->ex_flags
;
491 kauth_uucred_to_cred(np
->netc_anon
, &argp
->ex_anon
);
492 mp
->mnt_flag
|= MNT_DEFEXPORTED
;
496 if (argp
->ex_addrlen
> MLEN
|| argp
->ex_masklen
> MLEN
)
499 i
= sizeof(struct netcred
) + argp
->ex_addrlen
+ argp
->ex_masklen
;
500 np
= malloc(i
, M_NETADDR
, M_WAITOK
| M_ZERO
);
501 np
->netc_anon
= kauth_cred_alloc();
502 saddr
= (struct sockaddr
*)(np
+ 1);
503 error
= copyin(argp
->ex_addr
, saddr
, argp
->ex_addrlen
);
506 if (saddr
->sa_len
> argp
->ex_addrlen
)
507 saddr
->sa_len
= argp
->ex_addrlen
;
508 if (sacheck(saddr
) == -1)
510 if (argp
->ex_masklen
) {
511 smask
= (struct sockaddr
*)((char *)saddr
+ argp
->ex_addrlen
);
512 error
= copyin(argp
->ex_mask
, smask
, argp
->ex_masklen
);
515 if (smask
->sa_len
> argp
->ex_masklen
)
516 smask
->sa_len
= argp
->ex_masklen
;
517 if (smask
->sa_family
!= saddr
->sa_family
)
519 if (sacheck(smask
) == -1)
522 i
= saddr
->sa_family
;
523 if ((rnh
= nep
->ne_rtable
[i
]) == 0) {
525 * Seems silly to initialize every AF when most are not
526 * used, do so on demand here
528 DOMAIN_FOREACH(dom
) {
529 if (dom
->dom_family
== i
&& dom
->dom_rtattach
) {
530 dom
->dom_rtattach((void **)&nep
->ne_rtable
[i
],
535 if ((rnh
= nep
->ne_rtable
[i
]) == 0) {
541 enp
= (struct netcred
*)(*rnh
->rnh_addaddr
)(saddr
, smask
, rnh
,
545 enp
= (struct netcred
*)(*rnh
->rnh_lookup
)(saddr
,
556 enp
->netc_refcnt
= 1;
558 np
->netc_exflags
= argp
->ex_flags
;
559 kauth_uucred_to_cred(np
->netc_anon
, &argp
->ex_anon
);
562 if (enp
->netc_exflags
!= argp
->ex_flags
||
563 kauth_cred_uucmp(enp
->netc_anon
, &argp
->ex_anon
) != 0)
568 KASSERT(np
->netc_anon
!= NULL
);
569 kauth_cred_free(np
->netc_anon
);
575 * Ensure that the address stored in 'sa' is valid.
576 * Returns zero on success, otherwise -1.
579 sacheck(struct sockaddr
*sa
)
582 switch (sa
->sa_family
) {
585 struct sockaddr_in
*sin
= (struct sockaddr_in
*)sa
;
586 char *p
= (char *)sin
->sin_zero
;
589 if (sin
->sin_len
!= sizeof(*sin
))
591 if (sin
->sin_port
!= 0)
593 for (i
= 0; i
< sizeof(sin
->sin_zero
); i
++)
601 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
603 if (sin6
->sin6_len
!= sizeof(*sin6
))
605 if (sin6
->sin6_port
!= 0)
616 * Free the netcred object pointed to by the 'rn' radix node.
617 * 'w' holds a pointer to the radix tree head.
620 free_netcred(struct radix_node
*rn
, void *w
)
622 struct radix_node_head
*rnh
= (struct radix_node_head
*)w
;
623 struct netcred
*np
= (struct netcred
*)(void *)rn
;
625 (*rnh
->rnh_deladdr
)(rn
->rn_key
, rn
->rn_mask
, rnh
);
626 if (--(np
->netc_refcnt
) <= 0) {
627 KASSERT(np
->netc_anon
!= NULL
);
628 kauth_cred_free(np
->netc_anon
);
635 * Clears the exports list for a given file system.
638 netexport_clear(struct netexport
*ne
)
640 struct radix_node_head
*rnh
;
641 struct mount
*mp
= ne
->ne_mount
;
644 if (mp
->mnt_flag
& MNT_EXPUBLIC
) {
645 setpublicfs(NULL
, NULL
, NULL
);
646 mp
->mnt_flag
&= ~MNT_EXPUBLIC
;
649 for (i
= 0; i
<= AF_MAX
; i
++) {
650 if ((rnh
= ne
->ne_rtable
[i
]) != NULL
) {
651 rn_walktree(rnh
, free_netcred
, rnh
);
653 ne
->ne_rtable
[i
] = NULL
;
657 if ((mp
->mnt_flag
& MNT_DEFEXPORTED
) != 0) {
658 struct netcred
*np
= &ne
->ne_defexported
;
660 KASSERT(np
->netc_anon
!= NULL
);
661 kauth_cred_free(np
->netc_anon
);
662 np
->netc_anon
= NULL
;
664 KASSERT(ne
->ne_defexported
.netc_anon
== NULL
);
667 mp
->mnt_flag
&= ~(MNT_EXPORTED
| MNT_DEFEXPORTED
);
671 * Add a new export entry (described by an export_args structure) to the
675 export(struct netexport
*nep
, const struct export_args
*argp
)
677 struct mount
*mp
= nep
->ne_mount
;
680 if (argp
->ex_flags
& MNT_EXPORTED
) {
681 if (argp
->ex_flags
& MNT_EXPUBLIC
) {
682 if ((error
= setpublicfs(mp
, nep
, argp
)) != 0)
684 mp
->mnt_flag
|= MNT_EXPUBLIC
;
686 if ((error
= hang_addrlist(mp
, nep
, argp
)) != 0)
688 mp
->mnt_flag
|= MNT_EXPORTED
;
694 * Set the publicly exported filesystem (WebNFS). Currently, only
695 * one public filesystem is possible in the spec (RFC 2054 and 2055)
698 setpublicfs(struct mount
*mp
, struct netexport
*nep
,
699 const struct export_args
*argp
)
707 * mp == NULL -> invalidate the current info, the FS is
708 * no longer exported. May be called from either export
709 * or unmount, so check if it hasn't already been done.
712 if (nfs_pub
.np_valid
) {
713 nfs_pub
.np_valid
= 0;
714 if (nfs_pub
.np_handle
!= NULL
) {
715 free(nfs_pub
.np_handle
, M_TEMP
);
716 nfs_pub
.np_handle
= NULL
;
718 if (nfs_pub
.np_index
!= NULL
) {
719 FREE(nfs_pub
.np_index
, M_TEMP
);
720 nfs_pub
.np_index
= NULL
;
727 * Only one allowed at a time.
729 if (nfs_pub
.np_valid
!= 0 && mp
!= nfs_pub
.np_mount
)
733 * Get real filehandle for root of exported FS.
735 if ((error
= VFS_ROOT(mp
, &rvp
)))
739 error
= vfs_composefh(rvp
, NULL
, &fhsize
);
742 nfs_pub
.np_handle
= malloc(fhsize
, M_TEMP
, M_NOWAIT
);
743 if (nfs_pub
.np_handle
== NULL
)
746 error
= vfs_composefh(rvp
, nfs_pub
.np_handle
, &fhsize
);
753 * If an indexfile was specified, pull it in.
755 if (argp
->ex_indexfile
!= NULL
) {
756 MALLOC(nfs_pub
.np_index
, char *, MAXNAMLEN
+ 1, M_TEMP
,
758 error
= copyinstr(argp
->ex_indexfile
, nfs_pub
.np_index
,
759 MAXNAMLEN
, (size_t *)0);
762 * Check for illegal filenames.
764 for (cp
= nfs_pub
.np_index
; *cp
; cp
++) {
772 FREE(nfs_pub
.np_index
, M_TEMP
);
777 nfs_pub
.np_mount
= mp
;
778 nfs_pub
.np_valid
= 1;
783 * Lookup an export entry in the exports list that matches the address
784 * stored in 'nam'. If no entry is found, the default one is used instead
787 static struct netcred
*
788 netcred_lookup(struct netexport
*ne
, struct mbuf
*nam
)
791 struct radix_node_head
*rnh
;
792 struct sockaddr
*saddr
;
794 if ((ne
->ne_mount
->mnt_flag
& MNT_EXPORTED
) == 0) {
799 * Lookup in the export list first.
803 saddr
= mtod(nam
, struct sockaddr
*);
804 rnh
= ne
->ne_rtable
[saddr
->sa_family
];
806 np
= (struct netcred
*)
807 (*rnh
->rnh_matchaddr
)((void *)saddr
,
809 if (np
&& np
->netc_rnodes
->rn_flags
& RNF_ROOT
)
814 * If no address match, use the default if it exists.
816 if (np
== NULL
&& ne
->ne_mount
->mnt_flag
& MNT_DEFEXPORTED
)
817 np
= &ne
->ne_defexported
;
822 krwlock_t netexport_lock
;
825 netexport_rdlock(void)
828 rw_enter(&netexport_lock
, RW_READER
);
832 netexport_rdunlock(void)
835 rw_exit(&netexport_lock
);
839 netexport_wrlock(void)
842 rw_enter(&netexport_lock
, RW_WRITER
);
846 netexport_wrunlock(void)
849 rw_exit(&netexport_lock
);