xref: /freebsd/usr.sbin/mountd/mountd.c (revision 2546665afcaf0d53dc2c7058fee96354b3680f5a)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Herb Hasler and Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
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  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /*not lint*/
42 
43 #if 0
44 #ifndef lint
45 static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
46 #endif /*not lint*/
47 #endif
48 
49 #include <sys/cdefs.h>
50 __FBSDID("$FreeBSD$");
51 
52 #include <sys/param.h>
53 #include <sys/mount.h>
54 #include <sys/fcntl.h>
55 #include <sys/stat.h>
56 #include <sys/syslog.h>
57 #include <sys/sysctl.h>
58 #include <sys/linker.h>
59 #include <sys/module.h>
60 
61 #include <rpc/rpc.h>
62 #include <rpc/rpc_com.h>
63 #include <rpc/pmap_clnt.h>
64 #include <rpc/pmap_prot.h>
65 #include <rpcsvc/mount.h>
66 #include <nfs/rpcv2.h>
67 #include <nfs/nfsproto.h>
68 #include <nfsserver/nfs.h>
69 #include <ufs/ufs/ufsmount.h>
70 #include <fs/msdosfs/msdosfsmount.h>
71 #include <fs/ntfs/ntfsmount.h>
72 #include <isofs/cd9660/cd9660_mount.h>	/* XXX need isofs in include */
73 
74 #include <arpa/inet.h>
75 
76 #include <ctype.h>
77 #include <err.h>
78 #include <errno.h>
79 #include <grp.h>
80 #include <limits.h>
81 #include <netdb.h>
82 #include <pwd.h>
83 #include <signal.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include "pathnames.h"
89 
90 #ifdef DEBUG
91 #include <stdarg.h>
92 #endif
93 
94 #ifndef MOUNTDLOCK
95 #define MOUNTDLOCK "/var/run/mountd.lock"
96 #endif
97 
98 /*
99  * Structures for keeping the mount list and export list
100  */
101 struct mountlist {
102 	struct mountlist *ml_next;
103 	char	ml_host[RPCMNT_NAMELEN+1];
104 	char	ml_dirp[RPCMNT_PATHLEN+1];
105 };
106 
107 struct dirlist {
108 	struct dirlist	*dp_left;
109 	struct dirlist	*dp_right;
110 	int		dp_flag;
111 	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
112 	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
113 };
114 /* dp_flag bits */
115 #define	DP_DEFSET	0x1
116 #define DP_HOSTSET	0x2
117 
118 struct exportlist {
119 	struct exportlist *ex_next;
120 	struct dirlist	*ex_dirl;
121 	struct dirlist	*ex_defdir;
122 	int		ex_flag;
123 	fsid_t		ex_fs;
124 	char		*ex_fsdir;
125 	char		*ex_indexfile;
126 };
127 /* ex_flag bits */
128 #define	EX_LINKED	0x1
129 
130 struct netmsk {
131 	struct sockaddr_storage nt_net;
132 	struct sockaddr_storage nt_mask;
133 	char		*nt_name;
134 };
135 
136 union grouptypes {
137 	struct addrinfo *gt_addrinfo;
138 	struct netmsk	gt_net;
139 };
140 
141 struct grouplist {
142 	int gr_type;
143 	union grouptypes gr_ptr;
144 	struct grouplist *gr_next;
145 };
146 /* Group types */
147 #define	GT_NULL		0x0
148 #define	GT_HOST		0x1
149 #define	GT_NET		0x2
150 #define	GT_DEFAULT	0x3
151 #define GT_IGNORE	0x5
152 
153 struct hostlist {
154 	int		 ht_flag;	/* Uses DP_xx bits */
155 	struct grouplist *ht_grp;
156 	struct hostlist	 *ht_next;
157 };
158 
159 struct fhreturn {
160 	int	fhr_flag;
161 	int	fhr_vers;
162 	nfsfh_t	fhr_fh;
163 };
164 
165 /* Global defs */
166 char	*add_expdir(struct dirlist **, char *, int);
167 void	add_dlist(struct dirlist **, struct dirlist *,
168 				struct grouplist *, int);
169 void	add_mlist(char *, char *);
170 int	check_dirpath(char *);
171 int	check_options(struct dirlist *);
172 int	checkmask(struct sockaddr *sa);
173 int	chk_host(struct dirlist *, struct sockaddr *, int *, int *);
174 void	del_mlist(char *hostp, char *dirp);
175 struct dirlist *dirp_search(struct dirlist *, char *);
176 int	do_mount(struct exportlist *, struct grouplist *, int,
177 		struct xucred *, char *, int, struct statfs *);
178 int	do_opt(char **, char **, struct exportlist *, struct grouplist *,
179 				int *, int *, struct xucred *);
180 struct	exportlist *ex_search(fsid_t *);
181 struct	exportlist *get_exp(void);
182 void	free_dir(struct dirlist *);
183 void	free_exp(struct exportlist *);
184 void	free_grp(struct grouplist *);
185 void	free_host(struct hostlist *);
186 void	get_exportlist(void);
187 int	get_host(char *, struct grouplist *, struct grouplist *);
188 struct hostlist *get_ht(void);
189 int	get_line(void);
190 void	get_mountlist(void);
191 int	get_net(char *, struct netmsk *, int);
192 void	getexp_err(struct exportlist *, struct grouplist *);
193 struct grouplist *get_grp(void);
194 void	hang_dirp(struct dirlist *, struct grouplist *,
195 				struct exportlist *, int);
196 void	huphandler(int sig);
197 int	makemask(struct sockaddr_storage *ssp, int bitlen);
198 void	mntsrv(struct svc_req *, SVCXPRT *);
199 void	nextfield(char **, char **);
200 void	out_of_mem(void);
201 void	parsecred(char *, struct xucred *);
202 int	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
203 void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
204 int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
205     struct sockaddr *samask);
206 int	scan_tree(struct dirlist *, struct sockaddr *);
207 static void usage(void);
208 int	xdr_dir(XDR *, char *);
209 int	xdr_explist(XDR *, caddr_t);
210 int	xdr_explist_brief(XDR *, caddr_t);
211 int	xdr_fhs(XDR *, caddr_t);
212 int	xdr_mlist(XDR *, caddr_t);
213 void	terminate(int);
214 
215 struct exportlist *exphead;
216 struct mountlist *mlhead;
217 struct grouplist *grphead;
218 char exname[MAXPATHLEN];
219 struct xucred def_anon = {
220 	XUCRED_VERSION,
221 	(uid_t)-2,
222 	1,
223 	{ (gid_t)-2 },
224 	NULL
225 };
226 int force_v2 = 0;
227 int resvport_only = 1;
228 int dir_only = 1;
229 int dolog = 0;
230 int got_sighup = 0;
231 
232 int opt_flags;
233 static int have_v6 = 1;
234 #ifdef NI_WITHSCOPEID
235 static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
236 #else
237 static const int ninumeric = NI_NUMERICHOST;
238 #endif
239 
240 int mountdlockfd;
241 /* Bits for opt_flags above */
242 #define	OP_MAPROOT	0x01
243 #define	OP_MAPALL	0x02
244 /* 0x4 free */
245 #define	OP_MASK		0x08
246 #define	OP_NET		0x10
247 #define	OP_ALLDIRS	0x40
248 #define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
249 #define	OP_QUIET	0x100
250 #define OP_MASKLEN	0x200
251 
252 #ifdef DEBUG
253 int debug = 1;
254 void	SYSLOG(int, const char *, ...) __printflike(2, 3);
255 #define syslog SYSLOG
256 #else
257 int debug = 0;
258 #endif
259 
260 /*
261  * Mountd server for NFS mount protocol as described in:
262  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
263  * The optional arguments are the exports file name
264  * default: _PATH_EXPORTS
265  * and "-n" to allow nonroot mount.
266  */
267 int
268 main(argc, argv)
269 	int argc;
270 	char **argv;
271 {
272 	fd_set readfds;
273 	struct sockaddr_in sin;
274 	struct sockaddr_in6 sin6;
275 	char *endptr;
276 	SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
277 	struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
278 	int udpsock, tcpsock, udp6sock, tcp6sock;
279 	int xcreated = 0, s;
280 	int maxrec = RPC_MAXDATASIZE;
281 	int one = 1;
282 	int c, r;
283 	in_port_t svcport = 0;
284 
285 	udp6conf = tcp6conf = NULL;
286 	udp6sock = tcp6sock = 0;
287 
288 	/* Check that another mountd isn't already running. */
289 	if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
290 		err(1, "%s", MOUNTDLOCK);
291 
292 	if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
293 		errx(1, "another mountd is already running. Aborting");
294 	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
295 	if (s < 0)
296 		have_v6 = 0;
297 	else
298 		close(s);
299 	if (modfind("nfsserver") < 0) {
300 		/* Not present in kernel, try loading it */
301 		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
302 			errx(1, "NFS server is not available or loadable");
303 	}
304 
305 	while ((c = getopt(argc, argv, "2dlnp:r")) != -1)
306 		switch (c) {
307 		case '2':
308 			force_v2 = 1;
309 			break;
310 		case 'n':
311 			resvport_only = 0;
312 			break;
313 		case 'r':
314 			dir_only = 0;
315 			break;
316 		case 'd':
317 			debug = debug ? 0 : 1;
318 			break;
319 		case 'l':
320 			dolog = 1;
321 			break;
322 		case 'p':
323 			endptr = NULL;
324 			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
325 			if (endptr == NULL || *endptr != '\0' ||
326 			    svcport == 0 || svcport >= IPPORT_MAX)
327 				usage();
328 			break;
329 		default:
330 			usage();
331 		};
332 	argc -= optind;
333 	argv += optind;
334 	grphead = (struct grouplist *)NULL;
335 	exphead = (struct exportlist *)NULL;
336 	mlhead = (struct mountlist *)NULL;
337 	if (argc == 1) {
338 		strncpy(exname, *argv, MAXPATHLEN-1);
339 		exname[MAXPATHLEN-1] = '\0';
340 	} else
341 		strcpy(exname, _PATH_EXPORTS);
342 	openlog("mountd", LOG_PID, LOG_DAEMON);
343 	if (debug)
344 		warnx("getting export list");
345 	get_exportlist();
346 	if (debug)
347 		warnx("getting mount list");
348 	get_mountlist();
349 	if (debug)
350 		warnx("here we go");
351 	if (debug == 0) {
352 		daemon(0, 0);
353 		signal(SIGINT, SIG_IGN);
354 		signal(SIGQUIT, SIG_IGN);
355 	}
356 	signal(SIGHUP, huphandler);
357 	signal(SIGTERM, terminate);
358 	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
359 	  if (pidfile != NULL) {
360 		fprintf(pidfile, "%d\n", getpid());
361 		fclose(pidfile);
362 	  }
363 	}
364 	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
365 	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
366 	udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
367 	tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
368 	udpconf  = getnetconfigent("udp");
369 	tcpconf  = getnetconfigent("tcp");
370 
371 	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
372 
373 	if (!have_v6)
374 		goto skip_v6;
375 	udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
376 	tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
377 	/*
378 	 * We're doing host-based access checks here, so don't allow
379 	 * v4-in-v6 to confuse things. The kernel will disable it
380 	 * by default on NFS sockets too.
381 	 */
382 	if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
383 		IPV6_V6ONLY, &one, sizeof one) < 0) {
384 		syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
385 		exit(1);
386 	}
387 	if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
388 		IPV6_V6ONLY, &one, sizeof one) < 0) {
389 		syslog(LOG_ERR, "can't disable v4-in-v6 on TCP socket");
390 		exit(1);
391 	}
392 	udp6conf = getnetconfigent("udp6");
393 	tcp6conf = getnetconfigent("tcp6");
394 
395 skip_v6:
396 	if (!resvport_only) {
397 		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
398 		    &resvport_only, sizeof(resvport_only)) != 0 &&
399 		    errno != ENOENT) {
400 			syslog(LOG_ERR, "sysctl: %m");
401 			exit(1);
402 		}
403 	}
404 	if (svcport != 0) {
405 		bzero(&sin, sizeof(struct sockaddr_in));
406 		sin.sin_len = sizeof(struct sockaddr_in);
407 		sin.sin_family = AF_INET;
408 		sin.sin_port = htons(svcport);
409 
410 		bzero(&sin6, sizeof(struct sockaddr_in6));
411 		sin6.sin6_len = sizeof(struct sockaddr_in6);
412 		sin6.sin6_family = AF_INET6;
413 		sin6.sin6_port = htons(svcport);
414 	}
415 	if (udpsock != -1 && udpconf != NULL) {
416 		if (svcport != 0) {
417 			r = bindresvport(udpsock, &sin);
418 			if (r != 0) {
419 				syslog(LOG_ERR, "bindresvport: %m");
420 				exit(1);
421 			}
422 		} else
423 			(void)bindresvport(udpsock, NULL);
424 		udptransp = svc_dg_create(udpsock, 0, 0);
425 		if (udptransp != NULL) {
426 			if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
427 			    mntsrv, udpconf))
428 				syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
429 			else
430 				xcreated++;
431 			if (!force_v2) {
432 				if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
433 				    mntsrv, udpconf))
434 					syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
435 				else
436 					xcreated++;
437 			}
438 		} else
439 			syslog(LOG_WARNING, "can't create UDP services");
440 
441 	}
442 	if (tcpsock != -1 && tcpconf != NULL) {
443 		if (svcport != 0) {
444 			r = bindresvport(tcpsock, &sin);
445 			if (r != 0) {
446 				syslog(LOG_ERR, "bindresvport: %m");
447 				exit(1);
448 			}
449 		} else
450 			(void)bindresvport(tcpsock, NULL);
451 		listen(tcpsock, SOMAXCONN);
452 		tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
453 		if (tcptransp != NULL) {
454 			if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
455 			    mntsrv, tcpconf))
456 				syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
457 			else
458 				xcreated++;
459 			if (!force_v2) {
460 				if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
461 				    mntsrv, tcpconf))
462 					syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
463 				else
464 					xcreated++;
465 			}
466 		} else
467 			syslog(LOG_WARNING, "can't create TCP service");
468 
469 	}
470 	if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
471 		if (svcport != 0) {
472 			r = bindresvport_sa(udp6sock,
473 			    (struct sockaddr *)&sin6);
474 			if (r != 0) {
475 				syslog(LOG_ERR, "bindresvport_sa: %m");
476 				exit(1);
477 			}
478 		} else
479 			(void)bindresvport_sa(udp6sock, NULL);
480 		udp6transp = svc_dg_create(udp6sock, 0, 0);
481 		if (udp6transp != NULL) {
482 			if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
483 			    mntsrv, udp6conf))
484 				syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
485 			else
486 				xcreated++;
487 			if (!force_v2) {
488 				if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
489 				    mntsrv, udp6conf))
490 					syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
491 				else
492 					xcreated++;
493 			}
494 		} else
495 			syslog(LOG_WARNING, "can't create UDP6 service");
496 
497 	}
498 	if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
499 		if (svcport != 0) {
500 			r = bindresvport_sa(tcp6sock,
501 			    (struct sockaddr *)&sin6);
502 			if (r != 0) {
503 				syslog(LOG_ERR, "bindresvport_sa: %m");
504 				exit(1);
505 			}
506 		} else
507 			(void)bindresvport_sa(tcp6sock, NULL);
508 		listen(tcp6sock, SOMAXCONN);
509 		tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
510 		if (tcp6transp != NULL) {
511 			if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
512 			    mntsrv, tcp6conf))
513 				syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
514 			else
515 				xcreated++;
516 			if (!force_v2) {
517 				if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
518 				    mntsrv, tcp6conf))
519 					syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
520 					else
521 						xcreated++;
522 				}
523 		} else
524 			syslog(LOG_WARNING, "can't create TCP6 service");
525 
526 	}
527 	if (xcreated == 0) {
528 		syslog(LOG_ERR, "could not create any services");
529 		exit(1);
530 	}
531 
532 	/* Expand svc_run() here so that we can call get_exportlist(). */
533 	for (;;) {
534 		if (got_sighup) {
535 			get_exportlist();
536 			got_sighup = 0;
537 		}
538 		readfds = svc_fdset;
539 		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
540 		case -1:
541 			if (errno == EINTR)
542                                 continue;
543 			syslog(LOG_ERR, "mountd died: select: %m");
544 			exit(1);
545 		case 0:
546 			continue;
547 		default:
548 			svc_getreqset(&readfds);
549 		}
550 	}
551 }
552 
553 static void
554 usage()
555 {
556 	fprintf(stderr,
557 		"usage: mountd [-2] [-d] [-l] [-n] [-p <port>] [-r] "
558 		"[export_file]\n");
559 	exit(1);
560 }
561 
562 /*
563  * The mount rpc service
564  */
565 void
566 mntsrv(rqstp, transp)
567 	struct svc_req *rqstp;
568 	SVCXPRT *transp;
569 {
570 	struct exportlist *ep;
571 	struct dirlist *dp;
572 	struct fhreturn fhr;
573 	struct stat stb;
574 	struct statfs fsb;
575 	struct addrinfo *ai;
576 	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
577 	int lookup_failed = 1;
578 	struct sockaddr *saddr;
579 	u_short sport;
580 	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
581 	int bad = 0, defset, hostset;
582 	sigset_t sighup_mask;
583 
584 	sigemptyset(&sighup_mask);
585 	sigaddset(&sighup_mask, SIGHUP);
586 	saddr = svc_getrpccaller(transp)->buf;
587 	switch (saddr->sa_family) {
588 	case AF_INET6:
589 		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
590 		break;
591 	case AF_INET:
592 		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
593 		break;
594 	default:
595 		syslog(LOG_ERR, "request from unknown address family");
596 		return;
597 	}
598 	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
599 	    NULL, 0, 0);
600 	getnameinfo(saddr, saddr->sa_len, numerichost,
601 	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
602 	ai = NULL;
603 	switch (rqstp->rq_proc) {
604 	case NULLPROC:
605 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
606 			syslog(LOG_ERR, "can't send reply");
607 		return;
608 	case RPCMNT_MOUNT:
609 		if (sport >= IPPORT_RESERVED && resvport_only) {
610 			syslog(LOG_NOTICE,
611 			    "mount request from %s from unprivileged port",
612 			    numerichost);
613 			svcerr_weakauth(transp);
614 			return;
615 		}
616 		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
617 			syslog(LOG_NOTICE, "undecodable mount request from %s",
618 			    numerichost);
619 			svcerr_decode(transp);
620 			return;
621 		}
622 
623 		/*
624 		 * Get the real pathname and make sure it is a directory
625 		 * or a regular file if the -r option was specified
626 		 * and it exists.
627 		 */
628 		if (realpath(rpcpath, dirpath) == NULL ||
629 		    stat(dirpath, &stb) < 0 ||
630 		    (!S_ISDIR(stb.st_mode) &&
631 		    (dir_only || !S_ISREG(stb.st_mode))) ||
632 		    statfs(dirpath, &fsb) < 0) {
633 			chdir("/");	/* Just in case realpath doesn't */
634 			syslog(LOG_NOTICE,
635 			    "mount request from %s for non existent path %s",
636 			    numerichost, dirpath);
637 			if (debug)
638 				warnx("stat failed on %s", dirpath);
639 			bad = ENOENT;	/* We will send error reply later */
640 		}
641 
642 		/* Check in the exports list */
643 		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
644 		ep = ex_search(&fsb.f_fsid);
645 		hostset = defset = 0;
646 		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
647 		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
648 		      chk_host(dp, saddr, &defset, &hostset)) ||
649 		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
650 		     scan_tree(ep->ex_dirl, saddr) == 0))) {
651 			if (bad) {
652 				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
653 				    (caddr_t)&bad))
654 					syslog(LOG_ERR, "can't send reply");
655 				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
656 				return;
657 			}
658 			if (hostset & DP_HOSTSET)
659 				fhr.fhr_flag = hostset;
660 			else
661 				fhr.fhr_flag = defset;
662 			fhr.fhr_vers = rqstp->rq_vers;
663 			/* Get the file handle */
664 			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
665 			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
666 				bad = errno;
667 				syslog(LOG_ERR, "can't get fh for %s", dirpath);
668 				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
669 				    (caddr_t)&bad))
670 					syslog(LOG_ERR, "can't send reply");
671 				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
672 				return;
673 			}
674 			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
675 			    (caddr_t)&fhr))
676 				syslog(LOG_ERR, "can't send reply");
677 			if (!lookup_failed)
678 				add_mlist(host, dirpath);
679 			else
680 				add_mlist(numerichost, dirpath);
681 			if (debug)
682 				warnx("mount successful");
683 			if (dolog)
684 				syslog(LOG_NOTICE,
685 				    "mount request succeeded from %s for %s",
686 				    numerichost, dirpath);
687 		} else {
688 			bad = EACCES;
689 			syslog(LOG_NOTICE,
690 			    "mount request denied from %s for %s",
691 			    numerichost, dirpath);
692 		}
693 
694 		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
695 		    (caddr_t)&bad))
696 			syslog(LOG_ERR, "can't send reply");
697 		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
698 		return;
699 	case RPCMNT_DUMP:
700 		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
701 			syslog(LOG_ERR, "can't send reply");
702 		else if (dolog)
703 			syslog(LOG_NOTICE,
704 			    "dump request succeeded from %s",
705 			    numerichost);
706 		return;
707 	case RPCMNT_UMOUNT:
708 		if (sport >= IPPORT_RESERVED && resvport_only) {
709 			syslog(LOG_NOTICE,
710 			    "umount request from %s from unprivileged port",
711 			    numerichost);
712 			svcerr_weakauth(transp);
713 			return;
714 		}
715 		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
716 			syslog(LOG_NOTICE, "undecodable umount request from %s",
717 			    numerichost);
718 			svcerr_decode(transp);
719 			return;
720 		}
721 		if (realpath(rpcpath, dirpath) == NULL) {
722 			syslog(LOG_NOTICE, "umount request from %s "
723 			    "for non existent path %s",
724 			    numerichost, dirpath);
725 		}
726 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
727 			syslog(LOG_ERR, "can't send reply");
728 		if (!lookup_failed)
729 			del_mlist(host, dirpath);
730 		del_mlist(numerichost, dirpath);
731 		if (dolog)
732 			syslog(LOG_NOTICE,
733 			    "umount request succeeded from %s for %s",
734 			    numerichost, dirpath);
735 		return;
736 	case RPCMNT_UMNTALL:
737 		if (sport >= IPPORT_RESERVED && resvport_only) {
738 			syslog(LOG_NOTICE,
739 			    "umountall request from %s from unprivileged port",
740 			    numerichost);
741 			svcerr_weakauth(transp);
742 			return;
743 		}
744 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
745 			syslog(LOG_ERR, "can't send reply");
746 		if (!lookup_failed)
747 			del_mlist(host, NULL);
748 		del_mlist(numerichost, NULL);
749 		if (dolog)
750 			syslog(LOG_NOTICE,
751 			    "umountall request succeeded from %s",
752 			    numerichost);
753 		return;
754 	case RPCMNT_EXPORT:
755 		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
756 			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
757 			    (caddr_t)NULL))
758 				syslog(LOG_ERR, "can't send reply");
759 		if (dolog)
760 			syslog(LOG_NOTICE,
761 			    "export request succeeded from %s",
762 			    numerichost);
763 		return;
764 	default:
765 		svcerr_noproc(transp);
766 		return;
767 	}
768 }
769 
770 /*
771  * Xdr conversion for a dirpath string
772  */
773 int
774 xdr_dir(xdrsp, dirp)
775 	XDR *xdrsp;
776 	char *dirp;
777 {
778 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
779 }
780 
781 /*
782  * Xdr routine to generate file handle reply
783  */
784 int
785 xdr_fhs(xdrsp, cp)
786 	XDR *xdrsp;
787 	caddr_t cp;
788 {
789 	struct fhreturn *fhrp = (struct fhreturn *)cp;
790 	u_long ok = 0, len, auth;
791 
792 	if (!xdr_long(xdrsp, &ok))
793 		return (0);
794 	switch (fhrp->fhr_vers) {
795 	case 1:
796 		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
797 	case 3:
798 		len = NFSX_V3FH;
799 		if (!xdr_long(xdrsp, &len))
800 			return (0);
801 		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
802 			return (0);
803 		auth = RPCAUTH_UNIX;
804 		len = 1;
805 		if (!xdr_long(xdrsp, &len))
806 			return (0);
807 		return (xdr_long(xdrsp, &auth));
808 	};
809 	return (0);
810 }
811 
812 int
813 xdr_mlist(xdrsp, cp)
814 	XDR *xdrsp;
815 	caddr_t cp;
816 {
817 	struct mountlist *mlp;
818 	int true = 1;
819 	int false = 0;
820 	char *strp;
821 
822 	mlp = mlhead;
823 	while (mlp) {
824 		if (!xdr_bool(xdrsp, &true))
825 			return (0);
826 		strp = &mlp->ml_host[0];
827 		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
828 			return (0);
829 		strp = &mlp->ml_dirp[0];
830 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
831 			return (0);
832 		mlp = mlp->ml_next;
833 	}
834 	if (!xdr_bool(xdrsp, &false))
835 		return (0);
836 	return (1);
837 }
838 
839 /*
840  * Xdr conversion for export list
841  */
842 int
843 xdr_explist_common(xdrsp, cp, brief)
844 	XDR *xdrsp;
845 	caddr_t cp;
846 	int brief;
847 {
848 	struct exportlist *ep;
849 	int false = 0;
850 	int putdef;
851 	sigset_t sighup_mask;
852 
853 	sigemptyset(&sighup_mask);
854 	sigaddset(&sighup_mask, SIGHUP);
855 	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
856 	ep = exphead;
857 	while (ep) {
858 		putdef = 0;
859 		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
860 			       &putdef, brief))
861 			goto errout;
862 		if (ep->ex_defdir && putdef == 0 &&
863 			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
864 			&putdef, brief))
865 			goto errout;
866 		ep = ep->ex_next;
867 	}
868 	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
869 	if (!xdr_bool(xdrsp, &false))
870 		return (0);
871 	return (1);
872 errout:
873 	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
874 	return (0);
875 }
876 
877 /*
878  * Called from xdr_explist() to traverse the tree and export the
879  * directory paths.
880  */
881 int
882 put_exlist(dp, xdrsp, adp, putdefp, brief)
883 	struct dirlist *dp;
884 	XDR *xdrsp;
885 	struct dirlist *adp;
886 	int *putdefp;
887 	int brief;
888 {
889 	struct grouplist *grp;
890 	struct hostlist *hp;
891 	int true = 1;
892 	int false = 0;
893 	int gotalldir = 0;
894 	char *strp;
895 
896 	if (dp) {
897 		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
898 			return (1);
899 		if (!xdr_bool(xdrsp, &true))
900 			return (1);
901 		strp = dp->dp_dirp;
902 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
903 			return (1);
904 		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
905 			gotalldir = 1;
906 			*putdefp = 1;
907 		}
908 		if (brief) {
909 			if (!xdr_bool(xdrsp, &true))
910 				return (1);
911 			strp = "(...)";
912 			if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
913 				return (1);
914 		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
915 		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
916 			hp = dp->dp_hosts;
917 			while (hp) {
918 				grp = hp->ht_grp;
919 				if (grp->gr_type == GT_HOST) {
920 					if (!xdr_bool(xdrsp, &true))
921 						return (1);
922 					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
923 					if (!xdr_string(xdrsp, &strp,
924 					    RPCMNT_NAMELEN))
925 						return (1);
926 				} else if (grp->gr_type == GT_NET) {
927 					if (!xdr_bool(xdrsp, &true))
928 						return (1);
929 					strp = grp->gr_ptr.gt_net.nt_name;
930 					if (!xdr_string(xdrsp, &strp,
931 					    RPCMNT_NAMELEN))
932 						return (1);
933 				}
934 				hp = hp->ht_next;
935 				if (gotalldir && hp == (struct hostlist *)NULL) {
936 					hp = adp->dp_hosts;
937 					gotalldir = 0;
938 				}
939 			}
940 		}
941 		if (!xdr_bool(xdrsp, &false))
942 			return (1);
943 		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
944 			return (1);
945 	}
946 	return (0);
947 }
948 
949 int
950 xdr_explist(xdrsp, cp)
951 	XDR *xdrsp;
952 	caddr_t cp;
953 {
954 
955 	return xdr_explist_common(xdrsp, cp, 0);
956 }
957 
958 int
959 xdr_explist_brief(xdrsp, cp)
960 	XDR *xdrsp;
961 	caddr_t cp;
962 {
963 
964 	return xdr_explist_common(xdrsp, cp, 1);
965 }
966 
967 char *line;
968 int linesize;
969 FILE *exp_file;
970 
971 /*
972  * Get the export list
973  */
974 void
975 get_exportlist()
976 {
977 	struct exportlist *ep, *ep2;
978 	struct grouplist *grp, *tgrp;
979 	struct exportlist **epp;
980 	struct dirlist *dirhead;
981 	struct statfs fsb, *fsp;
982 	struct xucred anon;
983 	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
984 	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
985 
986 	dirp = NULL;
987 	dirplen = 0;
988 
989 	/*
990 	 * First, get rid of the old list
991 	 */
992 	ep = exphead;
993 	while (ep) {
994 		ep2 = ep;
995 		ep = ep->ex_next;
996 		free_exp(ep2);
997 	}
998 	exphead = (struct exportlist *)NULL;
999 
1000 	grp = grphead;
1001 	while (grp) {
1002 		tgrp = grp;
1003 		grp = grp->gr_next;
1004 		free_grp(tgrp);
1005 	}
1006 	grphead = (struct grouplist *)NULL;
1007 
1008 	/*
1009 	 * And delete exports that are in the kernel for all local
1010 	 * filesystems.
1011 	 * XXX: Should know how to handle all local exportable filesystems
1012 	 *      instead of just "ufs".
1013 	 */
1014 	num = getmntinfo(&fsp, MNT_NOWAIT);
1015 	for (i = 0; i < num; i++) {
1016 		union {
1017 			struct ufs_args ua;
1018 			struct iso_args ia;
1019 			struct msdosfs_args da;
1020 			struct ntfs_args na;
1021 		} targs;
1022 
1023 		if (!strcmp(fsp->f_fstypename, "ufs") ||
1024 		    !strcmp(fsp->f_fstypename, "msdosfs") ||
1025 		    !strcmp(fsp->f_fstypename, "ntfs") ||
1026 		    !strcmp(fsp->f_fstypename, "cd9660")) {
1027 			targs.ua.fspec = NULL;
1028 			targs.ua.export.ex_flags = MNT_DELEXPORT;
1029 			if (mount(fsp->f_fstypename, fsp->f_mntonname,
1030 			    fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
1031 			    errno != ENOENT)
1032 				syslog(LOG_ERR,
1033 				    "can't delete exports for %s: %m",
1034 				    fsp->f_mntonname);
1035 		}
1036 		fsp++;
1037 	}
1038 
1039 	/*
1040 	 * Read in the exports file and build the list, calling
1041 	 * mount() as we go along to push the export rules into the kernel.
1042 	 */
1043 	if ((exp_file = fopen(exname, "r")) == NULL) {
1044 		syslog(LOG_ERR, "can't open %s", exname);
1045 		exit(2);
1046 	}
1047 	dirhead = (struct dirlist *)NULL;
1048 	while (get_line()) {
1049 		if (debug)
1050 			warnx("got line %s", line);
1051 		cp = line;
1052 		nextfield(&cp, &endcp);
1053 		if (*cp == '#')
1054 			goto nextline;
1055 
1056 		/*
1057 		 * Set defaults.
1058 		 */
1059 		has_host = FALSE;
1060 		anon = def_anon;
1061 		exflags = MNT_EXPORTED;
1062 		got_nondir = 0;
1063 		opt_flags = 0;
1064 		ep = (struct exportlist *)NULL;
1065 
1066 		/*
1067 		 * Create new exports list entry
1068 		 */
1069 		len = endcp-cp;
1070 		tgrp = grp = get_grp();
1071 		while (len > 0) {
1072 			if (len > RPCMNT_NAMELEN) {
1073 			    getexp_err(ep, tgrp);
1074 			    goto nextline;
1075 			}
1076 			if (*cp == '-') {
1077 			    if (ep == (struct exportlist *)NULL) {
1078 				getexp_err(ep, tgrp);
1079 				goto nextline;
1080 			    }
1081 			    if (debug)
1082 				warnx("doing opt %s", cp);
1083 			    got_nondir = 1;
1084 			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1085 				&exflags, &anon)) {
1086 				getexp_err(ep, tgrp);
1087 				goto nextline;
1088 			    }
1089 			} else if (*cp == '/') {
1090 			    savedc = *endcp;
1091 			    *endcp = '\0';
1092 			    if (check_dirpath(cp) &&
1093 				statfs(cp, &fsb) >= 0) {
1094 				if (got_nondir) {
1095 				    syslog(LOG_ERR, "dirs must be first");
1096 				    getexp_err(ep, tgrp);
1097 				    goto nextline;
1098 				}
1099 				if (ep) {
1100 				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1101 					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1102 					getexp_err(ep, tgrp);
1103 					goto nextline;
1104 				    }
1105 				} else {
1106 				    /*
1107 				     * See if this directory is already
1108 				     * in the list.
1109 				     */
1110 				    ep = ex_search(&fsb.f_fsid);
1111 				    if (ep == (struct exportlist *)NULL) {
1112 					ep = get_exp();
1113 					ep->ex_fs = fsb.f_fsid;
1114 					ep->ex_fsdir = (char *)
1115 					    malloc(strlen(fsb.f_mntonname) + 1);
1116 					if (ep->ex_fsdir)
1117 					    strcpy(ep->ex_fsdir,
1118 						fsb.f_mntonname);
1119 					else
1120 					    out_of_mem();
1121 					if (debug)
1122 						warnx("making new ep fs=0x%x,0x%x",
1123 						    fsb.f_fsid.val[0],
1124 						    fsb.f_fsid.val[1]);
1125 				    } else if (debug)
1126 					warnx("found ep fs=0x%x,0x%x",
1127 					    fsb.f_fsid.val[0],
1128 					    fsb.f_fsid.val[1]);
1129 				}
1130 
1131 				/*
1132 				 * Add dirpath to export mount point.
1133 				 */
1134 				dirp = add_expdir(&dirhead, cp, len);
1135 				dirplen = len;
1136 			    } else {
1137 				getexp_err(ep, tgrp);
1138 				goto nextline;
1139 			    }
1140 			    *endcp = savedc;
1141 			} else {
1142 			    savedc = *endcp;
1143 			    *endcp = '\0';
1144 			    got_nondir = 1;
1145 			    if (ep == (struct exportlist *)NULL) {
1146 				getexp_err(ep, tgrp);
1147 				goto nextline;
1148 			    }
1149 
1150 			    /*
1151 			     * Get the host or netgroup.
1152 			     */
1153 			    setnetgrent(cp);
1154 			    netgrp = getnetgrent(&hst, &usr, &dom);
1155 			    do {
1156 				if (has_host) {
1157 				    grp->gr_next = get_grp();
1158 				    grp = grp->gr_next;
1159 				}
1160 				if (netgrp) {
1161 				    if (hst == 0) {
1162 					syslog(LOG_ERR,
1163 				"null hostname in netgroup %s, skipping", cp);
1164 					grp->gr_type = GT_IGNORE;
1165 				    } else if (get_host(hst, grp, tgrp)) {
1166 					syslog(LOG_ERR,
1167 			"bad host %s in netgroup %s, skipping", hst, cp);
1168 					grp->gr_type = GT_IGNORE;
1169 				    }
1170 				} else if (get_host(cp, grp, tgrp)) {
1171 				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1172 				    grp->gr_type = GT_IGNORE;
1173 				}
1174 				has_host = TRUE;
1175 			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1176 			    endnetgrent();
1177 			    *endcp = savedc;
1178 			}
1179 			cp = endcp;
1180 			nextfield(&cp, &endcp);
1181 			len = endcp - cp;
1182 		}
1183 		if (check_options(dirhead)) {
1184 			getexp_err(ep, tgrp);
1185 			goto nextline;
1186 		}
1187 		if (!has_host) {
1188 			grp->gr_type = GT_DEFAULT;
1189 			if (debug)
1190 				warnx("adding a default entry");
1191 
1192 		/*
1193 		 * Don't allow a network export coincide with a list of
1194 		 * host(s) on the same line.
1195 		 */
1196 		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1197 			syslog(LOG_ERR, "network/host conflict");
1198 			getexp_err(ep, tgrp);
1199 			goto nextline;
1200 
1201 		/*
1202 		 * If an export list was specified on this line, make sure
1203 		 * that we have at least one valid entry, otherwise skip it.
1204 		 */
1205 		} else {
1206 			grp = tgrp;
1207 			while (grp && grp->gr_type == GT_IGNORE)
1208 				grp = grp->gr_next;
1209 			if (! grp) {
1210 			    getexp_err(ep, tgrp);
1211 			    goto nextline;
1212 			}
1213 		}
1214 
1215 		/*
1216 		 * Loop through hosts, pushing the exports into the kernel.
1217 		 * After loop, tgrp points to the start of the list and
1218 		 * grp points to the last entry in the list.
1219 		 */
1220 		grp = tgrp;
1221 		do {
1222 			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1223 			    &fsb)) {
1224 				getexp_err(ep, tgrp);
1225 				goto nextline;
1226 			}
1227 		} while (grp->gr_next && (grp = grp->gr_next));
1228 
1229 		/*
1230 		 * Success. Update the data structures.
1231 		 */
1232 		if (has_host) {
1233 			hang_dirp(dirhead, tgrp, ep, opt_flags);
1234 			grp->gr_next = grphead;
1235 			grphead = tgrp;
1236 		} else {
1237 			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1238 				opt_flags);
1239 			free_grp(grp);
1240 		}
1241 		dirhead = (struct dirlist *)NULL;
1242 		if ((ep->ex_flag & EX_LINKED) == 0) {
1243 			ep2 = exphead;
1244 			epp = &exphead;
1245 
1246 			/*
1247 			 * Insert in the list in alphabetical order.
1248 			 */
1249 			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1250 				epp = &ep2->ex_next;
1251 				ep2 = ep2->ex_next;
1252 			}
1253 			if (ep2)
1254 				ep->ex_next = ep2;
1255 			*epp = ep;
1256 			ep->ex_flag |= EX_LINKED;
1257 		}
1258 nextline:
1259 		if (dirhead) {
1260 			free_dir(dirhead);
1261 			dirhead = (struct dirlist *)NULL;
1262 		}
1263 	}
1264 	fclose(exp_file);
1265 }
1266 
1267 /*
1268  * Allocate an export list element
1269  */
1270 struct exportlist *
1271 get_exp()
1272 {
1273 	struct exportlist *ep;
1274 
1275 	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1276 	if (ep == (struct exportlist *)NULL)
1277 		out_of_mem();
1278 	memset(ep, 0, sizeof(struct exportlist));
1279 	return (ep);
1280 }
1281 
1282 /*
1283  * Allocate a group list element
1284  */
1285 struct grouplist *
1286 get_grp()
1287 {
1288 	struct grouplist *gp;
1289 
1290 	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1291 	if (gp == (struct grouplist *)NULL)
1292 		out_of_mem();
1293 	memset(gp, 0, sizeof(struct grouplist));
1294 	return (gp);
1295 }
1296 
1297 /*
1298  * Clean up upon an error in get_exportlist().
1299  */
1300 void
1301 getexp_err(ep, grp)
1302 	struct exportlist *ep;
1303 	struct grouplist *grp;
1304 {
1305 	struct grouplist *tgrp;
1306 
1307 	if (!(opt_flags & OP_QUIET))
1308 		syslog(LOG_ERR, "bad exports list line %s", line);
1309 	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1310 		free_exp(ep);
1311 	while (grp) {
1312 		tgrp = grp;
1313 		grp = grp->gr_next;
1314 		free_grp(tgrp);
1315 	}
1316 }
1317 
1318 /*
1319  * Search the export list for a matching fs.
1320  */
1321 struct exportlist *
1322 ex_search(fsid)
1323 	fsid_t *fsid;
1324 {
1325 	struct exportlist *ep;
1326 
1327 	ep = exphead;
1328 	while (ep) {
1329 		if (ep->ex_fs.val[0] == fsid->val[0] &&
1330 		    ep->ex_fs.val[1] == fsid->val[1])
1331 			return (ep);
1332 		ep = ep->ex_next;
1333 	}
1334 	return (ep);
1335 }
1336 
1337 /*
1338  * Add a directory path to the list.
1339  */
1340 char *
1341 add_expdir(dpp, cp, len)
1342 	struct dirlist **dpp;
1343 	char *cp;
1344 	int len;
1345 {
1346 	struct dirlist *dp;
1347 
1348 	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1349 	if (dp == (struct dirlist *)NULL)
1350 		out_of_mem();
1351 	dp->dp_left = *dpp;
1352 	dp->dp_right = (struct dirlist *)NULL;
1353 	dp->dp_flag = 0;
1354 	dp->dp_hosts = (struct hostlist *)NULL;
1355 	strcpy(dp->dp_dirp, cp);
1356 	*dpp = dp;
1357 	return (dp->dp_dirp);
1358 }
1359 
1360 /*
1361  * Hang the dir list element off the dirpath binary tree as required
1362  * and update the entry for host.
1363  */
1364 void
1365 hang_dirp(dp, grp, ep, flags)
1366 	struct dirlist *dp;
1367 	struct grouplist *grp;
1368 	struct exportlist *ep;
1369 	int flags;
1370 {
1371 	struct hostlist *hp;
1372 	struct dirlist *dp2;
1373 
1374 	if (flags & OP_ALLDIRS) {
1375 		if (ep->ex_defdir)
1376 			free((caddr_t)dp);
1377 		else
1378 			ep->ex_defdir = dp;
1379 		if (grp == (struct grouplist *)NULL) {
1380 			ep->ex_defdir->dp_flag |= DP_DEFSET;
1381 		} else while (grp) {
1382 			hp = get_ht();
1383 			hp->ht_grp = grp;
1384 			hp->ht_next = ep->ex_defdir->dp_hosts;
1385 			ep->ex_defdir->dp_hosts = hp;
1386 			grp = grp->gr_next;
1387 		}
1388 	} else {
1389 
1390 		/*
1391 		 * Loop through the directories adding them to the tree.
1392 		 */
1393 		while (dp) {
1394 			dp2 = dp->dp_left;
1395 			add_dlist(&ep->ex_dirl, dp, grp, flags);
1396 			dp = dp2;
1397 		}
1398 	}
1399 }
1400 
1401 /*
1402  * Traverse the binary tree either updating a node that is already there
1403  * for the new directory or adding the new node.
1404  */
1405 void
1406 add_dlist(dpp, newdp, grp, flags)
1407 	struct dirlist **dpp;
1408 	struct dirlist *newdp;
1409 	struct grouplist *grp;
1410 	int flags;
1411 {
1412 	struct dirlist *dp;
1413 	struct hostlist *hp;
1414 	int cmp;
1415 
1416 	dp = *dpp;
1417 	if (dp) {
1418 		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1419 		if (cmp > 0) {
1420 			add_dlist(&dp->dp_left, newdp, grp, flags);
1421 			return;
1422 		} else if (cmp < 0) {
1423 			add_dlist(&dp->dp_right, newdp, grp, flags);
1424 			return;
1425 		} else
1426 			free((caddr_t)newdp);
1427 	} else {
1428 		dp = newdp;
1429 		dp->dp_left = (struct dirlist *)NULL;
1430 		*dpp = dp;
1431 	}
1432 	if (grp) {
1433 
1434 		/*
1435 		 * Hang all of the host(s) off of the directory point.
1436 		 */
1437 		do {
1438 			hp = get_ht();
1439 			hp->ht_grp = grp;
1440 			hp->ht_next = dp->dp_hosts;
1441 			dp->dp_hosts = hp;
1442 			grp = grp->gr_next;
1443 		} while (grp);
1444 	} else {
1445 		dp->dp_flag |= DP_DEFSET;
1446 	}
1447 }
1448 
1449 /*
1450  * Search for a dirpath on the export point.
1451  */
1452 struct dirlist *
1453 dirp_search(dp, dirp)
1454 	struct dirlist *dp;
1455 	char *dirp;
1456 {
1457 	int cmp;
1458 
1459 	if (dp) {
1460 		cmp = strcmp(dp->dp_dirp, dirp);
1461 		if (cmp > 0)
1462 			return (dirp_search(dp->dp_left, dirp));
1463 		else if (cmp < 0)
1464 			return (dirp_search(dp->dp_right, dirp));
1465 		else
1466 			return (dp);
1467 	}
1468 	return (dp);
1469 }
1470 
1471 /*
1472  * Scan for a host match in a directory tree.
1473  */
1474 int
1475 chk_host(dp, saddr, defsetp, hostsetp)
1476 	struct dirlist *dp;
1477 	struct sockaddr *saddr;
1478 	int *defsetp;
1479 	int *hostsetp;
1480 {
1481 	struct hostlist *hp;
1482 	struct grouplist *grp;
1483 	struct addrinfo *ai;
1484 
1485 	if (dp) {
1486 		if (dp->dp_flag & DP_DEFSET)
1487 			*defsetp = dp->dp_flag;
1488 		hp = dp->dp_hosts;
1489 		while (hp) {
1490 			grp = hp->ht_grp;
1491 			switch (grp->gr_type) {
1492 			case GT_HOST:
1493 				ai = grp->gr_ptr.gt_addrinfo;
1494 				for (; ai; ai = ai->ai_next) {
1495 					if (!sacmp(ai->ai_addr, saddr, NULL)) {
1496 						*hostsetp =
1497 						    (hp->ht_flag | DP_HOSTSET);
1498 						return (1);
1499 					}
1500 				}
1501 				break;
1502 			case GT_NET:
1503 				if (!sacmp(saddr, (struct sockaddr *)
1504 				    &grp->gr_ptr.gt_net.nt_net,
1505 				    (struct sockaddr *)
1506 				    &grp->gr_ptr.gt_net.nt_mask)) {
1507 					*hostsetp = (hp->ht_flag | DP_HOSTSET);
1508 					return (1);
1509 				}
1510 				break;
1511 			}
1512 			hp = hp->ht_next;
1513 		}
1514 	}
1515 	return (0);
1516 }
1517 
1518 /*
1519  * Scan tree for a host that matches the address.
1520  */
1521 int
1522 scan_tree(dp, saddr)
1523 	struct dirlist *dp;
1524 	struct sockaddr *saddr;
1525 {
1526 	int defset, hostset;
1527 
1528 	if (dp) {
1529 		if (scan_tree(dp->dp_left, saddr))
1530 			return (1);
1531 		if (chk_host(dp, saddr, &defset, &hostset))
1532 			return (1);
1533 		if (scan_tree(dp->dp_right, saddr))
1534 			return (1);
1535 	}
1536 	return (0);
1537 }
1538 
1539 /*
1540  * Traverse the dirlist tree and free it up.
1541  */
1542 void
1543 free_dir(dp)
1544 	struct dirlist *dp;
1545 {
1546 
1547 	if (dp) {
1548 		free_dir(dp->dp_left);
1549 		free_dir(dp->dp_right);
1550 		free_host(dp->dp_hosts);
1551 		free((caddr_t)dp);
1552 	}
1553 }
1554 
1555 /*
1556  * Parse the option string and update fields.
1557  * Option arguments may either be -<option>=<value> or
1558  * -<option> <value>
1559  */
1560 int
1561 do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1562 	char **cpp, **endcpp;
1563 	struct exportlist *ep;
1564 	struct grouplist *grp;
1565 	int *has_hostp;
1566 	int *exflagsp;
1567 	struct xucred *cr;
1568 {
1569 	char *cpoptarg, *cpoptend;
1570 	char *cp, *endcp, *cpopt, savedc, savedc2;
1571 	int allflag, usedarg;
1572 
1573 	savedc2 = '\0';
1574 	cpopt = *cpp;
1575 	cpopt++;
1576 	cp = *endcpp;
1577 	savedc = *cp;
1578 	*cp = '\0';
1579 	while (cpopt && *cpopt) {
1580 		allflag = 1;
1581 		usedarg = -2;
1582 		if ((cpoptend = strchr(cpopt, ','))) {
1583 			*cpoptend++ = '\0';
1584 			if ((cpoptarg = strchr(cpopt, '=')))
1585 				*cpoptarg++ = '\0';
1586 		} else {
1587 			if ((cpoptarg = strchr(cpopt, '=')))
1588 				*cpoptarg++ = '\0';
1589 			else {
1590 				*cp = savedc;
1591 				nextfield(&cp, &endcp);
1592 				**endcpp = '\0';
1593 				if (endcp > cp && *cp != '-') {
1594 					cpoptarg = cp;
1595 					savedc2 = *endcp;
1596 					*endcp = '\0';
1597 					usedarg = 0;
1598 				}
1599 			}
1600 		}
1601 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1602 			*exflagsp |= MNT_EXRDONLY;
1603 		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1604 		    !(allflag = strcmp(cpopt, "mapall")) ||
1605 		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1606 			usedarg++;
1607 			parsecred(cpoptarg, cr);
1608 			if (allflag == 0) {
1609 				*exflagsp |= MNT_EXPORTANON;
1610 				opt_flags |= OP_MAPALL;
1611 			} else
1612 				opt_flags |= OP_MAPROOT;
1613 		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1614 		    !strcmp(cpopt, "m"))) {
1615 			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1616 				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1617 				return (1);
1618 			}
1619 			usedarg++;
1620 			opt_flags |= OP_MASK;
1621 		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1622 			!strcmp(cpopt, "n"))) {
1623 			if (strchr(cpoptarg, '/') != NULL) {
1624 				if (debug)
1625 					fprintf(stderr, "setting OP_MASKLEN\n");
1626 				opt_flags |= OP_MASKLEN;
1627 			}
1628 			if (grp->gr_type != GT_NULL) {
1629 				syslog(LOG_ERR, "network/host conflict");
1630 				return (1);
1631 			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1632 				syslog(LOG_ERR, "bad net: %s", cpoptarg);
1633 				return (1);
1634 			}
1635 			grp->gr_type = GT_NET;
1636 			*has_hostp = 1;
1637 			usedarg++;
1638 			opt_flags |= OP_NET;
1639 		} else if (!strcmp(cpopt, "alldirs")) {
1640 			opt_flags |= OP_ALLDIRS;
1641 		} else if (!strcmp(cpopt, "public")) {
1642 			*exflagsp |= MNT_EXPUBLIC;
1643 		} else if (!strcmp(cpopt, "webnfs")) {
1644 			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1645 			opt_flags |= OP_MAPALL;
1646 		} else if (cpoptarg && !strcmp(cpopt, "index")) {
1647 			ep->ex_indexfile = strdup(cpoptarg);
1648 		} else if (!strcmp(cpopt, "quiet")) {
1649 			opt_flags |= OP_QUIET;
1650 		} else {
1651 			syslog(LOG_ERR, "bad opt %s", cpopt);
1652 			return (1);
1653 		}
1654 		if (usedarg >= 0) {
1655 			*endcp = savedc2;
1656 			**endcpp = savedc;
1657 			if (usedarg > 0) {
1658 				*cpp = cp;
1659 				*endcpp = endcp;
1660 			}
1661 			return (0);
1662 		}
1663 		cpopt = cpoptend;
1664 	}
1665 	**endcpp = savedc;
1666 	return (0);
1667 }
1668 
1669 /*
1670  * Translate a character string to the corresponding list of network
1671  * addresses for a hostname.
1672  */
1673 int
1674 get_host(cp, grp, tgrp)
1675 	char *cp;
1676 	struct grouplist *grp;
1677 	struct grouplist *tgrp;
1678 {
1679 	struct grouplist *checkgrp;
1680 	struct addrinfo *ai, *tai, hints;
1681 	int ecode;
1682 	char host[NI_MAXHOST];
1683 
1684 	if (grp->gr_type != GT_NULL) {
1685 		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
1686 		return (1);
1687 	}
1688 	memset(&hints, 0, sizeof hints);
1689 	hints.ai_flags = AI_CANONNAME;
1690 	hints.ai_protocol = IPPROTO_UDP;
1691 	ecode = getaddrinfo(cp, NULL, &hints, &ai);
1692 	if (ecode != 0) {
1693 		syslog(LOG_ERR,"can't get address info for host %s", cp);
1694 		return 1;
1695 	}
1696 	grp->gr_ptr.gt_addrinfo = ai;
1697 	while (ai != NULL) {
1698 		if (ai->ai_canonname == NULL) {
1699 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1700 			    sizeof host, NULL, 0, ninumeric) != 0)
1701 				strlcpy(host, "?", sizeof(host));
1702 			ai->ai_canonname = strdup(host);
1703 			ai->ai_flags |= AI_CANONNAME;
1704 		}
1705 		if (debug)
1706 			fprintf(stderr, "got host %s\n", ai->ai_canonname);
1707 		/*
1708 		 * Sanity check: make sure we don't already have an entry
1709 		 * for this host in the grouplist.
1710 		 */
1711 		for (checkgrp = tgrp; checkgrp != NULL;
1712 		    checkgrp = checkgrp->gr_next) {
1713 			if (checkgrp->gr_type != GT_HOST)
1714 				continue;
1715 			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
1716 			    tai = tai->ai_next) {
1717 				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
1718 					continue;
1719 				if (debug)
1720 					fprintf(stderr,
1721 					    "ignoring duplicate host %s\n",
1722 					    ai->ai_canonname);
1723 				grp->gr_type = GT_IGNORE;
1724 				return (0);
1725 			}
1726 		}
1727 		ai = ai->ai_next;
1728 	}
1729 	grp->gr_type = GT_HOST;
1730 	return (0);
1731 }
1732 
1733 /*
1734  * Free up an exports list component
1735  */
1736 void
1737 free_exp(ep)
1738 	struct exportlist *ep;
1739 {
1740 
1741 	if (ep->ex_defdir) {
1742 		free_host(ep->ex_defdir->dp_hosts);
1743 		free((caddr_t)ep->ex_defdir);
1744 	}
1745 	if (ep->ex_fsdir)
1746 		free(ep->ex_fsdir);
1747 	if (ep->ex_indexfile)
1748 		free(ep->ex_indexfile);
1749 	free_dir(ep->ex_dirl);
1750 	free((caddr_t)ep);
1751 }
1752 
1753 /*
1754  * Free hosts.
1755  */
1756 void
1757 free_host(hp)
1758 	struct hostlist *hp;
1759 {
1760 	struct hostlist *hp2;
1761 
1762 	while (hp) {
1763 		hp2 = hp;
1764 		hp = hp->ht_next;
1765 		free((caddr_t)hp2);
1766 	}
1767 }
1768 
1769 struct hostlist *
1770 get_ht()
1771 {
1772 	struct hostlist *hp;
1773 
1774 	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1775 	if (hp == (struct hostlist *)NULL)
1776 		out_of_mem();
1777 	hp->ht_next = (struct hostlist *)NULL;
1778 	hp->ht_flag = 0;
1779 	return (hp);
1780 }
1781 
1782 /*
1783  * Out of memory, fatal
1784  */
1785 void
1786 out_of_mem()
1787 {
1788 
1789 	syslog(LOG_ERR, "out of memory");
1790 	exit(2);
1791 }
1792 
1793 /*
1794  * Do the mount syscall with the update flag to push the export info into
1795  * the kernel.
1796  */
1797 int
1798 do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1799 	struct exportlist *ep;
1800 	struct grouplist *grp;
1801 	int exflags;
1802 	struct xucred *anoncrp;
1803 	char *dirp;
1804 	int dirplen;
1805 	struct statfs *fsb;
1806 {
1807 	struct statfs fsb1;
1808 	struct addrinfo *ai;
1809 	struct export_args *eap;
1810 	char *cp = NULL;
1811 	int done;
1812 	char savedc = '\0';
1813 	union {
1814 		struct ufs_args ua;
1815 		struct iso_args ia;
1816 		struct msdosfs_args da;
1817 		struct ntfs_args na;
1818 	} args;
1819 
1820 	bzero(&args, sizeof args);
1821 	/* XXX, we assume that all xx_args look like ufs_args. */
1822 	args.ua.fspec = 0;
1823 	eap = &args.ua.export;
1824 
1825 	eap->ex_flags = exflags;
1826 	eap->ex_anon = *anoncrp;
1827 	eap->ex_indexfile = ep->ex_indexfile;
1828 	if (grp->gr_type == GT_HOST)
1829 		ai = grp->gr_ptr.gt_addrinfo;
1830 	else
1831 		ai = NULL;
1832 	done = FALSE;
1833 	while (!done) {
1834 		switch (grp->gr_type) {
1835 		case GT_HOST:
1836 			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
1837 				goto skip;
1838 			eap->ex_addr = ai->ai_addr;
1839 			eap->ex_addrlen = ai->ai_addrlen;
1840 			eap->ex_masklen = 0;
1841 			break;
1842 		case GT_NET:
1843 			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
1844 			    have_v6 == 0)
1845 				goto skip;
1846 			eap->ex_addr =
1847 			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
1848 			eap->ex_addrlen = args.ua.export.ex_addr->sa_len;
1849 			eap->ex_mask =
1850 			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
1851 			eap->ex_masklen = args.ua.export.ex_mask->sa_len;
1852 			break;
1853 		case GT_DEFAULT:
1854 			eap->ex_addr = NULL;
1855 			eap->ex_addrlen = 0;
1856 			eap->ex_mask = NULL;
1857 			eap->ex_masklen = 0;
1858 			break;
1859 		case GT_IGNORE:
1860 			return(0);
1861 			break;
1862 		default:
1863 			syslog(LOG_ERR, "bad grouptype");
1864 			if (cp)
1865 				*cp = savedc;
1866 			return (1);
1867 		};
1868 
1869 		/*
1870 		 * XXX:
1871 		 * Maybe I should just use the fsb->f_mntonname path instead
1872 		 * of looping back up the dirp to the mount point??
1873 		 * Also, needs to know how to export all types of local
1874 		 * exportable filesystems and not just "ufs".
1875 		 */
1876 		while (mount(fsb->f_fstypename, dirp,
1877 		    fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1878 			if (cp)
1879 				*cp-- = savedc;
1880 			else
1881 				cp = dirp + dirplen - 1;
1882 			if (opt_flags & OP_QUIET)
1883 				return (1);
1884 			if (errno == EPERM) {
1885 				if (debug)
1886 					warnx("can't change attributes for %s",
1887 					    dirp);
1888 				syslog(LOG_ERR,
1889 				   "can't change attributes for %s", dirp);
1890 				return (1);
1891 			}
1892 			if (opt_flags & OP_ALLDIRS) {
1893 				if (errno == EINVAL)
1894 					syslog(LOG_ERR,
1895 		"-alldirs requested but %s is not a filesystem mountpoint",
1896 						dirp);
1897 				else
1898 					syslog(LOG_ERR,
1899 						"could not remount %s: %m",
1900 						dirp);
1901 				return (1);
1902 			}
1903 			/* back up over the last component */
1904 			while (*cp == '/' && cp > dirp)
1905 				cp--;
1906 			while (*(cp - 1) != '/' && cp > dirp)
1907 				cp--;
1908 			if (cp == dirp) {
1909 				if (debug)
1910 					warnx("mnt unsucc");
1911 				syslog(LOG_ERR, "can't export %s", dirp);
1912 				return (1);
1913 			}
1914 			savedc = *cp;
1915 			*cp = '\0';
1916 			/* Check that we're still on the same filesystem. */
1917 			if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
1918 			    &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
1919 				*cp = savedc;
1920 				syslog(LOG_ERR, "can't export %s", dirp);
1921 				return (1);
1922 			}
1923 		}
1924 skip:
1925 		if (ai != NULL)
1926 			ai = ai->ai_next;
1927 		if (ai == NULL)
1928 			done = TRUE;
1929 	}
1930 	if (cp)
1931 		*cp = savedc;
1932 	return (0);
1933 }
1934 
1935 /*
1936  * Translate a net address.
1937  *
1938  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
1939  */
1940 int
1941 get_net(cp, net, maskflg)
1942 	char *cp;
1943 	struct netmsk *net;
1944 	int maskflg;
1945 {
1946 	struct netent *np = NULL;
1947 	char *name, *p, *prefp;
1948 	struct sockaddr_in sin;
1949 	struct sockaddr *sa = NULL;
1950 	struct addrinfo hints, *ai = NULL;
1951 	char netname[NI_MAXHOST];
1952 	long preflen;
1953 
1954 	p = prefp = NULL;
1955 	if ((opt_flags & OP_MASKLEN) && !maskflg) {
1956 		p = strchr(cp, '/');
1957 		*p = '\0';
1958 		prefp = p + 1;
1959 	}
1960 
1961 	/*
1962 	 * Check for a numeric address first. We wish to avoid
1963 	 * possible DNS lookups in getnetbyname().
1964 	 */
1965 	if (isxdigit(*cp) || *cp == ':') {
1966 		memset(&hints, 0, sizeof hints);
1967 		/* Ensure the mask and the network have the same family. */
1968 		if (maskflg && (opt_flags & OP_NET))
1969 			hints.ai_family = net->nt_net.ss_family;
1970 		else if (!maskflg && (opt_flags & OP_HAVEMASK))
1971 			hints.ai_family = net->nt_mask.ss_family;
1972 		else
1973 			hints.ai_family = AF_UNSPEC;
1974 		hints.ai_flags = AI_NUMERICHOST;
1975 		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
1976 			sa = ai->ai_addr;
1977 		if (sa != NULL && ai->ai_family == AF_INET) {
1978 			/*
1979 			 * The address in `cp' is really a network address, so
1980 			 * use inet_network() to re-interpret this correctly.
1981 			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
1982 			 */
1983 			bzero(&sin, sizeof sin);
1984 			sin.sin_family = AF_INET;
1985 			sin.sin_len = sizeof sin;
1986 			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
1987 			if (debug)
1988 				fprintf(stderr, "get_net: v4 addr %s\n",
1989 				    inet_ntoa(sin.sin_addr));
1990 			sa = (struct sockaddr *)&sin;
1991 		}
1992 	}
1993 	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
1994 		bzero(&sin, sizeof sin);
1995 		sin.sin_family = AF_INET;
1996 		sin.sin_len = sizeof sin;
1997 		sin.sin_addr = inet_makeaddr(np->n_net, 0);
1998 		sa = (struct sockaddr *)&sin;
1999 	}
2000 	if (sa == NULL)
2001 		goto fail;
2002 
2003 	if (maskflg) {
2004 		/* The specified sockaddr is a mask. */
2005 		if (checkmask(sa) != 0)
2006 			goto fail;
2007 		bcopy(sa, &net->nt_mask, sa->sa_len);
2008 		opt_flags |= OP_HAVEMASK;
2009 	} else {
2010 		/* The specified sockaddr is a network address. */
2011 		bcopy(sa, &net->nt_net, sa->sa_len);
2012 
2013 		/* Get a network name for the export list. */
2014 		if (np) {
2015 			name = np->n_name;
2016 		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2017 		   NULL, 0, ninumeric) == 0) {
2018 			name = netname;
2019 		} else {
2020 			goto fail;
2021 		}
2022 		if ((net->nt_name = strdup(name)) == NULL)
2023 			out_of_mem();
2024 
2025 		/*
2026 		 * Extract a mask from either a "/<masklen>" suffix, or
2027 		 * from the class of an IPv4 address.
2028 		 */
2029 		if (opt_flags & OP_MASKLEN) {
2030 			preflen = strtol(prefp, NULL, 10);
2031 			if (preflen < 0L || preflen == LONG_MAX)
2032 				goto fail;
2033 			bcopy(sa, &net->nt_mask, sa->sa_len);
2034 			if (makemask(&net->nt_mask, (int)preflen) != 0)
2035 				goto fail;
2036 			opt_flags |= OP_HAVEMASK;
2037 			*p = '/';
2038 		} else if (sa->sa_family == AF_INET &&
2039 		    (opt_flags & OP_MASK) == 0) {
2040 			in_addr_t addr;
2041 
2042 			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2043 			if (IN_CLASSA(addr))
2044 				preflen = 8;
2045 			else if (IN_CLASSB(addr))
2046 				preflen = 16;
2047 			else if (IN_CLASSC(addr))
2048 				preflen = 24;
2049 			else if (IN_CLASSD(addr))
2050 				preflen = 28;
2051 			else
2052 				preflen = 32;	/* XXX */
2053 
2054 			bcopy(sa, &net->nt_mask, sa->sa_len);
2055 			makemask(&net->nt_mask, (int)preflen);
2056 			opt_flags |= OP_HAVEMASK;
2057 		}
2058 	}
2059 
2060 	if (ai)
2061 		freeaddrinfo(ai);
2062 	return 0;
2063 
2064 fail:
2065 	if (ai)
2066 		freeaddrinfo(ai);
2067 	return 1;
2068 }
2069 
2070 /*
2071  * Parse out the next white space separated field
2072  */
2073 void
2074 nextfield(cp, endcp)
2075 	char **cp;
2076 	char **endcp;
2077 {
2078 	char *p;
2079 
2080 	p = *cp;
2081 	while (*p == ' ' || *p == '\t')
2082 		p++;
2083 	if (*p == '\n' || *p == '\0')
2084 		*cp = *endcp = p;
2085 	else {
2086 		*cp = p++;
2087 		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2088 			p++;
2089 		*endcp = p;
2090 	}
2091 }
2092 
2093 /*
2094  * Get an exports file line. Skip over blank lines and handle line
2095  * continuations.
2096  */
2097 int
2098 get_line()
2099 {
2100 	char *p, *cp;
2101 	size_t len;
2102 	int totlen, cont_line;
2103 
2104 	/*
2105 	 * Loop around ignoring blank lines and getting all continuation lines.
2106 	 */
2107 	p = line;
2108 	totlen = 0;
2109 	do {
2110 		if ((p = fgetln(exp_file, &len)) == NULL)
2111 			return (0);
2112 		cp = p + len - 1;
2113 		cont_line = 0;
2114 		while (cp >= p &&
2115 		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2116 			if (*cp == '\\')
2117 				cont_line = 1;
2118 			cp--;
2119 			len--;
2120 		}
2121 		if (cont_line) {
2122 			*++cp = ' ';
2123 			len++;
2124 		}
2125 		if (linesize < len + totlen + 1) {
2126 			linesize = len + totlen + 1;
2127 			line = realloc(line, linesize);
2128 			if (line == NULL)
2129 				out_of_mem();
2130 		}
2131 		memcpy(line + totlen, p, len);
2132 		totlen += len;
2133 		line[totlen] = '\0';
2134 	} while (totlen == 0 || cont_line);
2135 	return (1);
2136 }
2137 
2138 /*
2139  * Parse a description of a credential.
2140  */
2141 void
2142 parsecred(namelist, cr)
2143 	char *namelist;
2144 	struct xucred *cr;
2145 {
2146 	char *name;
2147 	int cnt;
2148 	char *names;
2149 	struct passwd *pw;
2150 	struct group *gr;
2151 	int ngroups, groups[NGROUPS + 1];
2152 
2153 	cr->cr_version = XUCRED_VERSION;
2154 	/*
2155 	 * Set up the unprivileged user.
2156 	 */
2157 	cr->cr_uid = -2;
2158 	cr->cr_groups[0] = -2;
2159 	cr->cr_ngroups = 1;
2160 	/*
2161 	 * Get the user's password table entry.
2162 	 */
2163 	names = strsep(&namelist, " \t\n");
2164 	name = strsep(&names, ":");
2165 	if (isdigit(*name) || *name == '-')
2166 		pw = getpwuid(atoi(name));
2167 	else
2168 		pw = getpwnam(name);
2169 	/*
2170 	 * Credentials specified as those of a user.
2171 	 */
2172 	if (names == NULL) {
2173 		if (pw == NULL) {
2174 			syslog(LOG_ERR, "unknown user: %s", name);
2175 			return;
2176 		}
2177 		cr->cr_uid = pw->pw_uid;
2178 		ngroups = NGROUPS + 1;
2179 		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2180 			syslog(LOG_ERR, "too many groups");
2181 		/*
2182 		 * Convert from int's to gid_t's and compress out duplicate
2183 		 */
2184 		cr->cr_ngroups = ngroups - 1;
2185 		cr->cr_groups[0] = groups[0];
2186 		for (cnt = 2; cnt < ngroups; cnt++)
2187 			cr->cr_groups[cnt - 1] = groups[cnt];
2188 		return;
2189 	}
2190 	/*
2191 	 * Explicit credential specified as a colon separated list:
2192 	 *	uid:gid:gid:...
2193 	 */
2194 	if (pw != NULL)
2195 		cr->cr_uid = pw->pw_uid;
2196 	else if (isdigit(*name) || *name == '-')
2197 		cr->cr_uid = atoi(name);
2198 	else {
2199 		syslog(LOG_ERR, "unknown user: %s", name);
2200 		return;
2201 	}
2202 	cr->cr_ngroups = 0;
2203 	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2204 		name = strsep(&names, ":");
2205 		if (isdigit(*name) || *name == '-') {
2206 			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2207 		} else {
2208 			if ((gr = getgrnam(name)) == NULL) {
2209 				syslog(LOG_ERR, "unknown group: %s", name);
2210 				continue;
2211 			}
2212 			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2213 		}
2214 	}
2215 	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2216 		syslog(LOG_ERR, "too many groups");
2217 }
2218 
2219 #define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2220 /*
2221  * Routines that maintain the remote mounttab
2222  */
2223 void
2224 get_mountlist()
2225 {
2226 	struct mountlist *mlp, **mlpp;
2227 	char *host, *dirp, *cp;
2228 	char str[STRSIZ];
2229 	FILE *mlfile;
2230 
2231 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2232 		if (errno == ENOENT)
2233 			return;
2234 		else {
2235 			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2236 			return;
2237 		}
2238 	}
2239 	mlpp = &mlhead;
2240 	while (fgets(str, STRSIZ, mlfile) != NULL) {
2241 		cp = str;
2242 		host = strsep(&cp, " \t\n");
2243 		dirp = strsep(&cp, " \t\n");
2244 		if (host == NULL || dirp == NULL)
2245 			continue;
2246 		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2247 		if (mlp == (struct mountlist *)NULL)
2248 			out_of_mem();
2249 		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2250 		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2251 		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2252 		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2253 		mlp->ml_next = (struct mountlist *)NULL;
2254 		*mlpp = mlp;
2255 		mlpp = &mlp->ml_next;
2256 	}
2257 	fclose(mlfile);
2258 }
2259 
2260 void
2261 del_mlist(char *hostp, char *dirp)
2262 {
2263 	struct mountlist *mlp, **mlpp;
2264 	struct mountlist *mlp2;
2265 	FILE *mlfile;
2266 	int fnd = 0;
2267 
2268 	mlpp = &mlhead;
2269 	mlp = mlhead;
2270 	while (mlp) {
2271 		if (!strcmp(mlp->ml_host, hostp) &&
2272 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2273 			fnd = 1;
2274 			mlp2 = mlp;
2275 			*mlpp = mlp = mlp->ml_next;
2276 			free((caddr_t)mlp2);
2277 		} else {
2278 			mlpp = &mlp->ml_next;
2279 			mlp = mlp->ml_next;
2280 		}
2281 	}
2282 	if (fnd) {
2283 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2284 			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2285 			return;
2286 		}
2287 		mlp = mlhead;
2288 		while (mlp) {
2289 			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2290 			mlp = mlp->ml_next;
2291 		}
2292 		fclose(mlfile);
2293 	}
2294 }
2295 
2296 void
2297 add_mlist(hostp, dirp)
2298 	char *hostp, *dirp;
2299 {
2300 	struct mountlist *mlp, **mlpp;
2301 	FILE *mlfile;
2302 
2303 	mlpp = &mlhead;
2304 	mlp = mlhead;
2305 	while (mlp) {
2306 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2307 			return;
2308 		mlpp = &mlp->ml_next;
2309 		mlp = mlp->ml_next;
2310 	}
2311 	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2312 	if (mlp == (struct mountlist *)NULL)
2313 		out_of_mem();
2314 	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2315 	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2316 	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2317 	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2318 	mlp->ml_next = (struct mountlist *)NULL;
2319 	*mlpp = mlp;
2320 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2321 		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2322 		return;
2323 	}
2324 	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2325 	fclose(mlfile);
2326 }
2327 
2328 /*
2329  * Free up a group list.
2330  */
2331 void
2332 free_grp(grp)
2333 	struct grouplist *grp;
2334 {
2335 	if (grp->gr_type == GT_HOST) {
2336 		if (grp->gr_ptr.gt_addrinfo != NULL)
2337 			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2338 	} else if (grp->gr_type == GT_NET) {
2339 		if (grp->gr_ptr.gt_net.nt_name)
2340 			free(grp->gr_ptr.gt_net.nt_name);
2341 	}
2342 	free((caddr_t)grp);
2343 }
2344 
2345 #ifdef DEBUG
2346 void
2347 SYSLOG(int pri, const char *fmt, ...)
2348 {
2349 	va_list ap;
2350 
2351 	va_start(ap, fmt);
2352 	vfprintf(stderr, fmt, ap);
2353 	va_end(ap);
2354 }
2355 #endif /* DEBUG */
2356 
2357 /*
2358  * Check options for consistency.
2359  */
2360 int
2361 check_options(dp)
2362 	struct dirlist *dp;
2363 {
2364 
2365 	if (dp == (struct dirlist *)NULL)
2366 	    return (1);
2367 	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2368 	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2369 	    return (1);
2370 	}
2371 	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2372 		syslog(LOG_ERR, "-mask requires -network");
2373 		return (1);
2374 	}
2375 	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2376 		syslog(LOG_ERR, "-network requires mask specification");
2377 		return (1);
2378 	}
2379 	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
2380 		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
2381 		return (1);
2382 	}
2383 	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2384 	    syslog(LOG_ERR, "-alldirs has multiple directories");
2385 	    return (1);
2386 	}
2387 	return (0);
2388 }
2389 
2390 /*
2391  * Check an absolute directory path for any symbolic links. Return true
2392  */
2393 int
2394 check_dirpath(dirp)
2395 	char *dirp;
2396 {
2397 	char *cp;
2398 	int ret = 1;
2399 	struct stat sb;
2400 
2401 	cp = dirp + 1;
2402 	while (*cp && ret) {
2403 		if (*cp == '/') {
2404 			*cp = '\0';
2405 			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2406 				ret = 0;
2407 			*cp = '/';
2408 		}
2409 		cp++;
2410 	}
2411 	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2412 		ret = 0;
2413 	return (ret);
2414 }
2415 
2416 /*
2417  * Make a netmask according to the specified prefix length. The ss_family
2418  * and other non-address fields must be initialised before calling this.
2419  */
2420 int
2421 makemask(struct sockaddr_storage *ssp, int bitlen)
2422 {
2423 	u_char *p;
2424 	int bits, i, len;
2425 
2426 	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
2427 		return (-1);
2428 	if (bitlen > len * CHAR_BIT)
2429 		return (-1);
2430 
2431 	for (i = 0; i < len; i++) {
2432 		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
2433 		*p++ = (1 << bits) - 1;
2434 		bitlen -= bits;
2435 	}
2436 	return 0;
2437 }
2438 
2439 /*
2440  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
2441  * is acceptable (i.e. of the form 1...10....0).
2442  */
2443 int
2444 checkmask(struct sockaddr *sa)
2445 {
2446 	u_char *mask;
2447 	int i, len;
2448 
2449 	if ((mask = sa_rawaddr(sa, &len)) == NULL)
2450 		return (-1);
2451 
2452 	for (i = 0; i < len; i++)
2453 		if (mask[i] != 0xff)
2454 			break;
2455 	if (i < len) {
2456 		if (~mask[i] & (u_char)(~mask[i] + 1))
2457 			return (-1);
2458 		i++;
2459 	}
2460 	for (; i < len; i++)
2461 		if (mask[i] != 0)
2462 			return (-1);
2463 	return (0);
2464 }
2465 
2466 /*
2467  * Compare two sockaddrs according to a specified mask. Return zero if
2468  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
2469  * If samask is NULL, perform a full comparision.
2470  */
2471 int
2472 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
2473 {
2474 	unsigned char *p1, *p2, *mask;
2475 	int len, i;
2476 
2477 	if (sa1->sa_family != sa2->sa_family ||
2478 	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
2479 	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
2480 		return (1);
2481 
2482 	switch (sa1->sa_family) {
2483 	case AF_INET6:
2484 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
2485 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
2486 			return (1);
2487 		break;
2488 	}
2489 
2490 	/* Simple binary comparison if no mask specified. */
2491 	if (samask == NULL)
2492 		return (memcmp(p1, p2, len));
2493 
2494 	/* Set up the mask, and do a mask-based comparison. */
2495 	if (sa1->sa_family != samask->sa_family ||
2496 	    (mask = sa_rawaddr(samask, NULL)) == NULL)
2497 		return (1);
2498 
2499 	for (i = 0; i < len; i++)
2500 		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
2501 			return (1);
2502 	return (0);
2503 }
2504 
2505 /*
2506  * Return a pointer to the part of the sockaddr that contains the
2507  * raw address, and set *nbytes to its length in bytes. Returns
2508  * NULL if the address family is unknown.
2509  */
2510 void *
2511 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
2512 	void *p;
2513 	int len;
2514 
2515 	switch (sa->sa_family) {
2516 	case AF_INET:
2517 		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
2518 		p = &((struct sockaddr_in *)sa)->sin_addr;
2519 		break;
2520 	case AF_INET6:
2521 		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
2522 		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
2523 		break;
2524 	default:
2525 		p = NULL;
2526 		len = 0;
2527 	}
2528 
2529 	if (nbytes != NULL)
2530 		*nbytes = len;
2531 	return (p);
2532 }
2533 
2534 void
2535 huphandler(int sig)
2536 {
2537 	got_sighup = 1;
2538 }
2539 
2540 void terminate(sig)
2541 int sig;
2542 {
2543 	close(mountdlockfd);
2544 	unlink(MOUNTDLOCK);
2545 	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
2546 	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
2547 	exit (0);
2548 }
2549