FreeBSD kernel kern code
vfs_export.c
Go to the documentation of this file.
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("$BSDSUniX$");
39 
40 #include <sys/param.h>
41 #include <sys/dirent.h>
42 #include <sys/domain.h>
43 #include <sys/jail.h>
44 #include <sys/kernel.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/mbuf.h>
48 #include <sys/mount.h>
49 #include <sys/mutex.h>
50 #include <sys/rwlock.h>
51 #include <sys/refcount.h>
52 #include <sys/signalvar.h>
53 #include <sys/socket.h>
54 #include <sys/systm.h>
55 #include <sys/vnode.h>
56 
57 #include <net/radix.h>
58 
59 static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure");
60 
61 static void vfs_free_addrlist(struct netexport *nep);
62 static int vfs_free_netcred(struct radix_node *rn, void *w);
63 static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
64  struct export_args *argp);
65 static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *);
66 
67 /*
68  * Network address lookup element
69  */
70 struct netcred {
71  struct radix_node netc_rnodes[2];
73  struct ucred *netc_anon;
75  int netc_secflavors[MAXSECFLAVORS];
76 };
77 
78 /*
79  * Network export information
80  */
81 struct netexport {
82  struct netcred ne_defexported; /* Default export */
83  struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
84 };
85 
86 /*
87  * Build hash lists of net addresses and hang them off the mount point.
88  * Called by vfs_export() to set up the lists of export addresses.
89  */
90 static int
91 vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
92  struct export_args *argp)
93 {
94  register struct netcred *np;
95  register struct radix_node_head *rnh;
96  register int i;
97  struct radix_node *rn;
98  struct sockaddr *saddr, *smask = 0;
99  struct domain *dom;
100  int error;
101 
102  /*
103  * XXX: This routine converts from a `struct xucred'
104  * (argp->ex_anon) to a `struct ucred' (np->netc_anon). This
105  * operation is questionable; for example, what should be done
106  * with fields like cr_uidinfo and cr_prison? Currently, this
107  * routine does not touch them (leaves them as NULL).
108  */
109  if (argp->ex_anon.cr_version != XUCRED_VERSION) {
110  vfs_mount_error(mp, "ex_anon.cr_version: %d != %d",
111  argp->ex_anon.cr_version, XUCRED_VERSION);
112  return (EINVAL);
113  }
114 
115  if (argp->ex_addrlen == 0) {
116  if (mp->mnt_flag & MNT_DEFEXPORTED) {
117  vfs_mount_error(mp,
118  "MNT_DEFEXPORTED already set for mount %p", mp);
119  return (EPERM);
120  }
121  np = &nep->ne_defexported;
122  np->netc_exflags = argp->ex_flags;
123  np->netc_anon = crget();
124  np->netc_anon->cr_uid = argp->ex_anon.cr_uid;
125  crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups,
126  argp->ex_anon.cr_groups);
127  np->netc_anon->cr_prison = &prison0;
128  prison_hold(np->netc_anon->cr_prison);
129  np->netc_numsecflavors = argp->ex_numsecflavors;
130  bcopy(argp->ex_secflavors, np->netc_secflavors,
131  sizeof(np->netc_secflavors));
132  MNT_ILOCK(mp);
133  mp->mnt_flag |= MNT_DEFEXPORTED;
134  MNT_IUNLOCK(mp);
135  return (0);
136  }
137 
138 #if MSIZE <= 256
139  if (argp->ex_addrlen > MLEN) {
140  vfs_mount_error(mp, "ex_addrlen %d is greater than %d",
141  argp->ex_addrlen, MLEN);
142  return (EINVAL);
143  }
144 #endif
145 
146  i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
147  np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO);
148  saddr = (struct sockaddr *) (np + 1);
149  if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen)))
150  goto out;
151  if (saddr->sa_family == AF_UNSPEC || saddr->sa_family > AF_MAX) {
152  error = EINVAL;
153  vfs_mount_error(mp, "Invalid saddr->sa_family: %d");
154  goto out;
155  }
156  if (saddr->sa_len > argp->ex_addrlen)
157  saddr->sa_len = argp->ex_addrlen;
158  if (argp->ex_masklen) {
159  smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen);
160  error = copyin(argp->ex_mask, smask, argp->ex_masklen);
161  if (error)
162  goto out;
163  if (smask->sa_len > argp->ex_masklen)
164  smask->sa_len = argp->ex_masklen;
165  }
166  i = saddr->sa_family;
167  if ((rnh = nep->ne_rtable[i]) == NULL) {
168  /*
169  * Seems silly to initialize every AF when most are not used,
170  * do so on demand here
171  */
172  for (dom = domains; dom; dom = dom->dom_next) {
173  KASSERT(((i == AF_INET) || (i == AF_INET6)),
174  ("unexpected protocol in vfs_hang_addrlist"));
175  if (dom->dom_family == i && dom->dom_rtattach) {
176  /*
177  * XXX MRT
178  * The INET and INET6 domains know the
179  * offset already. We don't need to send it
180  * So we just use it as a flag to say that
181  * we are or are not setting up a real routing
182  * table. Only IP and IPV6 need have this
183  * be 0 so all other protocols can stay the
184  * same (ABI compatible).
185  */
186  dom->dom_rtattach(
187  (void **) &nep->ne_rtable[i], 0);
188  break;
189  }
190  }
191  if ((rnh = nep->ne_rtable[i]) == NULL) {
192  error = ENOBUFS;
193  vfs_mount_error(mp, "%s %s %d",
194  "Unable to initialize radix node head ",
195  "for address family", i);
196  goto out;
197  }
198  }
199  RADIX_NODE_HEAD_LOCK(rnh);
200  rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes);
201  RADIX_NODE_HEAD_UNLOCK(rnh);
202  if (rn == NULL || np != (struct netcred *)rn) { /* already exists */
203  error = EPERM;
204  vfs_mount_error(mp, "Invalid radix node head, rn: %p %p",
205  rn, np);
206  goto out;
207  }
208  np->netc_exflags = argp->ex_flags;
209  np->netc_anon = crget();
210  np->netc_anon->cr_uid = argp->ex_anon.cr_uid;
211  crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups,
212  argp->ex_anon.cr_groups);
213  np->netc_anon->cr_prison = &prison0;
214  prison_hold(np->netc_anon->cr_prison);
215  np->netc_numsecflavors = argp->ex_numsecflavors;
216  bcopy(argp->ex_secflavors, np->netc_secflavors,
217  sizeof(np->netc_secflavors));
218  return (0);
219 out:
220  free(np, M_NETADDR);
221  return (error);
222 }
223 
224 /* Helper for vfs_free_addrlist. */
225 /* ARGSUSED */
226 static int
227 vfs_free_netcred(struct radix_node *rn, void *w)
228 {
229  struct radix_node_head *rnh = (struct radix_node_head *) w;
230  struct ucred *cred;
231 
232  (*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh);
233  cred = ((struct netcred *)rn)->netc_anon;
234  if (cred != NULL)
235  crfree(cred);
236  free(rn, M_NETADDR);
237  return (0);
238 }
239 
240 /*
241  * Free the net address hash lists that are hanging off the mount points.
242  */
243 static void
245 {
246  int i;
247  struct radix_node_head *rnh;
248  struct ucred *cred;
249 
250  for (i = 0; i <= AF_MAX; i++) {
251  if ((rnh = nep->ne_rtable[i])) {
252  RADIX_NODE_HEAD_LOCK(rnh);
253  (*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh);
254  RADIX_NODE_HEAD_UNLOCK(rnh);
255  RADIX_NODE_HEAD_DESTROY(rnh);
256  free(rnh, M_RTABLE);
257  nep->ne_rtable[i] = NULL; /* not SMP safe XXX */
258  }
259  }
260  cred = nep->ne_defexported.netc_anon;
261  if (cred != NULL)
262  crfree(cred);
263 
264 }
265 
266 /*
267  * High level function to manipulate export options on a mount point
268  * and the passed in netexport.
269  * Struct export_args *argp is the variable used to twiddle options,
270  * the structure is described in sys/mount.h
271  */
272 int
273 vfs_export(struct mount *mp, struct export_args *argp)
274 {
275  struct netexport *nep;
276  int error;
277 
278  if (argp->ex_numsecflavors < 0
279  || argp->ex_numsecflavors >= MAXSECFLAVORS)
280  return (EINVAL);
281 
282  error = 0;
283  lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL);
284  nep = mp->mnt_export;
285  if (argp->ex_flags & MNT_DELEXPORT) {
286  if (nep == NULL) {
287  error = ENOENT;
288  goto out;
289  }
290  if (mp->mnt_flag & MNT_EXPUBLIC) {
291  vfs_setpublicfs(NULL, NULL, NULL);
292  MNT_ILOCK(mp);
293  mp->mnt_flag &= ~MNT_EXPUBLIC;
294  MNT_IUNLOCK(mp);
295  }
296  vfs_free_addrlist(nep);
297  mp->mnt_export = NULL;
298  free(nep, M_MOUNT);
299  nep = NULL;
300  MNT_ILOCK(mp);
301  mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED);
302  MNT_IUNLOCK(mp);
303  }
304  if (argp->ex_flags & MNT_EXPORTED) {
305  if (nep == NULL) {
306  nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO);
307  mp->mnt_export = nep;
308  }
309  if (argp->ex_flags & MNT_EXPUBLIC) {
310  if ((error = vfs_setpublicfs(mp, nep, argp)) != 0)
311  goto out;
312  MNT_ILOCK(mp);
313  mp->mnt_flag |= MNT_EXPUBLIC;
314  MNT_IUNLOCK(mp);
315  }
316  if ((error = vfs_hang_addrlist(mp, nep, argp)))
317  goto out;
318  MNT_ILOCK(mp);
319  mp->mnt_flag |= MNT_EXPORTED;
320  MNT_IUNLOCK(mp);
321  }
322 
323 out:
324  lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
325  /*
326  * Once we have executed the vfs_export() command, we do
327  * not want to keep the "export" option around in the
328  * options list, since that will cause subsequent MNT_UPDATE
329  * calls to fail. The export information is saved in
330  * mp->mnt_export, so we can safely delete the "export" mount option
331  * here.
332  */
333  vfs_deleteopt(mp->mnt_optnew, "export");
334  vfs_deleteopt(mp->mnt_opt, "export");
335  return (error);
336 }
337 
338 /*
339  * Set the publicly exported filesystem (WebNFS). Currently, only
340  * one public filesystem is possible in the spec (RFC 2054 and 2055)
341  */
342 int
343 vfs_setpublicfs(struct mount *mp, struct netexport *nep,
344  struct export_args *argp)
345 {
346  int error;
347  struct vnode *rvp;
348  char *cp;
349 
350  /*
351  * mp == NULL -> invalidate the current info, the FS is
352  * no longer exported. May be called from either vfs_export
353  * or unmount, so check if it hasn't already been done.
354  */
355  if (mp == NULL) {
356  if (nfs_pub.np_valid) {
357  nfs_pub.np_valid = 0;
358  if (nfs_pub.np_index != NULL) {
359  free(nfs_pub.np_index, M_TEMP);
360  nfs_pub.np_index = NULL;
361  }
362  }
363  return (0);
364  }
365 
366  /*
367  * Only one allowed at a time.
368  */
369  if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount)
370  return (EBUSY);
371 
372  /*
373  * Get real filehandle for root of exported FS.
374  */
375  bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle));
376  nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid;
377 
378  if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp)))
379  return (error);
380 
381  if ((error = VOP_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid)))
382  return (error);
383 
384  vput(rvp);
385 
386  /*
387  * If an indexfile was specified, pull it in.
388  */
389  if (argp->ex_indexfile != NULL) {
390  if (nfs_pub.np_index != NULL)
391  nfs_pub.np_index = malloc(MAXNAMLEN + 1, M_TEMP,
392  M_WAITOK);
393  error = copyinstr(argp->ex_indexfile, nfs_pub.np_index,
394  MAXNAMLEN, (size_t *)0);
395  if (!error) {
396  /*
397  * Check for illegal filenames.
398  */
399  for (cp = nfs_pub.np_index; *cp; cp++) {
400  if (*cp == '/') {
401  error = EINVAL;
402  break;
403  }
404  }
405  }
406  if (error) {
407  free(nfs_pub.np_index, M_TEMP);
408  nfs_pub.np_index = NULL;
409  return (error);
410  }
411  }
412 
413  nfs_pub.np_mount = mp;
414  nfs_pub.np_valid = 1;
415  return (0);
416 }
417 
418 /*
419  * Used by the filesystems to determine if a given network address
420  * (passed in 'nam') is present in their exports list, returns a pointer
421  * to struct netcred so that the filesystem can examine it for
422  * access rights (read/write/etc).
423  */
424 static struct netcred *
425 vfs_export_lookup(struct mount *mp, struct sockaddr *nam)
426 {
427  struct netexport *nep;
428  register struct netcred *np;
429  register struct radix_node_head *rnh;
430  struct sockaddr *saddr;
431 
432  nep = mp->mnt_export;
433  if (nep == NULL)
434  return (NULL);
435  np = NULL;
436  if (mp->mnt_flag & MNT_EXPORTED) {
437  /*
438  * Lookup in the export list first.
439  */
440  if (nam != NULL) {
441  saddr = nam;
442  rnh = nep->ne_rtable[saddr->sa_family];
443  if (rnh != NULL) {
444  RADIX_NODE_HEAD_RLOCK(rnh);
445  np = (struct netcred *)
446  (*rnh->rnh_matchaddr)(saddr, rnh);
447  RADIX_NODE_HEAD_RUNLOCK(rnh);
448  if (np && np->netc_rnodes->rn_flags & RNF_ROOT)
449  np = NULL;
450  }
451  }
452  /*
453  * If no address match, use the default if it exists.
454  */
455  if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED)
456  np = &nep->ne_defexported;
457  }
458  return (np);
459 }
460 
461 /*
462  * XXX: This comment comes from the deprecated ufs_check_export()
463  * XXX: and may not entirely apply, but lacking something better:
464  * This is the generic part of fhtovp called after the underlying
465  * filesystem has validated the file handle.
466  *
467  * Verify that a host should have access to a filesystem.
468  */
469 
470 int
471 vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp,
472  struct ucred **credanonp, int *numsecflavors, int **secflavors)
473 {
474  struct netcred *np;
475 
476  lockmgr(&mp->mnt_explock, LK_SHARED, NULL);
477  np = vfs_export_lookup(mp, nam);
478  if (np == NULL) {
479  lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
480  *credanonp = NULL;
481  return (EACCES);
482  }
483  *extflagsp = np->netc_exflags;
484  if ((*credanonp = np->netc_anon) != NULL)
485  crhold(*credanonp);
486  if (numsecflavors)
487  *numsecflavors = np->netc_numsecflavors;
488  if (secflavors)
489  *secflavors = np->netc_secflavors;
490  lockmgr(&mp->mnt_explock, LK_RELEASE, NULL);
491  return (0);
492 }
493 
struct netcred ne_defexported
Definition: vfs_export.c:82
void vfs_deleteopt(struct vfsoptlist *opts, const char *name)
Definition: vfs_mount.c:166
int vfs_export(struct mount *mp, struct export_args *argp)
Definition: vfs_export.c:273
void * malloc(unsigned long size, struct malloc_type *mtp, int flags)
Definition: kern_malloc.c:454
struct radix_node_head * ne_rtable[AF_MAX+1]
Definition: vfs_export.c:83
int netc_numsecflavors
Definition: vfs_export.c:74
void vfs_mount_error(struct mount *mp, const char *fmt,...)
Definition: vfs_mount.c:1375
int vfs_setpublicfs(struct mount *mp, struct netexport *nep, struct export_args *argp)
Definition: vfs_export.c:343
int netc_exflags
Definition: vfs_export.c:72
struct radix_node netc_rnodes[2]
Definition: vfs_export.c:71
static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, struct export_args *argp)
Definition: vfs_export.c:91
void vput(struct vnode *vp)
Definition: vfs_subr.c:2428
struct ucred * netc_anon
Definition: vfs_export.c:73
struct prison prison0
Definition: kern_jail.c:99
void crfree(struct ucred *cr)
Definition: kern_prot.c:1835
void prison_hold(struct prison *pr)
Definition: kern_jail.c:2632
static struct netcred * vfs_export_lookup(struct mount *, struct sockaddr *)
Definition: vfs_export.c:425
static void vfs_free_addrlist(struct netexport *nep)
Definition: vfs_export.c:244
static MALLOC_DEFINE(M_NETADDR,"export_host","Export host address structure")
int netc_secflavors[MAXSECFLAVORS]
Definition: vfs_export.c:75
__FBSDID("$BSDSUniX$")
struct ucred * crhold(struct ucred *cr)
Definition: kern_prot.c:1824
void free(void *addr, struct malloc_type *mtp)
Definition: kern_malloc.c:554
void crsetgroups(struct ucred *cr, int ngrp, gid_t *groups)
Definition: kern_prot.c:2052
struct domain * domains
Definition: uipc_domain.c:76
int vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, struct ucred **credanonp, int *numsecflavors, int **secflavors)
Definition: vfs_export.c:471
static int vfs_free_netcred(struct radix_node *rn, void *w)
Definition: vfs_export.c:227
struct ucred * crget(void)
Definition: kern_prot.c:1804