2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Herb Hasler and Rick Macklem at The University of Guelph.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 static const char copyright[] =
35 "@(#) Copyright (c) 1989, 1993\n\
36 The Regents of the University of California. All rights reserved.\n";
41 static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
45 #include <sys/cdefs.h>
46 __FBSDID("$FreeBSD$");
48 #include <sys/param.h>
49 #include <sys/mount.h>
50 #include <sys/fcntl.h>
52 #include <sys/syslog.h>
53 #include <sys/sysctl.h>
54 #include <sys/linker.h>
55 #include <sys/module.h>
58 #include <rpc/rpc_com.h>
59 #include <rpc/pmap_clnt.h>
60 #include <rpc/pmap_prot.h>
61 #include <rpcsvc/mount.h>
62 #include <nfs/rpcv2.h>
63 #include <nfs/nfsproto.h>
64 #include <nfsserver/nfs.h>
66 #include <arpa/inet.h>
81 #include "pathnames.h"
89 * Structures for keeping the mount list and export list
92 struct mountlist *ml_next;
93 char ml_host[RPCMNT_NAMELEN+1];
94 char ml_dirp[RPCMNT_PATHLEN+1];
98 struct dirlist *dp_left;
99 struct dirlist *dp_right;
101 struct hostlist *dp_hosts; /* List of hosts this dir exported to */
102 char dp_dirp[1]; /* Actually malloc'd to size of dir */
105 #define DP_DEFSET 0x1
106 #define DP_HOSTSET 0x2
109 struct exportlist *ex_next;
110 struct dirlist *ex_dirl;
111 struct dirlist *ex_defdir;
118 #define EX_LINKED 0x1
121 struct sockaddr_storage nt_net;
122 struct sockaddr_storage nt_mask;
127 struct addrinfo *gt_addrinfo;
128 struct netmsk gt_net;
133 union grouptypes gr_ptr;
134 struct grouplist *gr_next;
140 #define GT_DEFAULT 0x3
141 #define GT_IGNORE 0x5
144 int ht_flag; /* Uses DP_xx bits */
145 struct grouplist *ht_grp;
146 struct hostlist *ht_next;
156 char *add_expdir(struct dirlist **, char *, int);
157 void add_dlist(struct dirlist **, struct dirlist *,
158 struct grouplist *, int);
159 void add_mlist(char *, char *);
160 int check_dirpath(char *);
161 int check_options(struct dirlist *);
162 int checkmask(struct sockaddr *sa);
163 int chk_host(struct dirlist *, struct sockaddr *, int *, int *);
164 void create_service(struct netconfig *nconf);
165 void del_mlist(char *hostp, char *dirp);
166 struct dirlist *dirp_search(struct dirlist *, char *);
167 int do_mount(struct exportlist *, struct grouplist *, int,
168 struct xucred *, char *, int, struct statfs *);
169 int do_opt(char **, char **, struct exportlist *, struct grouplist *,
170 int *, int *, struct xucred *);
171 struct exportlist *ex_search(fsid_t *);
172 struct exportlist *get_exp(void);
173 void free_dir(struct dirlist *);
174 void free_exp(struct exportlist *);
175 void free_grp(struct grouplist *);
176 void free_host(struct hostlist *);
177 void get_exportlist(void);
178 int get_host(char *, struct grouplist *, struct grouplist *);
179 struct hostlist *get_ht(void);
181 void get_mountlist(void);
182 int get_net(char *, struct netmsk *, int);
183 void getexp_err(struct exportlist *, struct grouplist *);
184 struct grouplist *get_grp(void);
185 void hang_dirp(struct dirlist *, struct grouplist *,
186 struct exportlist *, int);
187 void huphandler(int sig);
188 int makemask(struct sockaddr_storage *ssp, int bitlen);
189 void mntsrv(struct svc_req *, SVCXPRT *);
190 void nextfield(char **, char **);
191 void out_of_mem(void);
192 void parsecred(char *, struct xucred *);
193 int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
194 void *sa_rawaddr(struct sockaddr *sa, int *nbytes);
195 int sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
196 struct sockaddr *samask);
197 int scan_tree(struct dirlist *, struct sockaddr *);
198 static void usage(void);
199 int xdr_dir(XDR *, char *);
200 int xdr_explist(XDR *, caddr_t);
201 int xdr_explist_brief(XDR *, caddr_t);
202 int xdr_fhs(XDR *, caddr_t);
203 int xdr_mlist(XDR *, caddr_t);
206 struct exportlist *exphead;
207 struct mountlist *mlhead;
208 struct grouplist *grphead;
209 char *exnames_default[2] = { _PATH_EXPORTS, NULL };
212 struct xucred def_anon = {
220 int resvport_only = 1;
227 char *svcport_str = NULL;
230 static int have_v6 = 1;
232 struct pidfh *pfh = NULL;
233 /* Bits for opt_flags above */
234 #define OP_MAPROOT 0x01
235 #define OP_MAPALL 0x02
239 #define OP_ALLDIRS 0x40
240 #define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */
241 #define OP_QUIET 0x100
242 #define OP_MASKLEN 0x200
246 void SYSLOG(int, const char *, ...) __printflike(2, 3);
247 #define syslog SYSLOG
253 * Mountd server for NFS mount protocol as described in:
254 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
255 * The optional arguments are the exports file name
256 * default: _PATH_EXPORTS
257 * and "-n" to allow nonroot mount.
265 struct netconfig *nconf;
266 char *endptr, **hosts_bak;
271 int maxrec = RPC_MAXDATASIZE;
273 /* Check that another mountd isn't already running. */
274 pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
277 errx(1, "mountd already running, pid: %d.", otherpid);
278 warn("cannot open or create pidfile");
281 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
286 if (modfind("nfsserver") < 0) {
287 /* Not present in kernel, try loading it */
288 if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
289 errx(1, "NFS server is not available or loadable");
292 while ((c = getopt(argc, argv, "2dh:lnp:r")) != -1)
304 debug = debug ? 0 : 1;
311 svcport = (in_port_t)strtoul(optarg, &endptr, 10);
312 if (endptr == NULL || *endptr != '\0' ||
313 svcport == 0 || svcport >= IPPORT_MAX)
315 svcport_str = strdup(optarg);
320 hosts_bak = realloc(hosts, nhosts * sizeof(char *));
321 if (hosts_bak == NULL) {
323 for (k = 0; k < nhosts; k++)
330 hosts[nhosts - 1] = strdup(optarg);
331 if (hosts[nhosts - 1] == NULL) {
332 for (k = 0; k < (nhosts - 1); k++)
343 grphead = (struct grouplist *)NULL;
344 exphead = (struct exportlist *)NULL;
345 mlhead = (struct mountlist *)NULL;
349 exnames = exnames_default;
350 openlog("mountd", LOG_PID, LOG_DAEMON);
352 warnx("getting export list");
355 warnx("getting mount list");
361 signal(SIGINT, SIG_IGN);
362 signal(SIGQUIT, SIG_IGN);
364 signal(SIGHUP, huphandler);
365 signal(SIGTERM, terminate);
366 signal(SIGPIPE, SIG_IGN);
370 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
371 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
372 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
374 if (!resvport_only) {
375 if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
376 &resvport_only, sizeof(resvport_only)) != 0 &&
378 syslog(LOG_ERR, "sysctl: %m");
384 * If no hosts were specified, add a wildcard entry to bind to
385 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
389 hosts = malloc(sizeof(char**));
397 hosts_bak = realloc(hosts, (nhosts + 2) *
399 if (hosts_bak == NULL) {
400 for (k = 0; k < nhosts; k++)
407 hosts[nhosts - 2] = "::1";
409 hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
410 if (hosts_bak == NULL) {
411 for (k = 0; k < nhosts; k++)
421 hosts[nhosts - 1] = "127.0.0.1";
424 nc_handle = setnetconfig();
425 while ((nconf = getnetconfig(nc_handle))) {
426 if (nconf->nc_flag & NC_VISIBLE) {
427 if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
431 create_service(nconf);
434 endnetconfig(nc_handle);
437 syslog(LOG_ERR, "could not create any services");
441 /* Expand svc_run() here so that we can call get_exportlist(). */
448 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
452 syslog(LOG_ERR, "mountd died: select: %m");
457 svc_getreqset(&readfds);
463 * This routine creates and binds sockets on the appropriate
464 * addresses. It gets called one time for each transport and
465 * registrates the service with rpcbind on that trasport.
468 create_service(struct netconfig *nconf)
470 struct addrinfo hints, *res = NULL;
471 struct sockaddr_in *sin;
472 struct sockaddr_in6 *sin6;
473 struct __rpc_sockinfo si;
474 struct netbuf servaddr;
475 SVCXPRT *transp = NULL;
482 u_int32_t host_addr[4]; /* IPv4 or IPv6 */
484 if ((nconf->nc_semantics != NC_TPI_CLTS) &&
485 (nconf->nc_semantics != NC_TPI_COTS) &&
486 (nconf->nc_semantics != NC_TPI_COTS_ORD))
487 return; /* not my type */
490 * XXX - using RPC library internal functions.
492 if (!__rpc_nconf2sockinfo(nconf, &si)) {
493 syslog(LOG_ERR, "cannot get information for %s",
498 /* Get mountd's address on this transport */
499 memset(&hints, 0, sizeof hints);
500 hints.ai_flags = AI_PASSIVE;
501 hints.ai_family = si.si_af;
502 hints.ai_socktype = si.si_socktype;
503 hints.ai_protocol = si.si_proto;
506 * Bind to specific IPs if asked to
509 while (nhostsbak > 0) {
512 * XXX - using RPC library internal functions.
514 if ((fd = __rpc_nconf2fd(nconf)) < 0) {
516 if (errno == EPROTONOSUPPORT &&
517 nconf->nc_semantics != NC_TPI_CLTS)
520 syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
521 "cannot create socket for %s", nconf->nc_netid);
525 switch (hints.ai_family) {
527 if (inet_pton(AF_INET, hosts[nhostsbak],
529 hints.ai_flags &= AI_NUMERICHOST;
532 * Skip if we have an AF_INET6 address.
534 if (inet_pton(AF_INET6, hosts[nhostsbak],
542 if (inet_pton(AF_INET6, hosts[nhostsbak],
544 hints.ai_flags &= AI_NUMERICHOST;
547 * Skip if we have an AF_INET address.
549 if (inet_pton(AF_INET, hosts[nhostsbak],
557 * We're doing host-based access checks here, so don't
558 * allow v4-in-v6 to confuse things. The kernel will
559 * disable it by default on NFS sockets too.
561 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
564 "can't disable v4-in-v6 on IPv6 socket");
573 * If no hosts were specified, just bind to INADDR_ANY
575 if (strcmp("*", hosts[nhostsbak]) == 0) {
576 if (svcport_str == NULL) {
577 res = malloc(sizeof(struct addrinfo));
580 res->ai_flags = hints.ai_flags;
581 res->ai_family = hints.ai_family;
582 res->ai_protocol = hints.ai_protocol;
583 switch (res->ai_family) {
585 sin = malloc(sizeof(struct sockaddr_in));
588 sin->sin_family = AF_INET;
589 sin->sin_port = htons(0);
590 sin->sin_addr.s_addr = htonl(INADDR_ANY);
591 res->ai_addr = (struct sockaddr*) sin;
592 res->ai_addrlen = (socklen_t)
593 sizeof(res->ai_addr);
596 sin6 = malloc(sizeof(struct sockaddr_in6));
599 sin6->sin6_family = AF_INET6;
600 sin6->sin6_port = htons(0);
601 sin6->sin6_addr = in6addr_any;
602 res->ai_addr = (struct sockaddr*) sin6;
603 res->ai_addrlen = (socklen_t)
604 sizeof(res->ai_addr);
610 if ((aicode = getaddrinfo(NULL, svcport_str,
611 &hints, &res)) != 0) {
613 "cannot get local address for %s: %s",
615 gai_strerror(aicode));
620 if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
621 &hints, &res)) != 0) {
623 "cannot get local address for %s: %s",
624 nconf->nc_netid, gai_strerror(aicode));
629 r = bindresvport_sa(fd, res->ai_addr);
631 syslog(LOG_ERR, "bindresvport_sa: %m");
635 if (nconf->nc_semantics != NC_TPI_CLTS)
636 listen(fd, SOMAXCONN);
638 if (nconf->nc_semantics == NC_TPI_CLTS )
639 transp = svc_dg_create(fd, 0, 0);
641 transp = svc_vc_create(fd, RPC_MAXDATASIZE,
644 if (transp != (SVCXPRT *) NULL) {
645 if (!svc_reg(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,
648 "can't register %s RPCMNT_VER1 service",
651 if (!svc_reg(transp, RPCPROG_MNT, RPCMNT_VER3,
654 "can't register %s RPCMNT_VER3 service",
658 syslog(LOG_WARNING, "can't create %s services",
661 if (registered == 0) {
663 memset(&hints, 0, sizeof hints);
664 hints.ai_flags = AI_PASSIVE;
665 hints.ai_family = si.si_af;
666 hints.ai_socktype = si.si_socktype;
667 hints.ai_protocol = si.si_proto;
669 if (svcport_str == NULL) {
670 svcport_str = malloc(NI_MAXSERV * sizeof(char));
671 if (svcport_str == NULL)
674 if (getnameinfo(res->ai_addr,
675 res->ai_addr->sa_len, NULL, NI_MAXHOST,
676 svcport_str, NI_MAXSERV * sizeof(char),
677 NI_NUMERICHOST | NI_NUMERICSERV))
678 errx(1, "Cannot get port number");
681 if((aicode = getaddrinfo(NULL, svcport_str, &hints,
683 syslog(LOG_ERR, "cannot get local address: %s",
684 gai_strerror(aicode));
688 servaddr.buf = malloc(res->ai_addrlen);
689 memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
690 servaddr.len = res->ai_addrlen;
692 rpcb_set(RPCPROG_MNT, RPCMNT_VER1, nconf, &servaddr);
693 rpcb_set(RPCPROG_MNT, RPCMNT_VER3, nconf, &servaddr);
705 "usage: mountd [-2] [-d] [-l] [-n] [-p <port>] [-r] "
706 "[-h <bindip>] [export_file ...]\n");
711 * The mount rpc service
714 mntsrv(rqstp, transp)
715 struct svc_req *rqstp;
718 struct exportlist *ep;
723 char host[NI_MAXHOST], numerichost[NI_MAXHOST];
724 int lookup_failed = 1;
725 struct sockaddr *saddr;
727 char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
728 int bad = 0, defset, hostset;
729 sigset_t sighup_mask;
731 sigemptyset(&sighup_mask);
732 sigaddset(&sighup_mask, SIGHUP);
733 saddr = svc_getrpccaller(transp)->buf;
734 switch (saddr->sa_family) {
736 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
739 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
742 syslog(LOG_ERR, "request from unknown address family");
745 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
747 getnameinfo(saddr, saddr->sa_len, numerichost,
748 sizeof numerichost, NULL, 0, NI_NUMERICHOST);
749 switch (rqstp->rq_proc) {
751 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
752 syslog(LOG_ERR, "can't send reply");
755 if (sport >= IPPORT_RESERVED && resvport_only) {
757 "mount request from %s from unprivileged port",
759 svcerr_weakauth(transp);
762 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
763 syslog(LOG_NOTICE, "undecodable mount request from %s",
765 svcerr_decode(transp);
770 * Get the real pathname and make sure it is a directory
771 * or a regular file if the -r option was specified
774 if (realpath(rpcpath, dirpath) == NULL ||
775 stat(dirpath, &stb) < 0 ||
776 (!S_ISDIR(stb.st_mode) &&
777 (dir_only || !S_ISREG(stb.st_mode))) ||
778 statfs(dirpath, &fsb) < 0) {
779 chdir("/"); /* Just in case realpath doesn't */
781 "mount request from %s for non existent path %s",
782 numerichost, dirpath);
784 warnx("stat failed on %s", dirpath);
785 bad = ENOENT; /* We will send error reply later */
788 /* Check in the exports list */
789 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
790 ep = ex_search(&fsb.f_fsid);
791 hostset = defset = 0;
792 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
793 ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
794 chk_host(dp, saddr, &defset, &hostset)) ||
795 (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
796 scan_tree(ep->ex_dirl, saddr) == 0))) {
798 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
800 syslog(LOG_ERR, "can't send reply");
801 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
804 if (hostset & DP_HOSTSET)
805 fhr.fhr_flag = hostset;
807 fhr.fhr_flag = defset;
808 fhr.fhr_vers = rqstp->rq_vers;
809 /* Get the file handle */
810 memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
811 if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
813 syslog(LOG_ERR, "can't get fh for %s", dirpath);
814 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
816 syslog(LOG_ERR, "can't send reply");
817 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
820 if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
822 syslog(LOG_ERR, "can't send reply");
824 add_mlist(host, dirpath);
826 add_mlist(numerichost, dirpath);
828 warnx("mount successful");
831 "mount request succeeded from %s for %s",
832 numerichost, dirpath);
836 "mount request denied from %s for %s",
837 numerichost, dirpath);
840 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
842 syslog(LOG_ERR, "can't send reply");
843 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
846 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
847 syslog(LOG_ERR, "can't send reply");
850 "dump request succeeded from %s",
854 if (sport >= IPPORT_RESERVED && resvport_only) {
856 "umount request from %s from unprivileged port",
858 svcerr_weakauth(transp);
861 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
862 syslog(LOG_NOTICE, "undecodable umount request from %s",
864 svcerr_decode(transp);
867 if (realpath(rpcpath, dirpath) == NULL) {
868 syslog(LOG_NOTICE, "umount request from %s "
869 "for non existent path %s",
870 numerichost, dirpath);
872 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
873 syslog(LOG_ERR, "can't send reply");
875 del_mlist(host, dirpath);
876 del_mlist(numerichost, dirpath);
879 "umount request succeeded from %s for %s",
880 numerichost, dirpath);
883 if (sport >= IPPORT_RESERVED && resvport_only) {
885 "umountall request from %s from unprivileged port",
887 svcerr_weakauth(transp);
890 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
891 syslog(LOG_ERR, "can't send reply");
893 del_mlist(host, NULL);
894 del_mlist(numerichost, NULL);
897 "umountall request succeeded from %s",
901 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
902 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
904 syslog(LOG_ERR, "can't send reply");
907 "export request succeeded from %s",
911 svcerr_noproc(transp);
917 * Xdr conversion for a dirpath string
924 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
928 * Xdr routine to generate file handle reply
935 struct fhreturn *fhrp = (struct fhreturn *)cp;
936 u_long ok = 0, len, auth;
938 if (!xdr_long(xdrsp, &ok))
940 switch (fhrp->fhr_vers) {
942 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
945 if (!xdr_long(xdrsp, &len))
947 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
951 if (!xdr_long(xdrsp, &len))
953 return (xdr_long(xdrsp, &auth));
963 struct mountlist *mlp;
970 if (!xdr_bool(xdrsp, &true))
972 strp = &mlp->ml_host[0];
973 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
975 strp = &mlp->ml_dirp[0];
976 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
980 if (!xdr_bool(xdrsp, &false))
986 * Xdr conversion for export list
989 xdr_explist_common(xdrsp, cp, brief)
994 struct exportlist *ep;
997 sigset_t sighup_mask;
999 sigemptyset(&sighup_mask);
1000 sigaddset(&sighup_mask, SIGHUP);
1001 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1005 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1008 if (ep->ex_defdir && putdef == 0 &&
1009 put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1014 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1015 if (!xdr_bool(xdrsp, &false))
1019 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1024 * Called from xdr_explist() to traverse the tree and export the
1028 put_exlist(dp, xdrsp, adp, putdefp, brief)
1031 struct dirlist *adp;
1035 struct grouplist *grp;
1036 struct hostlist *hp;
1043 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1045 if (!xdr_bool(xdrsp, &true))
1048 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
1050 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1055 if (!xdr_bool(xdrsp, &true))
1058 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
1060 } else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1061 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1065 if (grp->gr_type == GT_HOST) {
1066 if (!xdr_bool(xdrsp, &true))
1068 strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1069 if (!xdr_string(xdrsp, &strp,
1072 } else if (grp->gr_type == GT_NET) {
1073 if (!xdr_bool(xdrsp, &true))
1075 strp = grp->gr_ptr.gt_net.nt_name;
1076 if (!xdr_string(xdrsp, &strp,
1081 if (gotalldir && hp == (struct hostlist *)NULL) {
1087 if (!xdr_bool(xdrsp, &false))
1089 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1096 xdr_explist(xdrsp, cp)
1101 return xdr_explist_common(xdrsp, cp, 0);
1105 xdr_explist_brief(xdrsp, cp)
1110 return xdr_explist_common(xdrsp, cp, 1);
1118 * Get the export list from one, currently open file
1121 get_exportlist_one()
1123 struct exportlist *ep, *ep2;
1124 struct grouplist *grp, *tgrp;
1125 struct exportlist **epp;
1126 struct dirlist *dirhead;
1129 char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1130 int len, has_host, exflags, got_nondir, dirplen, netgrp;
1132 dirhead = (struct dirlist *)NULL;
1133 while (get_line()) {
1135 warnx("got line %s", line);
1137 nextfield(&cp, &endcp);
1146 exflags = MNT_EXPORTED;
1149 ep = (struct exportlist *)NULL;
1152 * Create new exports list entry
1155 tgrp = grp = get_grp();
1157 if (len > RPCMNT_NAMELEN) {
1158 getexp_err(ep, tgrp);
1162 if (ep == (struct exportlist *)NULL) {
1163 getexp_err(ep, tgrp);
1167 warnx("doing opt %s", cp);
1169 if (do_opt(&cp, &endcp, ep, grp, &has_host,
1171 getexp_err(ep, tgrp);
1174 } else if (*cp == '/') {
1177 if (check_dirpath(cp) &&
1178 statfs(cp, &fsb) >= 0) {
1180 syslog(LOG_ERR, "dirs must be first");
1181 getexp_err(ep, tgrp);
1185 if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1186 ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1187 getexp_err(ep, tgrp);
1192 * See if this directory is already
1195 ep = ex_search(&fsb.f_fsid);
1196 if (ep == (struct exportlist *)NULL) {
1198 ep->ex_fs = fsb.f_fsid;
1199 ep->ex_fsdir = (char *)
1200 malloc(strlen(fsb.f_mntonname) + 1);
1202 strcpy(ep->ex_fsdir,
1207 warnx("making new ep fs=0x%x,0x%x",
1211 warnx("found ep fs=0x%x,0x%x",
1217 * Add dirpath to export mount point.
1219 dirp = add_expdir(&dirhead, cp, len);
1222 getexp_err(ep, tgrp);
1230 if (ep == (struct exportlist *)NULL) {
1231 getexp_err(ep, tgrp);
1236 * Get the host or netgroup.
1239 netgrp = getnetgrent(&hst, &usr, &dom);
1242 grp->gr_next = get_grp();
1248 "null hostname in netgroup %s, skipping", cp);
1249 grp->gr_type = GT_IGNORE;
1250 } else if (get_host(hst, grp, tgrp)) {
1252 "bad host %s in netgroup %s, skipping", hst, cp);
1253 grp->gr_type = GT_IGNORE;
1255 } else if (get_host(cp, grp, tgrp)) {
1256 syslog(LOG_ERR, "bad host %s, skipping", cp);
1257 grp->gr_type = GT_IGNORE;
1260 } while (netgrp && getnetgrent(&hst, &usr, &dom));
1265 nextfield(&cp, &endcp);
1268 if (check_options(dirhead)) {
1269 getexp_err(ep, tgrp);
1273 grp->gr_type = GT_DEFAULT;
1275 warnx("adding a default entry");
1278 * Don't allow a network export coincide with a list of
1279 * host(s) on the same line.
1281 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1282 syslog(LOG_ERR, "network/host conflict");
1283 getexp_err(ep, tgrp);
1287 * If an export list was specified on this line, make sure
1288 * that we have at least one valid entry, otherwise skip it.
1292 while (grp && grp->gr_type == GT_IGNORE)
1295 getexp_err(ep, tgrp);
1301 * Loop through hosts, pushing the exports into the kernel.
1302 * After loop, tgrp points to the start of the list and
1303 * grp points to the last entry in the list.
1307 if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1309 getexp_err(ep, tgrp);
1312 } while (grp->gr_next && (grp = grp->gr_next));
1315 * Success. Update the data structures.
1318 hang_dirp(dirhead, tgrp, ep, opt_flags);
1319 grp->gr_next = grphead;
1322 hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1326 dirhead = (struct dirlist *)NULL;
1327 if ((ep->ex_flag & EX_LINKED) == 0) {
1332 * Insert in the list in alphabetical order.
1334 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1335 epp = &ep2->ex_next;
1341 ep->ex_flag |= EX_LINKED;
1346 dirhead = (struct dirlist *)NULL;
1352 * Get the export list from all specified files
1357 struct exportlist *ep, *ep2;
1358 struct grouplist *grp, *tgrp;
1359 struct export_args export;
1361 struct statfs *fsp, *mntbufp;
1362 struct xvfsconf vfc;
1365 int dirplen, num, i;
1369 bzero(&export, sizeof(export));
1370 export.ex_flags = MNT_DELEXPORT;
1375 bzero(errmsg, sizeof(errmsg));
1378 * First, get rid of the old list
1386 exphead = (struct exportlist *)NULL;
1394 grphead = (struct grouplist *)NULL;
1397 * And delete exports that are in the kernel for all local
1399 * XXX: Should know how to handle all local exportable filesystems.
1401 num = getmntinfo(&mntbufp, MNT_NOWAIT);
1404 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1405 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1406 build_iovec(&iov, &iovlen, "from", NULL, 0);
1407 build_iovec(&iov, &iovlen, "update", NULL, 0);
1408 build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1409 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1412 for (i = 0; i < num; i++) {
1414 if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1415 syslog(LOG_ERR, "getvfsbyname() failed for %s",
1421 * Do not delete export for network filesystem by
1422 * passing "export" arg to nmount().
1423 * It only makes sense to do this for local filesystems.
1425 if (vfc.vfc_flags & VFCF_NETWORK)
1428 iov[1].iov_base = fsp->f_fstypename;
1429 iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1430 iov[3].iov_base = fsp->f_mntonname;
1431 iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1432 iov[5].iov_base = fsp->f_mntfromname;
1433 iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1435 if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1436 errno != ENOENT && errno != ENOTSUP) {
1438 "can't delete exports for %s: %m %s",
1439 fsp->f_mntonname, errmsg);
1444 /* Free strings allocated by strdup() in getmntopts.c */
1445 free(iov[0].iov_base); /* fstype */
1446 free(iov[2].iov_base); /* fspath */
1447 free(iov[4].iov_base); /* from */
1448 free(iov[6].iov_base); /* update */
1449 free(iov[8].iov_base); /* export */
1450 free(iov[10].iov_base); /* errmsg */
1452 /* free iov, allocated by realloc() */
1458 * Read in the exports file and build the list, calling
1459 * nmount() as we go along to push the export rules into the kernel.
1462 for (i = 0; exnames[i] != NULL; i++) {
1464 warnx("reading exports from %s", exnames[i]);
1465 if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1466 syslog(LOG_WARNING, "can't open %s", exnames[i]);
1469 get_exportlist_one();
1474 syslog(LOG_ERR, "can't open any exports file");
1480 * Allocate an export list element
1485 struct exportlist *ep;
1487 ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1488 if (ep == (struct exportlist *)NULL)
1490 memset(ep, 0, sizeof(struct exportlist));
1495 * Allocate a group list element
1500 struct grouplist *gp;
1502 gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1503 if (gp == (struct grouplist *)NULL)
1505 memset(gp, 0, sizeof(struct grouplist));
1510 * Clean up upon an error in get_exportlist().
1514 struct exportlist *ep;
1515 struct grouplist *grp;
1517 struct grouplist *tgrp;
1519 if (!(opt_flags & OP_QUIET))
1520 syslog(LOG_ERR, "bad exports list line %s", line);
1521 if (ep && (ep->ex_flag & EX_LINKED) == 0)
1531 * Search the export list for a matching fs.
1537 struct exportlist *ep;
1541 if (ep->ex_fs.val[0] == fsid->val[0] &&
1542 ep->ex_fs.val[1] == fsid->val[1])
1550 * Add a directory path to the list.
1553 add_expdir(dpp, cp, len)
1554 struct dirlist **dpp;
1560 dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1561 if (dp == (struct dirlist *)NULL)
1564 dp->dp_right = (struct dirlist *)NULL;
1566 dp->dp_hosts = (struct hostlist *)NULL;
1567 strcpy(dp->dp_dirp, cp);
1569 return (dp->dp_dirp);
1573 * Hang the dir list element off the dirpath binary tree as required
1574 * and update the entry for host.
1577 hang_dirp(dp, grp, ep, flags)
1579 struct grouplist *grp;
1580 struct exportlist *ep;
1583 struct hostlist *hp;
1584 struct dirlist *dp2;
1586 if (flags & OP_ALLDIRS) {
1591 if (grp == (struct grouplist *)NULL) {
1592 ep->ex_defdir->dp_flag |= DP_DEFSET;
1593 } else while (grp) {
1596 hp->ht_next = ep->ex_defdir->dp_hosts;
1597 ep->ex_defdir->dp_hosts = hp;
1603 * Loop through the directories adding them to the tree.
1607 add_dlist(&ep->ex_dirl, dp, grp, flags);
1614 * Traverse the binary tree either updating a node that is already there
1615 * for the new directory or adding the new node.
1618 add_dlist(dpp, newdp, grp, flags)
1619 struct dirlist **dpp;
1620 struct dirlist *newdp;
1621 struct grouplist *grp;
1625 struct hostlist *hp;
1630 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1632 add_dlist(&dp->dp_left, newdp, grp, flags);
1634 } else if (cmp < 0) {
1635 add_dlist(&dp->dp_right, newdp, grp, flags);
1638 free((caddr_t)newdp);
1641 dp->dp_left = (struct dirlist *)NULL;
1647 * Hang all of the host(s) off of the directory point.
1652 hp->ht_next = dp->dp_hosts;
1657 dp->dp_flag |= DP_DEFSET;
1662 * Search for a dirpath on the export point.
1665 dirp_search(dp, dirp)
1672 cmp = strcmp(dp->dp_dirp, dirp);
1674 return (dirp_search(dp->dp_left, dirp));
1676 return (dirp_search(dp->dp_right, dirp));
1684 * Scan for a host match in a directory tree.
1687 chk_host(dp, saddr, defsetp, hostsetp)
1689 struct sockaddr *saddr;
1693 struct hostlist *hp;
1694 struct grouplist *grp;
1695 struct addrinfo *ai;
1698 if (dp->dp_flag & DP_DEFSET)
1699 *defsetp = dp->dp_flag;
1703 switch (grp->gr_type) {
1705 ai = grp->gr_ptr.gt_addrinfo;
1706 for (; ai; ai = ai->ai_next) {
1707 if (!sacmp(ai->ai_addr, saddr, NULL)) {
1709 (hp->ht_flag | DP_HOSTSET);
1715 if (!sacmp(saddr, (struct sockaddr *)
1716 &grp->gr_ptr.gt_net.nt_net,
1718 &grp->gr_ptr.gt_net.nt_mask)) {
1719 *hostsetp = (hp->ht_flag | DP_HOSTSET);
1731 * Scan tree for a host that matches the address.
1734 scan_tree(dp, saddr)
1736 struct sockaddr *saddr;
1738 int defset, hostset;
1741 if (scan_tree(dp->dp_left, saddr))
1743 if (chk_host(dp, saddr, &defset, &hostset))
1745 if (scan_tree(dp->dp_right, saddr))
1752 * Traverse the dirlist tree and free it up.
1760 free_dir(dp->dp_left);
1761 free_dir(dp->dp_right);
1762 free_host(dp->dp_hosts);
1768 * Parse the option string and update fields.
1769 * Option arguments may either be -<option>=<value> or
1773 do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1774 char **cpp, **endcpp;
1775 struct exportlist *ep;
1776 struct grouplist *grp;
1781 char *cpoptarg, *cpoptend;
1782 char *cp, *endcp, *cpopt, savedc, savedc2;
1783 int allflag, usedarg;
1791 while (cpopt && *cpopt) {
1794 if ((cpoptend = strchr(cpopt, ','))) {
1796 if ((cpoptarg = strchr(cpopt, '=')))
1799 if ((cpoptarg = strchr(cpopt, '=')))
1803 nextfield(&cp, &endcp);
1805 if (endcp > cp && *cp != '-') {
1813 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1814 *exflagsp |= MNT_EXRDONLY;
1815 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1816 !(allflag = strcmp(cpopt, "mapall")) ||
1817 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1819 parsecred(cpoptarg, cr);
1821 *exflagsp |= MNT_EXPORTANON;
1822 opt_flags |= OP_MAPALL;
1824 opt_flags |= OP_MAPROOT;
1825 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1826 !strcmp(cpopt, "m"))) {
1827 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1828 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1832 opt_flags |= OP_MASK;
1833 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1834 !strcmp(cpopt, "n"))) {
1835 if (strchr(cpoptarg, '/') != NULL) {
1837 fprintf(stderr, "setting OP_MASKLEN\n");
1838 opt_flags |= OP_MASKLEN;
1840 if (grp->gr_type != GT_NULL) {
1841 syslog(LOG_ERR, "network/host conflict");
1843 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1844 syslog(LOG_ERR, "bad net: %s", cpoptarg);
1847 grp->gr_type = GT_NET;
1850 opt_flags |= OP_NET;
1851 } else if (!strcmp(cpopt, "alldirs")) {
1852 opt_flags |= OP_ALLDIRS;
1853 } else if (!strcmp(cpopt, "public")) {
1854 *exflagsp |= MNT_EXPUBLIC;
1855 } else if (!strcmp(cpopt, "webnfs")) {
1856 *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1857 opt_flags |= OP_MAPALL;
1858 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1859 ep->ex_indexfile = strdup(cpoptarg);
1860 } else if (!strcmp(cpopt, "quiet")) {
1861 opt_flags |= OP_QUIET;
1863 syslog(LOG_ERR, "bad opt %s", cpopt);
1882 * Translate a character string to the corresponding list of network
1883 * addresses for a hostname.
1886 get_host(cp, grp, tgrp)
1888 struct grouplist *grp;
1889 struct grouplist *tgrp;
1891 struct grouplist *checkgrp;
1892 struct addrinfo *ai, *tai, hints;
1894 char host[NI_MAXHOST];
1896 if (grp->gr_type != GT_NULL) {
1897 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
1900 memset(&hints, 0, sizeof hints);
1901 hints.ai_flags = AI_CANONNAME;
1902 hints.ai_protocol = IPPROTO_UDP;
1903 ecode = getaddrinfo(cp, NULL, &hints, &ai);
1905 syslog(LOG_ERR,"can't get address info for host %s", cp);
1908 grp->gr_ptr.gt_addrinfo = ai;
1909 while (ai != NULL) {
1910 if (ai->ai_canonname == NULL) {
1911 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1912 sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
1913 strlcpy(host, "?", sizeof(host));
1914 ai->ai_canonname = strdup(host);
1915 ai->ai_flags |= AI_CANONNAME;
1918 fprintf(stderr, "got host %s\n", ai->ai_canonname);
1920 * Sanity check: make sure we don't already have an entry
1921 * for this host in the grouplist.
1923 for (checkgrp = tgrp; checkgrp != NULL;
1924 checkgrp = checkgrp->gr_next) {
1925 if (checkgrp->gr_type != GT_HOST)
1927 for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
1928 tai = tai->ai_next) {
1929 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
1933 "ignoring duplicate host %s\n",
1935 grp->gr_type = GT_IGNORE;
1941 grp->gr_type = GT_HOST;
1946 * Free up an exports list component
1950 struct exportlist *ep;
1953 if (ep->ex_defdir) {
1954 free_host(ep->ex_defdir->dp_hosts);
1955 free((caddr_t)ep->ex_defdir);
1959 if (ep->ex_indexfile)
1960 free(ep->ex_indexfile);
1961 free_dir(ep->ex_dirl);
1970 struct hostlist *hp;
1972 struct hostlist *hp2;
1984 struct hostlist *hp;
1986 hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1987 if (hp == (struct hostlist *)NULL)
1989 hp->ht_next = (struct hostlist *)NULL;
1995 * Out of memory, fatal
2001 syslog(LOG_ERR, "out of memory");
2006 * Do the nmount() syscall with the update flag to push the export info into
2010 do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2011 struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2014 struct addrinfo *ai;
2015 struct export_args eap;
2030 bzero(&eap, sizeof(eap));
2031 bzero(errmsg, sizeof(errmsg));
2032 eap.ex_flags = exflags;
2033 eap.ex_anon = *anoncrp;
2034 eap.ex_indexfile = ep->ex_indexfile;
2035 if (grp->gr_type == GT_HOST)
2036 ai = grp->gr_ptr.gt_addrinfo;
2041 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2042 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2043 build_iovec(&iov, &iovlen, "from", NULL, 0);
2044 build_iovec(&iov, &iovlen, "update", NULL, 0);
2045 build_iovec(&iov, &iovlen, "export", &eap, sizeof(eap));
2046 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2049 switch (grp->gr_type) {
2051 if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2053 eap.ex_addr = ai->ai_addr;
2054 eap.ex_addrlen = ai->ai_addrlen;
2058 if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2062 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2064 ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2066 (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2067 eap.ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2080 syslog(LOG_ERR, "bad grouptype");
2089 * Maybe I should just use the fsb->f_mntonname path instead
2090 * of looping back up the dirp to the mount point??
2091 * Also, needs to know how to export all types of local
2092 * exportable filesystems and not just "ufs".
2094 iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2095 iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2096 iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2097 iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2098 iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2099 iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2101 while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2105 cp = dirp + dirplen - 1;
2106 if (opt_flags & OP_QUIET) {
2110 if (errno == EPERM) {
2112 warnx("can't change attributes for %s",
2115 "can't change attributes for %s", dirp);
2119 if (opt_flags & OP_ALLDIRS) {
2120 if (errno == EINVAL)
2122 "-alldirs requested but %s is not a filesystem mountpoint",
2126 "could not remount %s: %m",
2131 /* back up over the last component */
2132 while (*cp == '/' && cp > dirp)
2134 while (*(cp - 1) != '/' && cp > dirp)
2138 warnx("mnt unsucc");
2139 syslog(LOG_ERR, "can't export %s %s", dirp,
2146 /* Check that we're still on the same filesystem. */
2147 if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
2148 &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
2150 syslog(LOG_ERR, "can't export %s %s", dirp,
2165 /* free strings allocated by strdup() in getmntopts.c */
2167 free(iov[0].iov_base); /* fstype */
2168 free(iov[2].iov_base); /* fspath */
2169 free(iov[4].iov_base); /* from */
2170 free(iov[6].iov_base); /* update */
2171 free(iov[8].iov_base); /* export */
2172 free(iov[10].iov_base); /* errmsg */
2174 /* free iov, allocated by realloc() */
2181 * Translate a net address.
2183 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2186 get_net(cp, net, maskflg)
2191 struct netent *np = NULL;
2192 char *name, *p, *prefp;
2193 struct sockaddr_in sin;
2194 struct sockaddr *sa = NULL;
2195 struct addrinfo hints, *ai = NULL;
2196 char netname[NI_MAXHOST];
2200 if ((opt_flags & OP_MASKLEN) && !maskflg) {
2201 p = strchr(cp, '/');
2207 * Check for a numeric address first. We wish to avoid
2208 * possible DNS lookups in getnetbyname().
2210 if (isxdigit(*cp) || *cp == ':') {
2211 memset(&hints, 0, sizeof hints);
2212 /* Ensure the mask and the network have the same family. */
2213 if (maskflg && (opt_flags & OP_NET))
2214 hints.ai_family = net->nt_net.ss_family;
2215 else if (!maskflg && (opt_flags & OP_HAVEMASK))
2216 hints.ai_family = net->nt_mask.ss_family;
2218 hints.ai_family = AF_UNSPEC;
2219 hints.ai_flags = AI_NUMERICHOST;
2220 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2222 if (sa != NULL && ai->ai_family == AF_INET) {
2224 * The address in `cp' is really a network address, so
2225 * use inet_network() to re-interpret this correctly.
2226 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2228 bzero(&sin, sizeof sin);
2229 sin.sin_family = AF_INET;
2230 sin.sin_len = sizeof sin;
2231 sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2233 fprintf(stderr, "get_net: v4 addr %s\n",
2234 inet_ntoa(sin.sin_addr));
2235 sa = (struct sockaddr *)&sin;
2238 if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2239 bzero(&sin, sizeof sin);
2240 sin.sin_family = AF_INET;
2241 sin.sin_len = sizeof sin;
2242 sin.sin_addr = inet_makeaddr(np->n_net, 0);
2243 sa = (struct sockaddr *)&sin;
2249 /* The specified sockaddr is a mask. */
2250 if (checkmask(sa) != 0)
2252 bcopy(sa, &net->nt_mask, sa->sa_len);
2253 opt_flags |= OP_HAVEMASK;
2255 /* The specified sockaddr is a network address. */
2256 bcopy(sa, &net->nt_net, sa->sa_len);
2258 /* Get a network name for the export list. */
2261 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2262 NULL, 0, NI_NUMERICHOST) == 0) {
2267 if ((net->nt_name = strdup(name)) == NULL)
2271 * Extract a mask from either a "/<masklen>" suffix, or
2272 * from the class of an IPv4 address.
2274 if (opt_flags & OP_MASKLEN) {
2275 preflen = strtol(prefp, NULL, 10);
2276 if (preflen < 0L || preflen == LONG_MAX)
2278 bcopy(sa, &net->nt_mask, sa->sa_len);
2279 if (makemask(&net->nt_mask, (int)preflen) != 0)
2281 opt_flags |= OP_HAVEMASK;
2283 } else if (sa->sa_family == AF_INET &&
2284 (opt_flags & OP_MASK) == 0) {
2287 addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2288 if (IN_CLASSA(addr))
2290 else if (IN_CLASSB(addr))
2292 else if (IN_CLASSC(addr))
2294 else if (IN_CLASSD(addr))
2297 preflen = 32; /* XXX */
2299 bcopy(sa, &net->nt_mask, sa->sa_len);
2300 makemask(&net->nt_mask, (int)preflen);
2301 opt_flags |= OP_HAVEMASK;
2316 * Parse out the next white space separated field
2319 nextfield(cp, endcp)
2326 while (*p == ' ' || *p == '\t')
2328 if (*p == '\n' || *p == '\0')
2332 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2339 * Get an exports file line. Skip over blank lines and handle line
2347 int totlen, cont_line;
2350 * Loop around ignoring blank lines and getting all continuation lines.
2355 if ((p = fgetln(exp_file, &len)) == NULL)
2360 (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2370 if (linesize < len + totlen + 1) {
2371 linesize = len + totlen + 1;
2372 line = realloc(line, linesize);
2376 memcpy(line + totlen, p, len);
2378 line[totlen] = '\0';
2379 } while (totlen == 0 || cont_line);
2384 * Parse a description of a credential.
2387 parsecred(namelist, cr)
2396 gid_t groups[NGROUPS + 1];
2399 cr->cr_version = XUCRED_VERSION;
2401 * Set up the unprivileged user.
2404 cr->cr_groups[0] = -2;
2407 * Get the user's password table entry.
2409 names = strsep(&namelist, " \t\n");
2410 name = strsep(&names, ":");
2411 if (isdigit(*name) || *name == '-')
2412 pw = getpwuid(atoi(name));
2414 pw = getpwnam(name);
2416 * Credentials specified as those of a user.
2418 if (names == NULL) {
2420 syslog(LOG_ERR, "unknown user: %s", name);
2423 cr->cr_uid = pw->pw_uid;
2424 ngroups = NGROUPS + 1;
2425 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2426 syslog(LOG_ERR, "too many groups");
2428 * Compress out duplicate.
2430 cr->cr_ngroups = ngroups - 1;
2431 cr->cr_groups[0] = groups[0];
2432 for (cnt = 2; cnt < ngroups; cnt++)
2433 cr->cr_groups[cnt - 1] = groups[cnt];
2437 * Explicit credential specified as a colon separated list:
2441 cr->cr_uid = pw->pw_uid;
2442 else if (isdigit(*name) || *name == '-')
2443 cr->cr_uid = atoi(name);
2445 syslog(LOG_ERR, "unknown user: %s", name);
2449 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2450 name = strsep(&names, ":");
2451 if (isdigit(*name) || *name == '-') {
2452 cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2454 if ((gr = getgrnam(name)) == NULL) {
2455 syslog(LOG_ERR, "unknown group: %s", name);
2458 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2461 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2462 syslog(LOG_ERR, "too many groups");
2465 #define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2467 * Routines that maintain the remote mounttab
2472 struct mountlist *mlp, **mlpp;
2473 char *host, *dirp, *cp;
2477 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2478 if (errno == ENOENT)
2481 syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2486 while (fgets(str, STRSIZ, mlfile) != NULL) {
2488 host = strsep(&cp, " \t\n");
2489 dirp = strsep(&cp, " \t\n");
2490 if (host == NULL || dirp == NULL)
2492 mlp = (struct mountlist *)malloc(sizeof (*mlp));
2493 if (mlp == (struct mountlist *)NULL)
2495 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2496 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2497 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2498 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2499 mlp->ml_next = (struct mountlist *)NULL;
2501 mlpp = &mlp->ml_next;
2507 del_mlist(char *hostp, char *dirp)
2509 struct mountlist *mlp, **mlpp;
2510 struct mountlist *mlp2;
2517 if (!strcmp(mlp->ml_host, hostp) &&
2518 (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2521 *mlpp = mlp = mlp->ml_next;
2522 free((caddr_t)mlp2);
2524 mlpp = &mlp->ml_next;
2529 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2530 syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2535 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2543 add_mlist(hostp, dirp)
2546 struct mountlist *mlp, **mlpp;
2552 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2554 mlpp = &mlp->ml_next;
2557 mlp = (struct mountlist *)malloc(sizeof (*mlp));
2558 if (mlp == (struct mountlist *)NULL)
2560 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2561 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2562 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2563 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2564 mlp->ml_next = (struct mountlist *)NULL;
2566 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2567 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2570 fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2575 * Free up a group list.
2579 struct grouplist *grp;
2581 if (grp->gr_type == GT_HOST) {
2582 if (grp->gr_ptr.gt_addrinfo != NULL)
2583 freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2584 } else if (grp->gr_type == GT_NET) {
2585 if (grp->gr_ptr.gt_net.nt_name)
2586 free(grp->gr_ptr.gt_net.nt_name);
2593 SYSLOG(int pri, const char *fmt, ...)
2598 vfprintf(stderr, fmt, ap);
2604 * Check options for consistency.
2611 if (dp == (struct dirlist *)NULL)
2613 if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2614 syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2617 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2618 syslog(LOG_ERR, "-mask requires -network");
2621 if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2622 syslog(LOG_ERR, "-network requires mask specification");
2625 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
2626 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
2629 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2630 syslog(LOG_ERR, "-alldirs has multiple directories");
2637 * Check an absolute directory path for any symbolic links. Return true
2648 while (*cp && ret) {
2651 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2657 if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2663 * Make a netmask according to the specified prefix length. The ss_family
2664 * and other non-address fields must be initialised before calling this.
2667 makemask(struct sockaddr_storage *ssp, int bitlen)
2672 if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
2674 if (bitlen > len * CHAR_BIT)
2677 for (i = 0; i < len; i++) {
2678 bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
2679 *p++ = (1 << bits) - 1;
2686 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
2687 * is acceptable (i.e. of the form 1...10....0).
2690 checkmask(struct sockaddr *sa)
2695 if ((mask = sa_rawaddr(sa, &len)) == NULL)
2698 for (i = 0; i < len; i++)
2699 if (mask[i] != 0xff)
2702 if (~mask[i] & (u_char)(~mask[i] + 1))
2706 for (; i < len; i++)
2713 * Compare two sockaddrs according to a specified mask. Return zero if
2714 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
2715 * If samask is NULL, perform a full comparision.
2718 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
2720 unsigned char *p1, *p2, *mask;
2723 if (sa1->sa_family != sa2->sa_family ||
2724 (p1 = sa_rawaddr(sa1, &len)) == NULL ||
2725 (p2 = sa_rawaddr(sa2, NULL)) == NULL)
2728 switch (sa1->sa_family) {
2730 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
2731 ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
2736 /* Simple binary comparison if no mask specified. */
2738 return (memcmp(p1, p2, len));
2740 /* Set up the mask, and do a mask-based comparison. */
2741 if (sa1->sa_family != samask->sa_family ||
2742 (mask = sa_rawaddr(samask, NULL)) == NULL)
2745 for (i = 0; i < len; i++)
2746 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
2752 * Return a pointer to the part of the sockaddr that contains the
2753 * raw address, and set *nbytes to its length in bytes. Returns
2754 * NULL if the address family is unknown.
2757 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
2761 switch (sa->sa_family) {
2763 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
2764 p = &((struct sockaddr_in *)sa)->sin_addr;
2767 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
2768 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
2789 pidfile_remove(pfh);
2790 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
2791 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);