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