vfs_export.c revision 138167
1/*
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 * (c) UNIX System Laboratories, Inc.
5 * All or some portions of this file are derived from material licensed
6 * to the University of California by American Telephone and Telegraph
7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8 * the permission of UNIX System Laboratories, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	@(#)vfs_subr.c	8.31 (Berkeley) 5/26/95
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/sys/kern/vfs_export.c 138167 2004-11-28 19:16:00Z cperciva $");
39
40#include <sys/param.h>
41#include <sys/dirent.h>
42#include <sys/domain.h>
43#include <sys/kernel.h>
44#include <sys/lock.h>
45#include <sys/malloc.h>
46#include <sys/mbuf.h>
47#include <sys/mount.h>
48#include <sys/mutex.h>
49#include <sys/socket.h>
50#include <sys/systm.h>
51#include <sys/vnode.h>
52
53#include <net/radix.h>
54
55static MALLOC_DEFINE(M_NETADDR, "Export Host", "Export host address structure");
56
57static void	vfs_free_addrlist(struct netexport *nep);
58static int	vfs_free_netcred(struct radix_node *rn, void *w);
59static int	vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
60		    struct export_args *argp);
61
62/*
63 * Network address lookup element
64 */
65struct netcred {
66	struct	radix_node netc_rnodes[2];
67	int	netc_exflags;
68	struct	ucred netc_anon;
69};
70
71/*
72 * Network export information
73 */
74struct netexport {
75	struct	netcred ne_defexported;		      /* Default export */
76	struct	radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
77};
78
79/*
80 * Build hash lists of net addresses and hang them off the mount point.
81 * Called by ufs_mount() to set up the lists of export addresses.
82 */
83static int
84vfs_hang_addrlist(mp, nep, argp)
85	struct mount *mp;
86	struct netexport *nep;
87	struct export_args *argp;
88{
89	register struct netcred *np;
90	register struct radix_node_head *rnh;
91	register int i;
92	struct radix_node *rn;
93	struct sockaddr *saddr, *smask = 0;
94	struct domain *dom;
95	int error;
96
97	/*
98	 * XXX: This routine converts from a `struct xucred'
99	 * (argp->ex_anon) to a `struct ucred' (np->netc_anon).  This
100	 * operation is questionable; for example, what should be done
101	 * with fields like cr_uidinfo and cr_prison?  Currently, this
102	 * routine does not touch them (leaves them as NULL).
103	 */
104	if (argp->ex_anon.cr_version != XUCRED_VERSION)
105		return (EINVAL);
106
107	if (argp->ex_addrlen == 0) {
108		if (mp->mnt_flag & MNT_DEFEXPORTED)
109			return (EPERM);
110		np = &nep->ne_defexported;
111		np->netc_exflags = argp->ex_flags;
112		bzero(&np->netc_anon, sizeof(np->netc_anon));
113		np->netc_anon.cr_uid = argp->ex_anon.cr_uid;
114		np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
115		bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
116		    sizeof(np->netc_anon.cr_groups));
117		np->netc_anon.cr_ref = 1;
118		mp->mnt_flag |= MNT_DEFEXPORTED;
119		return (0);
120	}
121
122#if MSIZE <= 256
123	if (argp->ex_addrlen > MLEN)
124		return (EINVAL);
125#endif
126
127	i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
128	np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
129	saddr = (struct sockaddr *) (np + 1);
130	if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen)))
131		goto out;
132	if (saddr->sa_family > SA_MAX) {
133		error = EINVAL;
134		goto out;
135	}
136	if (saddr->sa_len > argp->ex_addrlen)
137		saddr->sa_len = argp->ex_addrlen;
138	if (argp->ex_masklen) {
139		smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
140		error = copyin(argp->ex_mask, smask, argp->ex_masklen);
141		if (error)
142			goto out;
143		if (smask->sa_len > argp->ex_masklen)
144			smask->sa_len = argp->ex_masklen;
145	}
146	i = saddr->sa_family;
147	if ((rnh = nep->ne_rtable[i]) == NULL) {
148		/*
149		 * Seems silly to initialize every AF when most are not used,
150		 * do so on demand here
151		 */
152		for (dom = domains; dom; dom = dom->dom_next)
153			if (dom->dom_family == i && dom->dom_rtattach) {
154				dom->dom_rtattach((void **) &nep->ne_rtable[i],
155				    dom->dom_rtoffset);
156				break;
157			}
158		if ((rnh = nep->ne_rtable[i]) == NULL) {
159			error = ENOBUFS;
160			goto out;
161		}
162	}
163	RADIX_NODE_HEAD_LOCK(rnh);
164	rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes);
165	RADIX_NODE_HEAD_UNLOCK(rnh);
166	if (rn == NULL || np != (struct netcred *)rn) {	/* already exists */
167		error = EPERM;
168		goto out;
169	}
170	np->netc_exflags = argp->ex_flags;
171	bzero(&np->netc_anon, sizeof(np->netc_anon));
172	np->netc_anon.cr_uid = argp->ex_anon.cr_uid;
173	np->netc_anon.cr_ngroups = argp->ex_anon.cr_ngroups;
174	bcopy(argp->ex_anon.cr_groups, np->netc_anon.cr_groups,
175	    sizeof(np->netc_anon.cr_groups));
176	np->netc_anon.cr_ref = 1;
177	return (0);
178out:
179	free(np, M_NETADDR);
180	return (error);
181}
182
183/* Helper for vfs_free_addrlist. */
184/* ARGSUSED */
185static int
186vfs_free_netcred(rn, w)
187	struct radix_node *rn;
188	void *w;
189{
190	register struct radix_node_head *rnh = (struct radix_node_head *) w;
191
192	(*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh);
193	free(rn, M_NETADDR);
194	return (0);
195}
196
197/*
198 * Free the net address hash lists that are hanging off the mount points.
199 */
200static void
201vfs_free_addrlist(nep)
202	struct netexport *nep;
203{
204	register int i;
205	register struct radix_node_head *rnh;
206
207	for (i = 0; i <= AF_MAX; i++)
208		if ((rnh = nep->ne_rtable[i])) {
209			RADIX_NODE_HEAD_LOCK(rnh);
210			(*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh);
211			RADIX_NODE_HEAD_DESTROY(rnh);
212			free(rnh, M_RTABLE);
213			nep->ne_rtable[i] = NULL;	/* not SMP safe XXX */
214		}
215}
216
217/*
218 * High level function to manipulate export options on a mount point
219 * and the passed in netexport.
220 * Struct export_args *argp is the variable used to twiddle options,
221 * the structure is described in sys/mount.h
222 */
223int
224vfs_export(mp, argp)
225	struct mount *mp;
226	struct export_args *argp;
227{
228	struct netexport *nep;
229	int error;
230
231	nep = mp->mnt_export;
232	if (argp->ex_flags & MNT_DELEXPORT) {
233		if (nep == NULL)
234			return (ENOENT);
235		if (mp->mnt_flag & MNT_EXPUBLIC) {
236			vfs_setpublicfs(NULL, NULL, NULL);
237			mp->mnt_flag &= ~MNT_EXPUBLIC;
238		}
239		vfs_free_addrlist(nep);
240		mp->mnt_export = NULL;
241		free(nep, M_MOUNT);
242		mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
243	}
244	if (argp->ex_flags & MNT_EXPORTED) {
245		if (nep == NULL) {
246			nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO);
247			mp->mnt_export = nep;
248		}
249		if (argp->ex_flags & MNT_EXPUBLIC) {
250			if ((error = vfs_setpublicfs(mp, nep, argp)) != 0)
251				return (error);
252			mp->mnt_flag |= MNT_EXPUBLIC;
253		}
254		if ((error = vfs_hang_addrlist(mp, nep, argp)))
255			return (error);
256		mp->mnt_flag |= MNT_EXPORTED;
257	}
258	return (0);
259}
260
261/*
262 * Set the publicly exported filesystem (WebNFS). Currently, only
263 * one public filesystem is possible in the spec (RFC 2054 and 2055)
264 */
265int
266vfs_setpublicfs(mp, nep, argp)
267	struct mount *mp;
268	struct netexport *nep;
269	struct export_args *argp;
270{
271	int error;
272	struct vnode *rvp;
273	char *cp;
274
275	/*
276	 * mp == NULL -> invalidate the current info, the FS is
277	 * no longer exported. May be called from either vfs_export
278	 * or unmount, so check if it hasn't already been done.
279	 */
280	if (mp == NULL) {
281		if (nfs_pub.np_valid) {
282			nfs_pub.np_valid = 0;
283			if (nfs_pub.np_index != NULL) {
284				FREE(nfs_pub.np_index, M_TEMP);
285				nfs_pub.np_index = NULL;
286			}
287		}
288		return (0);
289	}
290
291	/*
292	 * Only one allowed at a time.
293	 */
294	if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
295		return (EBUSY);
296
297	/*
298	 * Get real filehandle for root of exported FS.
299	 */
300	bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle));
301	nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
302
303	if ((error = VFS_ROOT(mp, &rvp, curthread /* XXX */)))
304		return (error);
305
306	if ((error = VFS_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
307		return (error);
308
309	vput(rvp);
310
311	/*
312	 * If an indexfile was specified, pull it in.
313	 */
314	if (argp->ex_indexfile != NULL) {
315		MALLOC(nfs_pub.np_index, char *, MAXNAMLEN + 1, M_TEMP,
316		    M_WAITOK);
317		error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
318		    MAXNAMLEN, (size_t *)0);
319		if (!error) {
320			/*
321			 * Check for illegal filenames.
322			 */
323			for (cp = nfs_pub.np_index; *cp; cp++) {
324				if (*cp == '/') {
325					error = EINVAL;
326					break;
327				}
328			}
329		}
330		if (error) {
331			FREE(nfs_pub.np_index, M_TEMP);
332			return (error);
333		}
334	}
335
336	nfs_pub.np_mount = mp;
337	nfs_pub.np_valid = 1;
338	return (0);
339}
340
341/*
342 * Used by the filesystems to determine if a given network address
343 * (passed in 'nam') is present in thier exports list, returns a pointer
344 * to struct netcred so that the filesystem can examine it for
345 * access rights (read/write/etc).
346 */
347struct netcred *
348vfs_export_lookup(mp, nam)
349	register struct mount *mp;
350	struct sockaddr *nam;
351{
352	struct netexport *nep;
353	register struct netcred *np;
354	register struct radix_node_head *rnh;
355	struct sockaddr *saddr;
356
357	nep = mp->mnt_export;
358	if (nep == NULL)
359		return (NULL);
360	np = NULL;
361	if (mp->mnt_flag & MNT_EXPORTED) {
362		/*
363		 * Lookup in the export list first.
364		 */
365		if (nam != NULL) {
366			saddr = nam;
367			rnh = nep->ne_rtable[saddr->sa_family];
368			if (rnh != NULL) {
369				RADIX_NODE_HEAD_LOCK(rnh);
370				np = (struct netcred *)
371				    (*rnh->rnh_matchaddr)(saddr, rnh);
372				RADIX_NODE_HEAD_UNLOCK(rnh);
373				if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
374					np = NULL;
375			}
376		}
377		/*
378		 * If no address match, use the default if it exists.
379		 */
380		if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
381			np = &nep->ne_defexported;
382	}
383	return (np);
384}
385
386/*
387 * XXX: This comment comes from the deprecated ufs_check_export()
388 * XXX: and may not entirely apply, but lacking something better:
389 * This is the generic part of fhtovp called after the underlying
390 * filesystem has validated the file handle.
391 *
392 * Verify that a host should have access to a filesystem.
393 */
394
395int
396vfs_stdcheckexp(mp, nam, extflagsp, credanonp)
397	struct mount *mp;
398	struct sockaddr *nam;
399	int *extflagsp;
400	struct ucred **credanonp;
401{
402	struct netcred *np;
403
404	np = vfs_export_lookup(mp, nam);
405	if (np == NULL)
406		return (EACCES);
407	*extflagsp = np->netc_exflags;
408	*credanonp = &np->netc_anon;
409	return (0);
410}
411
412