mountd.c revision 121556
1116742Ssam/*
2116904Ssam * Copyright (c) 1989, 1993
3139530Ssam *	The Regents of the University of California.  All rights reserved.
4116742Ssam *
5116742Ssam * This code is derived from software contributed to Berkeley by
6116742Ssam * Herb Hasler and Rick Macklem at The University of Guelph.
7116742Ssam *
8116742Ssam * Redistribution and use in source and binary forms, with or without
9116742Ssam * modification, are permitted provided that the following conditions
10116904Ssam * are met:
11116904Ssam * 1. Redistributions of source code must retain the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer.
13116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
14116904Ssam *    notice, this list of conditions and the following disclaimer in the
15116904Ssam *    documentation and/or other materials provided with the distribution.
16116742Ssam * 3. All advertising materials mentioning features or use of this software
17116742Ssam *    must display the following acknowledgement:
18116742Ssam *	This product includes software developed by the University of
19116742Ssam *	California, Berkeley and its contributors.
20116742Ssam * 4. Neither the name of the University nor the names of its contributors
21116904Ssam *    may be used to endorse or promote products derived from this software
22116904Ssam *    without specific prior written permission.
23116904Ssam *
24116904Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25116904Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27116904Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28116904Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29116904Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30116904Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31116742Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32116742Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33116742Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34116742Ssam * SUCH DAMAGE.
35116742Ssam */
36116742Ssam
37116742Ssam#ifndef lint
38116742Ssamstatic const char copyright[] =
39116742Ssam"@(#) Copyright (c) 1989, 1993\n\
40116742Ssam	The Regents of the University of California.  All rights reserved.\n";
41138568Ssam#endif /*not lint*/
42116742Ssam
43116742Ssam#if 0
44116742Ssam#ifndef lint
45116742Ssamstatic char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
46116742Ssam#endif /*not lint*/
47116742Ssam#endif
48116742Ssam
49116742Ssam#include <sys/cdefs.h>
50116742Ssam__FBSDID("$FreeBSD: head/usr.sbin/mountd/mountd.c 121556 2003-10-26 05:58:21Z peter $");
51116742Ssam
52147221Ssam#include <sys/param.h>
53147221Ssam#include <sys/mount.h>
54147221Ssam#include <sys/fcntl.h>
55147221Ssam#include <sys/stat.h>
56147221Ssam#include <sys/syslog.h>
57147221Ssam#include <sys/sysctl.h>
58147221Ssam#include <sys/linker.h>
59147221Ssam#include <sys/module.h>
60147221Ssam
61147221Ssam#include <rpc/rpc.h>
62138568Ssam#include <rpc/rpc_com.h>
63138568Ssam#include <rpc/pmap_clnt.h>
64138568Ssam#include <rpc/pmap_prot.h>
65138568Ssam#include <rpcsvc/mount.h>
66116742Ssam#include <nfs/rpcv2.h>
67138568Ssam#include <nfs/nfsproto.h>
68138568Ssam#include <nfsserver/nfs.h>
69138568Ssam#include <ufs/ufs/ufsmount.h>
70138568Ssam#include <fs/msdosfs/msdosfsmount.h>
71120104Ssam#include <fs/ntfs/ntfsmount.h>
72138568Ssam#include <isofs/cd9660/cd9660_mount.h>	/* XXX need isofs in include */
73138568Ssam
74116742Ssam#include <arpa/inet.h>
75148304Ssam
76138568Ssam#include <ctype.h>
77138568Ssam#include <err.h>
78148863Ssam#include <errno.h>
79148863Ssam#include <grp.h>
80138568Ssam#include <limits.h>
81138568Ssam#include <netdb.h>
82138568Ssam#include <pwd.h>
83127876Ssam#include <signal.h>
84120481Ssam#include <stdio.h>
85116742Ssam#include <stdlib.h>
86138568Ssam#include <string.h>
87116742Ssam#include <unistd.h>
88116742Ssam#include "pathnames.h"
89138568Ssam
90138568Ssam#ifdef DEBUG
91138568Ssam#include <stdarg.h>
92138568Ssam#endif
93138568Ssam
94138568Ssam#ifndef MOUNTDLOCK
95138568Ssam#define MOUNTDLOCK "/var/run/mountd.lock"
96138568Ssam#endif
97138568Ssam
98138568Ssam/*
99138568Ssam * Structures for keeping the mount list and export list
100148863Ssam */
101148863Ssamstruct mountlist {
102148863Ssam	struct mountlist *ml_next;
103148863Ssam	char	ml_host[RPCMNT_NAMELEN+1];
104148863Ssam	char	ml_dirp[RPCMNT_PATHLEN+1];
105148863Ssam};
106148863Ssam
107148863Ssamstruct dirlist {
108148863Ssam	struct dirlist	*dp_left;
109148863Ssam	struct dirlist	*dp_right;
110148863Ssam	int		dp_flag;
111138568Ssam	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
112138568Ssam	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
113138568Ssam};
114138568Ssam/* dp_flag bits */
115138568Ssam#define	DP_DEFSET	0x1
116138568Ssam#define DP_HOSTSET	0x2
117138568Ssam
118138568Ssamstruct exportlist {
119138568Ssam	struct exportlist *ex_next;
120138568Ssam	struct dirlist	*ex_dirl;
121138568Ssam	struct dirlist	*ex_defdir;
122138568Ssam	int		ex_flag;
123138568Ssam	fsid_t		ex_fs;
124138568Ssam	char		*ex_fsdir;
125138568Ssam	char		*ex_indexfile;
126138568Ssam};
127138568Ssam/* ex_flag bits */
128138568Ssam#define	EX_LINKED	0x1
129118887Ssam
130148863Ssamstruct netmsk {
131148863Ssam	struct sockaddr_storage nt_net;
132148863Ssam	struct sockaddr_storage nt_mask;
133148863Ssam	char		*nt_name;
134148863Ssam};
135148863Ssam
136118887Ssamunion grouptypes {
137148863Ssam	struct addrinfo *gt_addrinfo;
138138568Ssam	struct netmsk	gt_net;
139138568Ssam};
140138568Ssam
141138568Ssamstruct grouplist {
142148863Ssam	int gr_type;
143138568Ssam	union grouptypes gr_ptr;
144138568Ssam	struct grouplist *gr_next;
145138568Ssam};
146138568Ssam/* Group types */
147138568Ssam#define	GT_NULL		0x0
148138568Ssam#define	GT_HOST		0x1
149138568Ssam#define	GT_NET		0x2
150138568Ssam#define	GT_DEFAULT	0x3
151138568Ssam#define GT_IGNORE	0x5
152138568Ssam
153138568Ssamstruct hostlist {
154138568Ssam	int		 ht_flag;	/* Uses DP_xx bits */
155138568Ssam	struct grouplist *ht_grp;
156138568Ssam	struct hostlist	 *ht_next;
157138568Ssam};
158138568Ssam
159138568Ssamstruct fhreturn {
160138568Ssam	int	fhr_flag;
161138568Ssam	int	fhr_vers;
162138568Ssam	nfsfh_t	fhr_fh;
163138568Ssam};
164138568Ssam
165138568Ssam/* Global defs */
166138568Ssamchar	*add_expdir(struct dirlist **, char *, int);
167138568Ssamvoid	add_dlist(struct dirlist **, struct dirlist *,
168138568Ssam				struct grouplist *, int);
169138568Ssamvoid	add_mlist(char *, char *);
170138568Ssamint	check_dirpath(char *);
171138568Ssamint	check_options(struct dirlist *);
172138568Ssamint	checkmask(struct sockaddr *sa);
173138568Ssamint	chk_host(struct dirlist *, struct sockaddr *, int *, int *);
174148863Ssamvoid	del_mlist(char *hostp, char *dirp);
175116742Ssamstruct dirlist *dirp_search(struct dirlist *, char *);
176116742Ssamint	do_mount(struct exportlist *, struct grouplist *, int,
177116742Ssam		struct xucred *, char *, int, struct statfs *);
178138568Ssamint	do_opt(char **, char **, struct exportlist *, struct grouplist *,
179116742Ssam				int *, int *, struct xucred *);
180116742Ssamstruct	exportlist *ex_search(fsid_t *);
181138568Ssamstruct	exportlist *get_exp(void);
182138568Ssamvoid	free_dir(struct dirlist *);
183138568Ssamvoid	free_exp(struct exportlist *);
184138568Ssamvoid	free_grp(struct grouplist *);
185138568Ssamvoid	free_host(struct hostlist *);
186140753Ssamvoid	get_exportlist(void);
187138568Ssamint	get_host(char *, struct grouplist *, struct grouplist *);
188138568Ssamstruct hostlist *get_ht(void);
189138568Ssamint	get_line(void);
190138568Ssamvoid	get_mountlist(void);
191138568Ssamint	get_net(char *, struct netmsk *, int);
192138568Ssamvoid	getexp_err(struct exportlist *, struct grouplist *);
193138568Ssamstruct grouplist *get_grp(void);
194138568Ssamvoid	hang_dirp(struct dirlist *, struct grouplist *,
195116742Ssam				struct exportlist *, int);
196116742Ssamvoid	huphandler(int sig);
197138568Ssamint	makemask(struct sockaddr_storage *ssp, int bitlen);
198138568Ssamvoid	mntsrv(struct svc_req *, SVCXPRT *);
199138568Ssamvoid	nextfield(char **, char **);
200138568Ssamvoid	out_of_mem(void);
201138568Ssamvoid	parsecred(char *, struct xucred *);
202148302Ssamint	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
203138568Ssamvoid	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
204148302Ssamint	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
205148302Ssam    struct sockaddr *samask);
206138568Ssamint	scan_tree(struct dirlist *, struct sockaddr *);
207139528Ssamstatic void usage(void);
208138568Ssamint	xdr_dir(XDR *, char *);
209138568Ssamint	xdr_explist(XDR *, caddr_t);
210138568Ssamint	xdr_explist_brief(XDR *, caddr_t);
211148302Ssamint	xdr_fhs(XDR *, caddr_t);
212138568Ssamint	xdr_mlist(XDR *, caddr_t);
213138568Ssamvoid	terminate(int);
214138568Ssam
215138568Ssamstruct exportlist *exphead;
216116742Ssamstruct mountlist *mlhead;
217138568Ssamstruct grouplist *grphead;
218138568Ssamchar exname[MAXPATHLEN];
219138568Ssamstruct xucred def_anon = {
220153351Ssam	XUCRED_VERSION,
221138568Ssam	(uid_t)-2,
222138568Ssam	1,
223138568Ssam	{ (gid_t)-2 },
224153351Ssam	NULL
225153351Ssam};
226138568Ssamint force_v2 = 0;
227138568Ssamint resvport_only = 1;
228138568Ssamint dir_only = 1;
229138568Ssamint log = 0;
230138568Ssamint got_sighup = 0;
231116742Ssam
232116742Ssamint opt_flags;
233116742Ssamstatic int have_v6 = 1;
234138568Ssam#ifdef NI_WITHSCOPEID
235138568Ssamstatic const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
236138568Ssam#else
237138568Ssamstatic const int ninumeric = NI_NUMERICHOST;
238138568Ssam#endif
239138568Ssam
240138568Ssamint mountdlockfd;
241138568Ssam/* Bits for opt_flags above */
242138568Ssam#define	OP_MAPROOT	0x01
243138568Ssam#define	OP_MAPALL	0x02
244138568Ssam/* 0x4 free */
245138568Ssam#define	OP_MASK		0x08
246138568Ssam#define	OP_NET		0x10
247138568Ssam#define	OP_ALLDIRS	0x40
248138568Ssam#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
249138568Ssam#define	OP_QUIET	0x100
250116742Ssam#define OP_MASKLEN	0x200
251138568Ssam
252116742Ssam#ifdef DEBUG
253116742Ssamint debug = 1;
254117811Ssamvoid	SYSLOG(int, const char *, ...) __printflike(2, 3);
255138568Ssam#define syslog SYSLOG
256116742Ssam#else
257116742Ssamint debug = 0;
258138568Ssam#endif
259138568Ssam
260138568Ssam/*
261138568Ssam * Mountd server for NFS mount protocol as described in:
262138568Ssam * NFS: Network File System Protocol Specification, RFC1094, Appendix A
263138568Ssam * The optional arguments are the exports file name
264138568Ssam * default: _PATH_EXPORTS
265138568Ssam * and "-n" to allow nonroot mount.
266138568Ssam */
267138568Ssamint
268138568Ssammain(argc, argv)
269138568Ssam	int argc;
270138568Ssam	char **argv;
271148936Ssam{
272138568Ssam	fd_set readfds;
273138568Ssam	SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
274116742Ssam	struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
275116742Ssam	int udpsock, tcpsock, udp6sock, tcp6sock;
276116742Ssam	int xcreated = 0, s;
277116742Ssam	int maxrec = RPC_MAXDATASIZE;
278116742Ssam	int one = 1;
279116742Ssam	int c;
280138568Ssam
281116742Ssam	udp6conf = tcp6conf = NULL;
282116742Ssam	udp6sock = tcp6sock = NULL;
283138568Ssam
284117811Ssam	/* Check that another mountd isn't already running. */
285117811Ssam	if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
286117811Ssam		err(1, "%s", MOUNTDLOCK);
287117811Ssam
288121180Ssam	if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
289117811Ssam		errx(1, "another mountd is already running. Aborting");
290121180Ssam	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
291121180Ssam	if (s < 0)
292121180Ssam		have_v6 = 0;
293139520Ssam	else
294139520Ssam		close(s);
295138568Ssam	if (modfind("nfsserver") < 0) {
296139520Ssam		/* Not present in kernel, try loading it */
297116742Ssam		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
298138568Ssam			errx(1, "NFS server is not available or loadable");
299116742Ssam	}
300138568Ssam
301138568Ssam	while ((c = getopt(argc, argv, "2dlnr")) != -1)
302138568Ssam		switch (c) {
303116742Ssam		case '2':
304138568Ssam			force_v2 = 1;
305138568Ssam			break;
306117811Ssam		case 'n':
307138568Ssam			resvport_only = 0;
308116742Ssam			break;
309116742Ssam		case 'r':
310116742Ssam			dir_only = 0;
311116742Ssam			break;
312116742Ssam		case 'd':
313138568Ssam			debug = debug ? 0 : 1;
314138568Ssam			break;
315116742Ssam		case 'l':
316116742Ssam			log = 1;
317116742Ssam			break;
318138568Ssam		default:
319138568Ssam			usage();
320138568Ssam		};
321138568Ssam	argc -= optind;
322138568Ssam	argv += optind;
323138568Ssam	grphead = (struct grouplist *)NULL;
324138568Ssam	exphead = (struct exportlist *)NULL;
325148936Ssam	mlhead = (struct mountlist *)NULL;
326138568Ssam	if (argc == 1) {
327116742Ssam		strncpy(exname, *argv, MAXPATHLEN-1);
328116742Ssam		exname[MAXPATHLEN-1] = '\0';
329116742Ssam	} else
330138568Ssam		strcpy(exname, _PATH_EXPORTS);
331138568Ssam	openlog("mountd", LOG_PID, LOG_DAEMON);
332138568Ssam	if (debug)
333148936Ssam		warnx("getting export list");
334138568Ssam	get_exportlist();
335148936Ssam	if (debug)
336148936Ssam		warnx("getting mount list");
337148936Ssam	get_mountlist();
338148936Ssam	if (debug)
339148936Ssam		warnx("here we go");
340148936Ssam	if (debug == 0) {
341148936Ssam		daemon(0, 0);
342138568Ssam		signal(SIGINT, SIG_IGN);
343138568Ssam		signal(SIGQUIT, SIG_IGN);
344116742Ssam	}
345148936Ssam	signal(SIGHUP, huphandler);
346138568Ssam	signal(SIGTERM, terminate);
347138568Ssam	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
348116742Ssam	  if (pidfile != NULL) {
349116742Ssam		fprintf(pidfile, "%d\n", getpid());
350141658Ssam		fclose(pidfile);
351141658Ssam	  }
352141658Ssam	}
353141658Ssam	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
354141658Ssam	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
355141658Ssam	udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
356141658Ssam	tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
357141658Ssam	udpconf  = getnetconfigent("udp");
358141658Ssam	tcpconf  = getnetconfigent("tcp");
359141658Ssam
360141658Ssam	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
361116742Ssam
362116742Ssam	if (!have_v6)
363116742Ssam		goto skip_v6;
364140753Ssam	udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
365116742Ssam	tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
366116742Ssam	/*
367138568Ssam	 * We're doing host-based access checks here, so don't allow
368138568Ssam	 * v4-in-v6 to confuse things. The kernel will disable it
369138568Ssam	 * by default on NFS sockets too.
370138568Ssam	 */
371138568Ssam	if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
372138568Ssam		IPV6_V6ONLY, &one, sizeof one) < 0) {
373138568Ssam		syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
374138568Ssam		exit(1);
375138568Ssam	}
376140753Ssam	if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
377140753Ssam		IPV6_V6ONLY, &one, sizeof one) < 0) {
378140753Ssam		syslog(LOG_ERR, "can't disable v4-in-v6 on TCP socket");
379140753Ssam		exit(1);
380140753Ssam	}
381140753Ssam	udp6conf = getnetconfigent("udp6");
382140753Ssam	tcp6conf = getnetconfigent("tcp6");
383140753Ssam
384140753Ssamskip_v6:
385140753Ssam	if (!resvport_only) {
386140753Ssam		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
387148863Ssam		    &resvport_only, sizeof(resvport_only)) != 0 &&
388140753Ssam		    errno != ENOENT) {
389140753Ssam			syslog(LOG_ERR, "sysctl: %m");
390138568Ssam			exit(1);
391138568Ssam		}
392116742Ssam	}
393116742Ssam	if (udpsock != -1 && udpconf != NULL) {
394116742Ssam		bindresvport(udpsock, NULL);
395141658Ssam		udptransp = svc_dg_create(udpsock, 0, 0);
396148843Ssam		if (udptransp != NULL) {
397138568Ssam			if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
398116742Ssam			    mntsrv, udpconf))
399116742Ssam				syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
400116742Ssam			else
401116742Ssam				xcreated++;
402116742Ssam			if (!force_v2) {
403138568Ssam				if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
404138568Ssam				    mntsrv, udpconf))
405138568Ssam					syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
406143300Ssam				else
407143300Ssam					xcreated++;
408143300Ssam			}
409143300Ssam		} else
410153403Ssam			syslog(LOG_WARNING, "can't create UDP services");
411153403Ssam
412153403Ssam	}
413153403Ssam	if (tcpsock != -1 && tcpconf != NULL) {
414153403Ssam		bindresvport(tcpsock, NULL);
415138568Ssam		listen(tcpsock, SOMAXCONN);
416138568Ssam		tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
417138568Ssam		if (tcptransp != NULL) {
418138568Ssam			if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
419138568Ssam			    mntsrv, tcpconf))
420148936Ssam				syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
421138568Ssam			else
422138568Ssam				xcreated++;
423138568Ssam			if (!force_v2) {
424138568Ssam				if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
425138568Ssam				    mntsrv, tcpconf))
426138568Ssam					syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
427138568Ssam				else
428138568Ssam					xcreated++;
429138568Ssam			}
430138568Ssam		} else
431138568Ssam			syslog(LOG_WARNING, "can't create TCP service");
432138568Ssam
433138568Ssam	}
434138568Ssam	if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
435138568Ssam		bindresvport(udp6sock, NULL);
436138568Ssam		udp6transp = svc_dg_create(udp6sock, 0, 0);
437140753Ssam		if (udp6transp != NULL) {
438116742Ssam			if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
439116742Ssam			    mntsrv, udp6conf))
440138568Ssam				syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
441138568Ssam			else
442138568Ssam				xcreated++;
443138568Ssam			if (!force_v2) {
444138568Ssam				if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
445138568Ssam				    mntsrv, udp6conf))
446140753Ssam					syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
447140753Ssam				else
448138568Ssam					xcreated++;
449138568Ssam			}
450138568Ssam		} else
451138568Ssam			syslog(LOG_WARNING, "can't create UDP6 service");
452141658Ssam
453141658Ssam	}
454148843Ssam	if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
455138568Ssam		bindresvport(tcp6sock, NULL);
456141658Ssam		listen(tcp6sock, SOMAXCONN);
457138568Ssam		tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
458138568Ssam		if (tcp6transp != NULL) {
459148432Ssam			if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
460148432Ssam			    mntsrv, tcp6conf))
461148432Ssam				syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
462127767Ssam			else
463138568Ssam				xcreated++;
464127767Ssam			if (!force_v2) {
465127767Ssam				if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
466127767Ssam				    mntsrv, tcp6conf))
467127767Ssam					syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
468127767Ssam					else
469127767Ssam						xcreated++;
470127767Ssam				}
471127767Ssam		} else
472127767Ssam			syslog(LOG_WARNING, "can't create TCP6 service");
473127767Ssam
474127767Ssam	}
475127767Ssam	if (xcreated == 0) {
476127767Ssam		syslog(LOG_ERR, "could not create any services");
477127767Ssam		exit(1);
478127767Ssam	}
479127767Ssam
480127767Ssam	/* Expand svc_run() here so that we can call get_exportlist(). */
481138568Ssam	for (;;) {
482127767Ssam		if (got_sighup) {
483127767Ssam			get_exportlist();
484127767Ssam			got_sighup = 0;
485127767Ssam		}
486127767Ssam		readfds = svc_fdset;
487127767Ssam		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
488127767Ssam		case -1:
489148299Ssam			if (errno == EINTR)
490127767Ssam                                continue;
491127767Ssam			syslog(LOG_ERR, "mountd died: select: %m");
492127767Ssam			exit(1);
493127767Ssam		case 0:
494127767Ssam			continue;
495127767Ssam		default:
496127767Ssam			svc_getreqset(&readfds);
497127767Ssam		}
498127767Ssam	}
499148432Ssam}
500148432Ssam
501127767Ssamstatic void
502138568Ssamusage()
503148432Ssam{
504148432Ssam	fprintf(stderr,
505127767Ssam		"usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
506127767Ssam	exit(1);
507127767Ssam}
508127767Ssam
509127767Ssam/*
510127767Ssam * The mount rpc service
511127767Ssam */
512127767Ssamvoid
513127767Ssammntsrv(rqstp, transp)
514127767Ssam	struct svc_req *rqstp;
515127767Ssam	SVCXPRT *transp;
516127767Ssam{
517127767Ssam	struct exportlist *ep;
518127767Ssam	struct dirlist *dp;
519127767Ssam	struct fhreturn fhr;
520127767Ssam	struct stat stb;
521127767Ssam	struct statfs fsb;
522127767Ssam	struct addrinfo *ai;
523127767Ssam	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
524127767Ssam	int lookup_failed = 1;
525127767Ssam	struct sockaddr *saddr;
526127767Ssam	u_short sport;
527127767Ssam	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
528127767Ssam	int bad = 0, defset, hostset;
529138568Ssam	sigset_t sighup_mask;
530138568Ssam
531138568Ssam	sigemptyset(&sighup_mask);
532138568Ssam	sigaddset(&sighup_mask, SIGHUP);
533138568Ssam	saddr = svc_getrpccaller(transp)->buf;
534138568Ssam	switch (saddr->sa_family) {
535138568Ssam	case AF_INET6:
536138568Ssam		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
537116742Ssam		break;
538138568Ssam	case AF_INET:
539138568Ssam		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
540138568Ssam		break;
541138568Ssam	default:
542138568Ssam		syslog(LOG_ERR, "request from unknown address family");
543138568Ssam		return;
544138568Ssam	}
545138568Ssam	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
546138568Ssam	    NULL, 0, 0);
547138568Ssam	getnameinfo(saddr, saddr->sa_len, numerichost,
548138568Ssam	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
549138568Ssam	ai = NULL;
550138568Ssam	switch (rqstp->rq_proc) {
551148432Ssam	case NULLPROC:
552138568Ssam		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
553138568Ssam			syslog(LOG_ERR, "can't send reply");
554138568Ssam		return;
555138568Ssam	case RPCMNT_MOUNT:
556138568Ssam		if (sport >= IPPORT_RESERVED && resvport_only) {
557138568Ssam			syslog(LOG_NOTICE,
558138568Ssam			    "mount request from %s from unprivileged port",
559138568Ssam			    numerichost);
560138568Ssam			svcerr_weakauth(transp);
561148432Ssam			return;
562148432Ssam		}
563148432Ssam		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
564148432Ssam			syslog(LOG_NOTICE, "undecodable mount request from %s",
565148432Ssam			    numerichost);
566138568Ssam			svcerr_decode(transp);
567138568Ssam			return;
568139543Ssam		}
569139543Ssam
570139543Ssam		/*
571139543Ssam		 * Get the real pathname and make sure it is a directory
572139543Ssam		 * or a regular file if the -r option was specified
573139543Ssam		 * and it exists.
574139543Ssam		 */
575139543Ssam		if (realpath(rpcpath, dirpath) == NULL ||
576139543Ssam		    stat(dirpath, &stb) < 0 ||
577139543Ssam		    (!S_ISDIR(stb.st_mode) &&
578139543Ssam		    (dir_only || !S_ISREG(stb.st_mode))) ||
579139543Ssam		    statfs(dirpath, &fsb) < 0) {
580139543Ssam			chdir("/");	/* Just in case realpath doesn't */
581139543Ssam			syslog(LOG_NOTICE,
582139543Ssam			    "mount request from %s for non existent path %s",
583138568Ssam			    numerichost, dirpath);
584138568Ssam			if (debug)
585138568Ssam				warnx("stat failed on %s", dirpath);
586138568Ssam			bad = ENOENT;	/* We will send error reply later */
587138568Ssam		}
588140441Ssam
589140441Ssam		/* Check in the exports list */
590140441Ssam		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
591140441Ssam		ep = ex_search(&fsb.f_fsid);
592140441Ssam		hostset = defset = 0;
593140441Ssam		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
594140441Ssam		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
595140441Ssam		      chk_host(dp, saddr, &defset, &hostset)) ||
596140441Ssam		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
597140441Ssam		     scan_tree(ep->ex_dirl, saddr) == 0))) {
598140441Ssam			if (bad) {
599140441Ssam				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
600140441Ssam				    (caddr_t)&bad))
601140441Ssam					syslog(LOG_ERR, "can't send reply");
602116742Ssam				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
603116742Ssam				return;
604116742Ssam			}
605138568Ssam			if (hostset & DP_HOSTSET)
606116742Ssam				fhr.fhr_flag = hostset;
607140448Ssam			else
608140448Ssam				fhr.fhr_flag = defset;
609116742Ssam			fhr.fhr_vers = rqstp->rq_vers;
610140441Ssam			/* Get the file handle */
611140441Ssam			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
612116742Ssam			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
613116742Ssam				bad = errno;
614138568Ssam				syslog(LOG_ERR, "can't get fh for %s", dirpath);
615138568Ssam				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
616138568Ssam				    (caddr_t)&bad))
617138568Ssam					syslog(LOG_ERR, "can't send reply");
618116742Ssam				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
619116742Ssam				return;
620116742Ssam			}
621116742Ssam			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
622140753Ssam			    (caddr_t)&fhr))
623138568Ssam				syslog(LOG_ERR, "can't send reply");
624116742Ssam			if (!lookup_failed)
625138568Ssam				add_mlist(host, dirpath);
626140448Ssam			else
627140448Ssam				add_mlist(numerichost, dirpath);
628138568Ssam			if (debug)
629138568Ssam				warnx("mount successful");
630138568Ssam			if (log)
631138568Ssam				syslog(LOG_NOTICE,
632116742Ssam				    "mount request succeeded from %s for %s",
633140448Ssam				    numerichost, dirpath);
634138568Ssam		} else {
635138568Ssam			bad = EACCES;
636116742Ssam			syslog(LOG_NOTICE,
637138568Ssam			    "mount request denied from %s for %s",
638138568Ssam			    numerichost, dirpath);
639138568Ssam		}
640138568Ssam
641138568Ssam		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
642138568Ssam		    (caddr_t)&bad))
643138568Ssam			syslog(LOG_ERR, "can't send reply");
644138568Ssam		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
645138568Ssam		return;
646116742Ssam	case RPCMNT_DUMP:
647138568Ssam		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
648143715Ssam			syslog(LOG_ERR, "can't send reply");
649143715Ssam		else if (log)
650138568Ssam			syslog(LOG_NOTICE,
651138568Ssam			    "dump request succeeded from %s",
652138568Ssam			    numerichost);
653138568Ssam		return;
654138568Ssam	case RPCMNT_UMOUNT:
655116742Ssam		if (sport >= IPPORT_RESERVED && resvport_only) {
656138568Ssam			syslog(LOG_NOTICE,
657138568Ssam			    "umount request from %s from unprivileged port",
658138568Ssam			    numerichost);
659138568Ssam			svcerr_weakauth(transp);
660138568Ssam			return;
661138568Ssam		}
662138568Ssam		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
663138568Ssam			syslog(LOG_NOTICE, "undecodable umount request from %s",
664138568Ssam			    numerichost);
665138568Ssam			svcerr_decode(transp);
666116742Ssam			return;
667138568Ssam		}
668138568Ssam		if (realpath(rpcpath, dirpath) == NULL) {
669138568Ssam			syslog(LOG_NOTICE, "umount request from %s "
670138568Ssam			    "for non existent path %s",
671140448Ssam			    numerichost, dirpath);
672140448Ssam		}
673138568Ssam		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
674138568Ssam			syslog(LOG_ERR, "can't send reply");
675116742Ssam		if (!lookup_failed)
676116742Ssam			del_mlist(host, dirpath);
677116742Ssam		del_mlist(numerichost, dirpath);
678116742Ssam		if (log)
679116742Ssam			syslog(LOG_NOTICE,
680116742Ssam			    "umount request succeeded from %s for %s",
681116742Ssam			    numerichost, dirpath);
682116742Ssam		return;
683148432Ssam	case RPCMNT_UMNTALL:
684148432Ssam		if (sport >= IPPORT_RESERVED && resvport_only) {
685148432Ssam			syslog(LOG_NOTICE,
686153352Ssam			    "umountall request from %s from unprivileged port",
687148432Ssam			    numerichost);
688148432Ssam			svcerr_weakauth(transp);
689148432Ssam			return;
690148432Ssam		}
691148432Ssam		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
692148432Ssam			syslog(LOG_ERR, "can't send reply");
693148432Ssam		if (!lookup_failed)
694116742Ssam			del_mlist(host, NULL);
695116742Ssam		del_mlist(numerichost, NULL);
696138568Ssam		if (log)
697138568Ssam			syslog(LOG_NOTICE,
698138568Ssam			    "umountall request succeeded from %s",
699116742Ssam			    numerichost);
700116742Ssam		return;
701116742Ssam	case RPCMNT_EXPORT:
702138568Ssam		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
703138568Ssam			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
704140448Ssam			    (caddr_t)NULL))
705140448Ssam				syslog(LOG_ERR, "can't send reply");
706138568Ssam		if (log)
707116742Ssam			syslog(LOG_NOTICE,
708116742Ssam			    "export request succeeded from %s",
709140448Ssam			    numerichost);
710116742Ssam		return;
711116742Ssam	default:
712116742Ssam		svcerr_noproc(transp);
713140448Ssam		return;
714140448Ssam	}
715140448Ssam}
716116742Ssam
717116742Ssam/*
718138568Ssam * Xdr conversion for a dirpath string
719140448Ssam */
720138568Ssamint
721138568Ssamxdr_dir(xdrsp, dirp)
722138568Ssam	XDR *xdrsp;
723138568Ssam	char *dirp;
724138568Ssam{
725138568Ssam	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
726138568Ssam}
727138568Ssam
728138568Ssam/*
729138568Ssam * Xdr routine to generate file handle reply
730138568Ssam */
731138568Ssamint
732138568Ssamxdr_fhs(xdrsp, cp)
733138568Ssam	XDR *xdrsp;
734138568Ssam	caddr_t cp;
735138568Ssam{
736138568Ssam	struct fhreturn *fhrp = (struct fhreturn *)cp;
737148306Ssam	u_long ok = 0, len, auth;
738138568Ssam
739148306Ssam	if (!xdr_long(xdrsp, &ok))
740138568Ssam		return (0);
741140453Ssam	switch (fhrp->fhr_vers) {
742140453Ssam	case 1:
743138568Ssam		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
744138568Ssam	case 3:
745138568Ssam		len = NFSX_V3FH;
746138568Ssam		if (!xdr_long(xdrsp, &len))
747138568Ssam			return (0);
748138568Ssam		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
749138568Ssam			return (0);
750138568Ssam		auth = RPCAUTH_UNIX;
751138568Ssam		len = 1;
752138568Ssam		if (!xdr_long(xdrsp, &len))
753138568Ssam			return (0);
754138568Ssam		return (xdr_long(xdrsp, &auth));
755138568Ssam	};
756138568Ssam	return (0);
757138568Ssam}
758138568Ssam
759140753Ssamint
760138568Ssamxdr_mlist(xdrsp, cp)
761138568Ssam	XDR *xdrsp;
762138568Ssam	caddr_t cp;
763138568Ssam{
764138568Ssam	struct mountlist *mlp;
765138568Ssam	int true = 1;
766138568Ssam	int false = 0;
767138568Ssam	char *strp;
768138568Ssam
769138568Ssam	mlp = mlhead;
770138568Ssam	while (mlp) {
771116742Ssam		if (!xdr_bool(xdrsp, &true))
772140753Ssam			return (0);
773138568Ssam		strp = &mlp->ml_host[0];
774140440Ssam		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
775140440Ssam			return (0);
776138568Ssam		strp = &mlp->ml_dirp[0];
777148299Ssam		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
778127772Ssam			return (0);
779140753Ssam		mlp = mlp->ml_next;
780139521Ssam	}
781140753Ssam	if (!xdr_bool(xdrsp, &false))
782127772Ssam		return (0);
783140753Ssam	return (1);
784140753Ssam}
785140753Ssam
786140753Ssam/*
787140753Ssam * Xdr conversion for export list
788138568Ssam */
789138568Ssamint
790138568Ssamxdr_explist_common(xdrsp, cp, brief)
791138568Ssam	XDR *xdrsp;
792138568Ssam	caddr_t cp;
793138568Ssam	int brief;
794140753Ssam{
795153352Ssam	struct exportlist *ep;
796153352Ssam	int false = 0;
797138568Ssam	int putdef;
798153352Ssam	sigset_t sighup_mask;
799138568Ssam
800138568Ssam	sigemptyset(&sighup_mask);
801138568Ssam	sigaddset(&sighup_mask, SIGHUP);
802138568Ssam	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
803138568Ssam	ep = exphead;
804138568Ssam	while (ep) {
805148936Ssam		putdef = 0;
806138568Ssam		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
807138568Ssam			       &putdef, brief))
808140753Ssam			goto errout;
809140753Ssam		if (ep->ex_defdir && putdef == 0 &&
810140753Ssam			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
811140753Ssam			&putdef, brief))
812117811Ssam			goto errout;
813138568Ssam		ep = ep->ex_next;
814116742Ssam	}
815116742Ssam	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
816138568Ssam	if (!xdr_bool(xdrsp, &false))
817138568Ssam		return (0);
818138568Ssam	return (1);
819138568Ssamerrout:
820138568Ssam	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
821138568Ssam	return (0);
822138568Ssam}
823138568Ssam
824138568Ssam/*
825138568Ssam * Called from xdr_explist() to traverse the tree and export the
826138568Ssam * directory paths.
827116742Ssam */
828138568Ssamint
829116742Ssamput_exlist(dp, xdrsp, adp, putdefp, brief)
830127768Ssam	struct dirlist *dp;
831138568Ssam	XDR *xdrsp;
832127768Ssam	struct dirlist *adp;
833127768Ssam	int *putdefp;
834127768Ssam	int brief;
835116742Ssam{
836116742Ssam	struct grouplist *grp;
837138568Ssam	struct hostlist *hp;
838138568Ssam	int true = 1;
839138568Ssam	int false = 0;
840138568Ssam	int gotalldir = 0;
841138568Ssam	char *strp;
842138568Ssam
843116742Ssam	if (dp) {
844138568Ssam		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
845116742Ssam			return (1);
846138568Ssam		if (!xdr_bool(xdrsp, &true))
847138568Ssam			return (1);
848138568Ssam		strp = dp->dp_dirp;
849138568Ssam		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
850138568Ssam			return (1);
851138568Ssam		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
852138568Ssam			gotalldir = 1;
853138568Ssam			*putdefp = 1;
854138568Ssam		}
855138568Ssam		if (brief) {
856138568Ssam			if (!xdr_bool(xdrsp, &true))
857138568Ssam				return (1);
858147788Ssam			strp = "(...)";
859147788Ssam			if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
860147788Ssam				return (1);
861147788Ssam		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
862147788Ssam		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
863147788Ssam			hp = dp->dp_hosts;
864147788Ssam			while (hp) {
865138568Ssam				grp = hp->ht_grp;
866138568Ssam				if (grp->gr_type == GT_HOST) {
867138568Ssam					if (!xdr_bool(xdrsp, &true))
868138568Ssam						return (1);
869138568Ssam					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
870138568Ssam					if (!xdr_string(xdrsp, &strp,
871148304Ssam					    RPCMNT_NAMELEN))
872138568Ssam						return (1);
873138568Ssam				} else if (grp->gr_type == GT_NET) {
874138568Ssam					if (!xdr_bool(xdrsp, &true))
875138568Ssam						return (1);
876138568Ssam					strp = grp->gr_ptr.gt_net.nt_name;
877138568Ssam					if (!xdr_string(xdrsp, &strp,
878138568Ssam					    RPCMNT_NAMELEN))
879138568Ssam						return (1);
880138568Ssam				}
881138568Ssam				hp = hp->ht_next;
882138568Ssam				if (gotalldir && hp == (struct hostlist *)NULL) {
883138568Ssam					hp = adp->dp_hosts;
884138568Ssam					gotalldir = 0;
885138568Ssam				}
886138568Ssam			}
887138568Ssam		}
888138568Ssam		if (!xdr_bool(xdrsp, &false))
889138568Ssam			return (1);
890138568Ssam		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
891138568Ssam			return (1);
892138568Ssam	}
893138568Ssam	return (0);
894138568Ssam}
895148863Ssam
896148863Ssamint
897148863Ssamxdr_explist(xdrsp, cp)
898148863Ssam	XDR *xdrsp;
899138568Ssam	caddr_t cp;
900116742Ssam{
901116742Ssam
902116742Ssam	return xdr_explist_common(xdrsp, cp, 0);
903138568Ssam}
904116742Ssam
905138568Ssamint
906138568Ssamxdr_explist_brief(xdrsp, cp)
907138568Ssam	XDR *xdrsp;
908138568Ssam	caddr_t cp;
909138568Ssam{
910138568Ssam
911138568Ssam	return xdr_explist_common(xdrsp, cp, 1);
912138568Ssam}
913138568Ssam
914116742Ssamchar *line;
915116742Ssamint linesize;
916120104SsamFILE *exp_file;
917138568Ssam
918120104Ssam/*
919120104Ssam * Get the export list
920120104Ssam */
921120104Ssamvoid
922116742Ssamget_exportlist()
923138568Ssam{
924138568Ssam	struct exportlist *ep, *ep2;
925116742Ssam	struct grouplist *grp, *tgrp;
926138568Ssam	struct exportlist **epp;
927116742Ssam	struct dirlist *dirhead;
928116742Ssam	struct statfs fsb, *fsp;
929138568Ssam	struct xucred anon;
930140766Ssam	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
931138568Ssam	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
932138568Ssam
933116742Ssam	dirp = NULL;
934116742Ssam	dirplen = 0;
935138568Ssam
936138568Ssam	/*
937138568Ssam	 * First, get rid of the old list
938138568Ssam	 */
939138568Ssam	ep = exphead;
940139528Ssam	while (ep) {
941139528Ssam		ep2 = ep;
942138568Ssam		ep = ep->ex_next;
943138568Ssam		free_exp(ep2);
944138568Ssam	}
945138568Ssam	exphead = (struct exportlist *)NULL;
946138568Ssam
947138568Ssam	grp = grphead;
948138568Ssam	while (grp) {
949138568Ssam		tgrp = grp;
950116742Ssam		grp = grp->gr_next;
951116742Ssam		free_grp(tgrp);
952116742Ssam	}
953138568Ssam	grphead = (struct grouplist *)NULL;
954116742Ssam
955138568Ssam	/*
956138568Ssam	 * And delete exports that are in the kernel for all local
957138568Ssam	 * filesystems.
958138568Ssam	 * XXX: Should know how to handle all local exportable filesystems
959116742Ssam	 *      instead of just "ufs".
960138568Ssam	 */
961127769Ssam	num = getmntinfo(&fsp, MNT_NOWAIT);
962127769Ssam	for (i = 0; i < num; i++) {
963116742Ssam		union {
964116742Ssam			struct ufs_args ua;
965116742Ssam			struct iso_args ia;
966148777Ssam			struct msdosfs_args da;
967148777Ssam			struct ntfs_args na;
968148777Ssam		} targs;
969148777Ssam
970148777Ssam		if (!strcmp(fsp->f_fstypename, "ufs") ||
971148777Ssam		    !strcmp(fsp->f_fstypename, "msdosfs") ||
972116742Ssam		    !strcmp(fsp->f_fstypename, "ntfs") ||
973148777Ssam		    !strcmp(fsp->f_fstypename, "cd9660")) {
974148777Ssam			targs.ua.fspec = NULL;
975148777Ssam			targs.ua.export.ex_flags = MNT_DELEXPORT;
976148777Ssam			if (mount(fsp->f_fstypename, fsp->f_mntonname,
977148777Ssam			    fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
978148777Ssam			    errno != ENOENT)
979148777Ssam				syslog(LOG_ERR,
980148777Ssam				    "can't delete exports for %s: %m",
981148777Ssam				    fsp->f_mntonname);
982148777Ssam		}
983148777Ssam		fsp++;
984148777Ssam	}
985148777Ssam
986148777Ssam	/*
987148777Ssam	 * Read in the exports file and build the list, calling
988148777Ssam	 * mount() as we go along to push the export rules into the kernel.
989148777Ssam	 */
990148777Ssam	if ((exp_file = fopen(exname, "r")) == NULL) {
991148777Ssam		syslog(LOG_ERR, "can't open %s", exname);
992148777Ssam		exit(2);
993148777Ssam	}
994148777Ssam	dirhead = (struct dirlist *)NULL;
995148777Ssam	while (get_line()) {
996148777Ssam		if (debug)
997148777Ssam			warnx("got line %s", line);
998148777Ssam		cp = line;
999148777Ssam		nextfield(&cp, &endcp);
1000148777Ssam		if (*cp == '#')
1001148777Ssam			goto nextline;
1002148777Ssam
1003138568Ssam		/*
1004116742Ssam		 * Set defaults.
1005138568Ssam		 */
1006138568Ssam		has_host = FALSE;
1007138568Ssam		anon = def_anon;
1008138568Ssam		exflags = MNT_EXPORTED;
1009116742Ssam		got_nondir = 0;
1010138568Ssam		opt_flags = 0;
1011127770Ssam		ep = (struct exportlist *)NULL;
1012127770Ssam
1013127770Ssam		/*
1014138568Ssam		 * Create new exports list entry
1015138568Ssam		 */
1016138568Ssam		len = endcp-cp;
1017127770Ssam		tgrp = grp = get_grp();
1018138568Ssam		while (len > 0) {
1019138568Ssam			if (len > RPCMNT_NAMELEN) {
1020127770Ssam			    getexp_err(ep, tgrp);
1021127770Ssam			    goto nextline;
1022116742Ssam			}
1023116742Ssam			if (*cp == '-') {
1024116742Ssam			    if (ep == (struct exportlist *)NULL) {
1025127772Ssam				getexp_err(ep, tgrp);
1026138568Ssam				goto nextline;
1027138568Ssam			    }
1028138568Ssam			    if (debug)
1029138568Ssam				warnx("doing opt %s", cp);
1030138568Ssam			    got_nondir = 1;
1031138568Ssam			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1032138568Ssam				&exflags, &anon)) {
1033116742Ssam				getexp_err(ep, tgrp);
1034116742Ssam				goto nextline;
1035116742Ssam			    }
1036116742Ssam			} else if (*cp == '/') {
1037138568Ssam			    savedc = *endcp;
1038127772Ssam			    *endcp = '\0';
1039116742Ssam			    if (check_dirpath(cp) &&
1040138568Ssam				statfs(cp, &fsb) >= 0) {
1041116742Ssam				if (got_nondir) {
1042138568Ssam				    syslog(LOG_ERR, "dirs must be first");
1043138568Ssam				    getexp_err(ep, tgrp);
1044138568Ssam				    goto nextline;
1045140766Ssam				}
1046140766Ssam				if (ep) {
1047140766Ssam				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1048140766Ssam					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1049138568Ssam					getexp_err(ep, tgrp);
1050127772Ssam					goto nextline;
1051116742Ssam				    }
1052116742Ssam				} else {
1053127772Ssam				    /*
1054127772Ssam				     * See if this directory is already
1055138568Ssam				     * in the list.
1056138568Ssam				     */
1057138568Ssam				    ep = ex_search(&fsb.f_fsid);
1058138568Ssam				    if (ep == (struct exportlist *)NULL) {
1059127772Ssam					ep = get_exp();
1060127772Ssam					ep->ex_fs = fsb.f_fsid;
1061138568Ssam					ep->ex_fsdir = (char *)
1062138568Ssam					    malloc(strlen(fsb.f_mntonname) + 1);
1063138568Ssam					if (ep->ex_fsdir)
1064138568Ssam					    strcpy(ep->ex_fsdir,
1065138568Ssam						fsb.f_mntonname);
1066138568Ssam					else
1067127772Ssam					    out_of_mem();
1068127772Ssam					if (debug)
1069127772Ssam						warnx("making new ep fs=0x%x,0x%x",
1070138568Ssam						    fsb.f_fsid.val[0],
1071138568Ssam						    fsb.f_fsid.val[1]);
1072138568Ssam				    } else if (debug)
1073116742Ssam					warnx("found ep fs=0x%x,0x%x",
1074116742Ssam					    fsb.f_fsid.val[0],
1075116742Ssam					    fsb.f_fsid.val[1]);
1076116742Ssam				}
1077138568Ssam
1078138568Ssam				/*
1079138568Ssam				 * Add dirpath to export mount point.
1080138568Ssam				 */
1081138568Ssam				dirp = add_expdir(&dirhead, cp, len);
1082138568Ssam				dirplen = len;
1083138568Ssam			    } else {
1084138568Ssam				getexp_err(ep, tgrp);
1085138568Ssam				goto nextline;
1086138568Ssam			    }
1087138568Ssam			    *endcp = savedc;
1088138568Ssam			} else {
1089153073Ssam			    savedc = *endcp;
1090153073Ssam			    *endcp = '\0';
1091138568Ssam			    got_nondir = 1;
1092138568Ssam			    if (ep == (struct exportlist *)NULL) {
1093138568Ssam				getexp_err(ep, tgrp);
1094138568Ssam				goto nextline;
1095139524Ssam			    }
1096148307Ssam
1097138568Ssam			    /*
1098148302Ssam			     * Get the host or netgroup.
1099153404Ssam			     */
1100153404Ssam			    setnetgrent(cp);
1101153404Ssam			    netgrp = getnetgrent(&hst, &usr, &dom);
1102153404Ssam			    do {
1103153404Ssam				if (has_host) {
1104153404Ssam				    grp->gr_next = get_grp();
1105153404Ssam				    grp = grp->gr_next;
1106153404Ssam				}
1107153404Ssam				if (netgrp) {
1108138568Ssam				    if (hst == 0) {
1109138568Ssam					syslog(LOG_ERR,
1110138568Ssam				"null hostname in netgroup %s, skipping", cp);
1111138568Ssam					grp->gr_type = GT_IGNORE;
1112148936Ssam				    } else if (get_host(hst, grp, tgrp)) {
1113148936Ssam					syslog(LOG_ERR,
1114148936Ssam			"bad host %s in netgroup %s, skipping", hst, cp);
1115148936Ssam					grp->gr_type = GT_IGNORE;
1116148936Ssam				    }
1117148936Ssam				} else if (get_host(cp, grp, tgrp)) {
1118148936Ssam				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1119148936Ssam				    grp->gr_type = GT_IGNORE;
1120148936Ssam				}
1121148936Ssam				has_host = TRUE;
1122148936Ssam			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1123148936Ssam			    endnetgrent();
1124148936Ssam			    *endcp = savedc;
1125148936Ssam			}
1126148936Ssam			cp = endcp;
1127148936Ssam			nextfield(&cp, &endcp);
1128148936Ssam			len = endcp - cp;
1129148936Ssam		}
1130148936Ssam		if (check_options(dirhead)) {
1131148936Ssam			getexp_err(ep, tgrp);
1132148936Ssam			goto nextline;
1133148936Ssam		}
1134148936Ssam		if (!has_host) {
1135148936Ssam			grp->gr_type = GT_DEFAULT;
1136148936Ssam			if (debug)
1137148936Ssam				warnx("adding a default entry");
1138148936Ssam
1139148936Ssam		/*
1140148936Ssam		 * Don't allow a network export coincide with a list of
1141148936Ssam		 * host(s) on the same line.
1142148936Ssam		 */
1143148936Ssam		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1144148936Ssam			syslog(LOG_ERR, "network/host conflict");
1145148936Ssam			getexp_err(ep, tgrp);
1146148936Ssam			goto nextline;
1147148936Ssam
1148148936Ssam		/*
1149148936Ssam		 * If an export list was specified on this line, make sure
1150148936Ssam		 * that we have at least one valid entry, otherwise skip it.
1151148936Ssam		 */
1152148936Ssam		} else {
1153148936Ssam			grp = tgrp;
1154148936Ssam			while (grp && grp->gr_type == GT_IGNORE)
1155148936Ssam				grp = grp->gr_next;
1156148936Ssam			if (! grp) {
1157148936Ssam			    getexp_err(ep, tgrp);
1158148936Ssam			    goto nextline;
1159148936Ssam			}
1160148936Ssam		}
1161148936Ssam
1162148936Ssam		/*
1163148936Ssam		 * Loop through hosts, pushing the exports into the kernel.
1164148936Ssam		 * After loop, tgrp points to the start of the list and
1165148936Ssam		 * grp points to the last entry in the list.
1166148936Ssam		 */
1167148936Ssam		grp = tgrp;
1168148936Ssam		do {
1169148936Ssam			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1170148936Ssam			    &fsb)) {
1171148936Ssam				getexp_err(ep, tgrp);
1172148936Ssam				goto nextline;
1173148936Ssam			}
1174148936Ssam		} while (grp->gr_next && (grp = grp->gr_next));
1175148936Ssam
1176148936Ssam		/*
1177148936Ssam		 * Success. Update the data structures.
1178148936Ssam		 */
1179148936Ssam		if (has_host) {
1180148936Ssam			hang_dirp(dirhead, tgrp, ep, opt_flags);
1181148936Ssam			grp->gr_next = grphead;
1182148936Ssam			grphead = tgrp;
1183148936Ssam		} else {
1184148936Ssam			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1185148936Ssam				opt_flags);
1186148936Ssam			free_grp(grp);
1187148936Ssam		}
1188148936Ssam		dirhead = (struct dirlist *)NULL;
1189148936Ssam		if ((ep->ex_flag & EX_LINKED) == 0) {
1190148936Ssam			ep2 = exphead;
1191148936Ssam			epp = &exphead;
1192148936Ssam
1193148936Ssam			/*
1194148936Ssam			 * Insert in the list in alphabetical order.
1195148936Ssam			 */
1196148936Ssam			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1197148936Ssam				epp = &ep2->ex_next;
1198148936Ssam				ep2 = ep2->ex_next;
1199148936Ssam			}
1200148936Ssam			if (ep2)
1201148936Ssam				ep->ex_next = ep2;
1202148936Ssam			*epp = ep;
1203148936Ssam			ep->ex_flag |= EX_LINKED;
1204148936Ssam		}
1205148936Ssamnextline:
1206148936Ssam		if (dirhead) {
1207148936Ssam			free_dir(dirhead);
1208148936Ssam			dirhead = (struct dirlist *)NULL;
1209148936Ssam		}
1210148936Ssam	}
1211148936Ssam	fclose(exp_file);
1212148936Ssam}
1213148936Ssam
1214148936Ssam/*
1215148936Ssam * Allocate an export list element
1216148936Ssam */
1217148936Ssamstruct exportlist *
1218148936Ssamget_exp()
1219148936Ssam{
1220148936Ssam	struct exportlist *ep;
1221148936Ssam
1222148936Ssam	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1223148936Ssam	if (ep == (struct exportlist *)NULL)
1224148936Ssam		out_of_mem();
1225148936Ssam	memset(ep, 0, sizeof(struct exportlist));
1226148936Ssam	return (ep);
1227148936Ssam}
1228148936Ssam
1229148936Ssam/*
1230148936Ssam * Allocate a group list element
1231148936Ssam */
1232148936Ssamstruct grouplist *
1233148936Ssamget_grp()
1234148936Ssam{
1235148936Ssam	struct grouplist *gp;
1236148936Ssam
1237148936Ssam	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1238148936Ssam	if (gp == (struct grouplist *)NULL)
1239148936Ssam		out_of_mem();
1240148936Ssam	memset(gp, 0, sizeof(struct grouplist));
1241153073Ssam	return (gp);
1242153073Ssam}
1243153073Ssam
1244153073Ssam/*
1245153073Ssam * Clean up upon an error in get_exportlist().
1246153073Ssam */
1247153073Ssamvoid
1248153073Ssamgetexp_err(ep, grp)
1249153073Ssam	struct exportlist *ep;
1250153073Ssam	struct grouplist *grp;
1251153073Ssam{
1252153073Ssam	struct grouplist *tgrp;
1253153073Ssam
1254153073Ssam	if (!(opt_flags & OP_QUIET))
1255153073Ssam		syslog(LOG_ERR, "bad exports list line %s", line);
1256153073Ssam	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1257153073Ssam		free_exp(ep);
1258153073Ssam	while (grp) {
1259153073Ssam		tgrp = grp;
1260153073Ssam		grp = grp->gr_next;
1261153073Ssam		free_grp(tgrp);
1262153073Ssam	}
1263153073Ssam}
1264153073Ssam
1265153073Ssam/*
1266153073Ssam * Search the export list for a matching fs.
1267153073Ssam */
1268153073Ssamstruct exportlist *
1269148936Ssamex_search(fsid)
1270148936Ssam	fsid_t *fsid;
1271148936Ssam{
1272148936Ssam	struct exportlist *ep;
1273148936Ssam
1274148936Ssam	ep = exphead;
1275148936Ssam	while (ep) {
1276148936Ssam		if (ep->ex_fs.val[0] == fsid->val[0] &&
1277148936Ssam		    ep->ex_fs.val[1] == fsid->val[1])
1278148936Ssam			return (ep);
1279148936Ssam		ep = ep->ex_next;
1280148936Ssam	}
1281148936Ssam	return (ep);
1282153073Ssam}
1283153073Ssam
1284148936Ssam/*
1285148936Ssam * Add a directory path to the list.
1286153073Ssam */
1287148936Ssamchar *
1288148936Ssamadd_expdir(dpp, cp, len)
1289148936Ssam	struct dirlist **dpp;
1290148936Ssam	char *cp;
1291148936Ssam	int len;
1292148936Ssam{
1293148936Ssam	struct dirlist *dp;
1294148936Ssam
1295148863Ssam	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1296148863Ssam	if (dp == (struct dirlist *)NULL)
1297148863Ssam		out_of_mem();
1298148863Ssam	dp->dp_left = *dpp;
1299138568Ssam	dp->dp_right = (struct dirlist *)NULL;
1300138568Ssam	dp->dp_flag = 0;
1301138568Ssam	dp->dp_hosts = (struct hostlist *)NULL;
1302138568Ssam	strcpy(dp->dp_dirp, cp);
1303138568Ssam	*dpp = dp;
1304138568Ssam	return (dp->dp_dirp);
1305138568Ssam}
1306138568Ssam
1307138568Ssam/*
1308138568Ssam * Hang the dir list element off the dirpath binary tree as required
1309138568Ssam * and update the entry for host.
1310138568Ssam */
1311138568Ssamvoid
1312138568Ssamhang_dirp(dp, grp, ep, flags)
1313138568Ssam	struct dirlist *dp;
1314138568Ssam	struct grouplist *grp;
1315138568Ssam	struct exportlist *ep;
1316138568Ssam	int flags;
1317138568Ssam{
1318138568Ssam	struct hostlist *hp;
1319138568Ssam	struct dirlist *dp2;
1320138568Ssam
1321138568Ssam	if (flags & OP_ALLDIRS) {
1322138568Ssam		if (ep->ex_defdir)
1323138568Ssam			free((caddr_t)dp);
1324138568Ssam		else
1325140753Ssam			ep->ex_defdir = dp;
1326138568Ssam		if (grp == (struct grouplist *)NULL) {
1327138568Ssam			ep->ex_defdir->dp_flag |= DP_DEFSET;
1328138568Ssam		} else while (grp) {
1329138568Ssam			hp = get_ht();
1330138568Ssam			hp->ht_grp = grp;
1331138568Ssam			hp->ht_next = ep->ex_defdir->dp_hosts;
1332138568Ssam			ep->ex_defdir->dp_hosts = hp;
1333148863Ssam			grp = grp->gr_next;
1334148863Ssam		}
1335138568Ssam	} else {
1336138568Ssam
1337148863Ssam		/*
1338148863Ssam		 * Loop through the directories adding them to the tree.
1339148863Ssam		 */
1340148863Ssam		while (dp) {
1341148863Ssam			dp2 = dp->dp_left;
1342148863Ssam			add_dlist(&ep->ex_dirl, dp, grp, flags);
1343148863Ssam			dp = dp2;
1344148863Ssam		}
1345148863Ssam	}
1346148863Ssam}
1347148863Ssam
1348148863Ssam/*
1349148863Ssam * Traverse the binary tree either updating a node that is already there
1350148863Ssam * for the new directory or adding the new node.
1351148863Ssam */
1352148863Ssamvoid
1353148863Ssamadd_dlist(dpp, newdp, grp, flags)
1354148863Ssam	struct dirlist **dpp;
1355148863Ssam	struct dirlist *newdp;
1356148863Ssam	struct grouplist *grp;
1357148863Ssam	int flags;
1358148863Ssam{
1359148863Ssam	struct dirlist *dp;
1360148863Ssam	struct hostlist *hp;
1361148863Ssam	int cmp;
1362148863Ssam
1363148863Ssam	dp = *dpp;
1364148863Ssam	if (dp) {
1365148863Ssam		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1366148863Ssam		if (cmp > 0) {
1367148863Ssam			add_dlist(&dp->dp_left, newdp, grp, flags);
1368148863Ssam			return;
1369148863Ssam		} else if (cmp < 0) {
1370148863Ssam			add_dlist(&dp->dp_right, newdp, grp, flags);
1371148863Ssam			return;
1372148863Ssam		} else
1373148863Ssam			free((caddr_t)newdp);
1374148863Ssam	} else {
1375148863Ssam		dp = newdp;
1376148863Ssam		dp->dp_left = (struct dirlist *)NULL;
1377148863Ssam		*dpp = dp;
1378148863Ssam	}
1379148863Ssam	if (grp) {
1380148863Ssam
1381148863Ssam		/*
1382148863Ssam		 * Hang all of the host(s) off of the directory point.
1383148863Ssam		 */
1384148863Ssam		do {
1385148863Ssam			hp = get_ht();
1386148863Ssam			hp->ht_grp = grp;
1387148863Ssam			hp->ht_next = dp->dp_hosts;
1388148863Ssam			dp->dp_hosts = hp;
1389148863Ssam			grp = grp->gr_next;
1390148863Ssam		} while (grp);
1391148863Ssam	} else {
1392148863Ssam		dp->dp_flag |= DP_DEFSET;
1393148863Ssam	}
1394148863Ssam}
1395148863Ssam
1396148863Ssam/*
1397148863Ssam * Search for a dirpath on the export point.
1398148863Ssam */
1399148863Ssamstruct dirlist *
1400148863Ssamdirp_search(dp, dirp)
1401148863Ssam	struct dirlist *dp;
1402138568Ssam	char *dirp;
1403138568Ssam{
1404138568Ssam	int cmp;
1405138568Ssam
1406127772Ssam	if (dp) {
1407127772Ssam		cmp = strcmp(dp->dp_dirp, dirp);
1408127772Ssam		if (cmp > 0)
1409127772Ssam			return (dirp_search(dp->dp_left, dirp));
1410138568Ssam		else if (cmp < 0)
1411138568Ssam			return (dirp_search(dp->dp_right, dirp));
1412138568Ssam		else
1413138568Ssam			return (dp);
1414138568Ssam	}
1415138568Ssam	return (dp);
1416127772Ssam}
1417140753Ssam
1418127772Ssam/*
1419127772Ssam * Scan for a host match in a directory tree.
1420127772Ssam */
1421127772Ssamint
1422148863Ssamchk_host(dp, saddr, defsetp, hostsetp)
1423148863Ssam	struct dirlist *dp;
1424148863Ssam	struct sockaddr *saddr;
1425127772Ssam	int *defsetp;
1426127772Ssam	int *hostsetp;
1427138568Ssam{
1428148863Ssam	struct hostlist *hp;
1429148863Ssam	struct grouplist *grp;
1430148863Ssam	struct addrinfo *ai;
1431148863Ssam
1432138568Ssam	if (dp) {
1433138568Ssam		if (dp->dp_flag & DP_DEFSET)
1434138568Ssam			*defsetp = dp->dp_flag;
1435138568Ssam		hp = dp->dp_hosts;
1436140497Ssam		while (hp) {
1437140497Ssam			grp = hp->ht_grp;
1438140497Ssam			switch (grp->gr_type) {
1439140497Ssam			case GT_HOST:
1440140497Ssam				ai = grp->gr_ptr.gt_addrinfo;
1441140497Ssam				for (; ai; ai = ai->ai_next) {
1442138568Ssam					if (!sacmp(ai->ai_addr, saddr, NULL)) {
1443140497Ssam						*hostsetp =
1444140497Ssam						    (hp->ht_flag | DP_HOSTSET);
1445140497Ssam						return (1);
1446138568Ssam					}
1447138568Ssam				}
1448138568Ssam				break;
1449138568Ssam			case GT_NET:
1450127772Ssam				if (!sacmp(saddr, (struct sockaddr *)
1451127772Ssam				    &grp->gr_ptr.gt_net.nt_net,
1452127772Ssam				    (struct sockaddr *)
1453127772Ssam				    &grp->gr_ptr.gt_net.nt_mask)) {
1454127772Ssam					*hostsetp = (hp->ht_flag | DP_HOSTSET);
1455127772Ssam					return (1);
1456116742Ssam				}
1457116742Ssam				break;
1458116742Ssam			}
1459138568Ssam			hp = hp->ht_next;
1460138568Ssam		}
1461138568Ssam	}
1462138568Ssam	return (0);
1463138568Ssam}
1464138568Ssam
1465138568Ssam/*
1466138568Ssam * Scan tree for a host that matches the address.
1467116742Ssam */
1468116742Ssamint
1469116742Ssamscan_tree(dp, saddr)
1470116742Ssam	struct dirlist *dp;
1471116742Ssam	struct sockaddr *saddr;
1472138568Ssam{
1473138568Ssam	int defset, hostset;
1474127772Ssam
1475127772Ssam	if (dp) {
1476138568Ssam		if (scan_tree(dp->dp_left, saddr))
1477138568Ssam			return (1);
1478138568Ssam		if (chk_host(dp, saddr, &defset, &hostset))
1479140766Ssam			return (1);
1480140766Ssam		if (scan_tree(dp->dp_right, saddr))
1481138568Ssam			return (1);
1482140766Ssam	}
1483138568Ssam	return (0);
1484140766Ssam}
1485140766Ssam
1486116742Ssam/*
1487116742Ssam * Traverse the dirlist tree and free it up.
1488116742Ssam */
1489138568Ssamvoid
1490116742Ssamfree_dir(dp)
1491116742Ssam	struct dirlist *dp;
1492116742Ssam{
1493138568Ssam
1494138568Ssam	if (dp) {
1495138568Ssam		free_dir(dp->dp_left);
1496138568Ssam		free_dir(dp->dp_right);
1497138568Ssam		free_host(dp->dp_hosts);
1498138568Ssam		free((caddr_t)dp);
1499138568Ssam	}
1500138568Ssam}
1501138568Ssam
1502138568Ssam/*
1503138568Ssam * Parse the option string and update fields.
1504138568Ssam * Option arguments may either be -<option>=<value> or
1505138568Ssam * -<option> <value>
1506147118Ssam */
1507147118Ssamint
1508147118Ssamdo_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1509138568Ssam	char **cpp, **endcpp;
1510138568Ssam	struct exportlist *ep;
1511138568Ssam	struct grouplist *grp;
1512138568Ssam	int *has_hostp;
1513138568Ssam	int *exflagsp;
1514147118Ssam	struct xucred *cr;
1515147118Ssam{
1516147118Ssam	char *cpoptarg, *cpoptend;
1517147118Ssam	char *cp, *endcp, *cpopt, savedc, savedc2;
1518147118Ssam	int allflag, usedarg;
1519147118Ssam
1520147118Ssam	savedc2 = '\0';
1521147118Ssam	cpopt = *cpp;
1522147118Ssam	cpopt++;
1523147118Ssam	cp = *endcpp;
1524147118Ssam	savedc = *cp;
1525147118Ssam	*cp = '\0';
1526147118Ssam	while (cpopt && *cpopt) {
1527147118Ssam		allflag = 1;
1528147118Ssam		usedarg = -2;
1529147118Ssam		if ((cpoptend = strchr(cpopt, ','))) {
1530147118Ssam			*cpoptend++ = '\0';
1531147118Ssam			if ((cpoptarg = strchr(cpopt, '=')))
1532147118Ssam				*cpoptarg++ = '\0';
1533147118Ssam		} else {
1534138568Ssam			if ((cpoptarg = strchr(cpopt, '=')))
1535147118Ssam				*cpoptarg++ = '\0';
1536147118Ssam			else {
1537138568Ssam				*cp = savedc;
1538147118Ssam				nextfield(&cp, &endcp);
1539138568Ssam				**endcpp = '\0';
1540147118Ssam				if (endcp > cp && *cp != '-') {
1541147118Ssam					cpoptarg = cp;
1542138568Ssam					savedc2 = *endcp;
1543138568Ssam					*endcp = '\0';
1544138568Ssam					usedarg = 0;
1545147118Ssam				}
1546138568Ssam			}
1547138568Ssam		}
1548116742Ssam		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1549138568Ssam			*exflagsp |= MNT_EXRDONLY;
1550116742Ssam		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1551138568Ssam		    !(allflag = strcmp(cpopt, "mapall")) ||
1552138568Ssam		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1553119150Ssam			usedarg++;
1554138568Ssam			parsecred(cpoptarg, cr);
1555140766Ssam			if (allflag == 0) {
1556140766Ssam				*exflagsp |= MNT_EXPORTANON;
1557138568Ssam				opt_flags |= OP_MAPALL;
1558138568Ssam			} else
1559138568Ssam				opt_flags |= OP_MAPROOT;
1560138568Ssam		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1561138568Ssam		    !strcmp(cpopt, "m"))) {
1562138568Ssam			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1563138568Ssam				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1564138568Ssam				return (1);
1565116742Ssam			}
1566116742Ssam			usedarg++;
1567116742Ssam			opt_flags |= OP_MASK;
1568138568Ssam		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1569138568Ssam			!strcmp(cpopt, "n"))) {
1570138568Ssam			if (strchr(cpoptarg, '/') != NULL) {
1571138568Ssam				if (debug)
1572138568Ssam					fprintf(stderr, "setting OP_MASKLEN\n");
1573116742Ssam				opt_flags |= OP_MASKLEN;
1574138568Ssam			}
1575119150Ssam			if (grp->gr_type != GT_NULL) {
1576138568Ssam				syslog(LOG_ERR, "network/host conflict");
1577140454Ssam				return (1);
1578140766Ssam			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1579138568Ssam				syslog(LOG_ERR, "bad net: %s", cpoptarg);
1580138568Ssam				return (1);
1581148863Ssam			}
1582148863Ssam			grp->gr_type = GT_NET;
1583148863Ssam			*has_hostp = 1;
1584148863Ssam			usedarg++;
1585148863Ssam			opt_flags |= OP_NET;
1586148863Ssam		} else if (!strcmp(cpopt, "alldirs")) {
1587138568Ssam			opt_flags |= OP_ALLDIRS;
1588148863Ssam		} else if (!strcmp(cpopt, "public")) {
1589148863Ssam			*exflagsp |= MNT_EXPUBLIC;
1590148863Ssam		} else if (!strcmp(cpopt, "webnfs")) {
1591148863Ssam			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1592148863Ssam			opt_flags |= OP_MAPALL;
1593148863Ssam		} else if (cpoptarg && !strcmp(cpopt, "index")) {
1594148863Ssam			ep->ex_indexfile = strdup(cpoptarg);
1595148863Ssam		} else if (!strcmp(cpopt, "quiet")) {
1596148863Ssam			opt_flags |= OP_QUIET;
1597148863Ssam		} else {
1598148863Ssam			syslog(LOG_ERR, "bad opt %s", cpopt);
1599148863Ssam			return (1);
1600148863Ssam		}
1601148863Ssam		if (usedarg >= 0) {
1602148863Ssam			*endcp = savedc2;
1603148863Ssam			**endcpp = savedc;
1604148863Ssam			if (usedarg > 0) {
1605148863Ssam				*cpp = cp;
1606148863Ssam				*endcpp = endcp;
1607148863Ssam			}
1608138568Ssam			return (0);
1609116742Ssam		}
1610116742Ssam		cpopt = cpoptend;
1611116742Ssam	}
1612138568Ssam	**endcpp = savedc;
1613148863Ssam	return (0);
1614148863Ssam}
1615148863Ssam
1616148863Ssam/*
1617148863Ssam * Translate a character string to the corresponding list of network
1618148863Ssam * addresses for a hostname.
1619148863Ssam */
1620148863Ssamint
1621148863Ssamget_host(cp, grp, tgrp)
1622148863Ssam	char *cp;
1623148863Ssam	struct grouplist *grp;
1624148863Ssam	struct grouplist *tgrp;
1625148863Ssam{
1626148863Ssam	struct grouplist *checkgrp;
1627148863Ssam	struct addrinfo *ai, *tai, hints;
1628148863Ssam	int ecode;
1629148863Ssam	char host[NI_MAXHOST];
1630148863Ssam
1631148863Ssam	if (grp->gr_type != GT_NULL) {
1632148863Ssam		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
1633148863Ssam		return (1);
1634148863Ssam	}
1635148863Ssam	memset(&hints, 0, sizeof hints);
1636148863Ssam	hints.ai_flags = AI_CANONNAME;
1637148863Ssam	hints.ai_protocol = IPPROTO_UDP;
1638148863Ssam	ecode = getaddrinfo(cp, NULL, &hints, &ai);
1639148863Ssam	if (ecode != 0) {
1640148863Ssam		syslog(LOG_ERR,"can't get address info for host %s", cp);
1641148863Ssam		return 1;
1642148863Ssam	}
1643148863Ssam	grp->gr_ptr.gt_addrinfo = ai;
1644148863Ssam	while (ai != NULL) {
1645148863Ssam		if (ai->ai_canonname == NULL) {
1646148863Ssam			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1647148863Ssam			    sizeof host, NULL, 0, ninumeric) != 0)
1648148863Ssam				strlcpy(host, "?", sizeof(host));
1649148863Ssam			ai->ai_canonname = strdup(host);
1650148863Ssam			ai->ai_flags |= AI_CANONNAME;
1651148863Ssam		}
1652148863Ssam		if (debug)
1653148863Ssam			fprintf(stderr, "got host %s\n", ai->ai_canonname);
1654148863Ssam		/*
1655148863Ssam		 * Sanity check: make sure we don't already have an entry
1656148863Ssam		 * for this host in the grouplist.
1657148863Ssam		 */
1658148863Ssam		for (checkgrp = tgrp; checkgrp != NULL;
1659148863Ssam		    checkgrp = checkgrp->gr_next) {
1660148863Ssam			if (checkgrp->gr_type != GT_HOST)
1661148863Ssam				continue;
1662138568Ssam			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
1663138568Ssam			    tai = tai->ai_next) {
1664138568Ssam				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
1665138568Ssam					continue;
1666138568Ssam				if (debug)
1667138568Ssam					fprintf(stderr,
1668116742Ssam					    "ignoring duplicate host %s\n",
1669148863Ssam					    ai->ai_canonname);
1670138568Ssam				grp->gr_type = GT_IGNORE;
1671148863Ssam				return (0);
1672148863Ssam			}
1673140766Ssam		}
1674140766Ssam		ai = ai->ai_next;
1675140766Ssam	}
1676140766Ssam	grp->gr_type = GT_HOST;
1677148863Ssam	return (0);
1678148863Ssam}
1679148863Ssam
1680148863Ssam/*
1681148863Ssam * Free up an exports list component
1682148863Ssam */
1683148863Ssamvoid
1684148863Ssamfree_exp(ep)
1685148863Ssam	struct exportlist *ep;
1686148863Ssam{
1687148863Ssam
1688148863Ssam	if (ep->ex_defdir) {
1689148863Ssam		free_host(ep->ex_defdir->dp_hosts);
1690148863Ssam		free((caddr_t)ep->ex_defdir);
1691148863Ssam	}
1692148863Ssam	if (ep->ex_fsdir)
1693138568Ssam		free(ep->ex_fsdir);
1694138568Ssam	if (ep->ex_indexfile)
1695138568Ssam		free(ep->ex_indexfile);
1696138568Ssam	free_dir(ep->ex_dirl);
1697138568Ssam	free((caddr_t)ep);
1698140753Ssam}
1699138568Ssam
1700138568Ssam/*
1701138568Ssam * Free hosts.
1702138568Ssam */
1703138568Ssamvoid
1704138568Ssamfree_host(hp)
1705138568Ssam	struct hostlist *hp;
1706138568Ssam{
1707138568Ssam	struct hostlist *hp2;
1708138568Ssam
1709138568Ssam	while (hp) {
1710138568Ssam		hp2 = hp;
1711116742Ssam		hp = hp->ht_next;
1712116742Ssam		free((caddr_t)hp2);
1713138568Ssam	}
1714138568Ssam}
1715138568Ssam
1716138568Ssamstruct hostlist *
1717138568Ssamget_ht()
1718138568Ssam{
1719138568Ssam	struct hostlist *hp;
1720138568Ssam
1721138568Ssam	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1722138568Ssam	if (hp == (struct hostlist *)NULL)
1723138568Ssam		out_of_mem();
1724138568Ssam	hp->ht_next = (struct hostlist *)NULL;
1725116742Ssam	hp->ht_flag = 0;
1726116742Ssam	return (hp);
1727138568Ssam}
1728138568Ssam
1729138568Ssam/*
1730138568Ssam * Out of memory, fatal
1731138568Ssam */
1732138568Ssamvoid
1733138568Ssamout_of_mem()
1734138568Ssam{
1735138568Ssam
1736120483Ssam	syslog(LOG_ERR, "out of memory");
1737138568Ssam	exit(2);
1738138568Ssam}
1739138568Ssam
1740138568Ssam/*
1741138568Ssam * Do the mount syscall with the update flag to push the export info into
1742138568Ssam * the kernel.
1743138568Ssam */
1744138568Ssamint
1745138568Ssamdo_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1746138568Ssam	struct exportlist *ep;
1747138568Ssam	struct grouplist *grp;
1748138568Ssam	int exflags;
1749138568Ssam	struct xucred *anoncrp;
1750138568Ssam	char *dirp;
1751138568Ssam	int dirplen;
1752138568Ssam	struct statfs *fsb;
1753138568Ssam{
1754138568Ssam	struct statfs fsb1;
1755138568Ssam	struct addrinfo *ai;
1756138568Ssam	struct export_args *eap;
1757140766Ssam	char *cp = NULL;
1758138568Ssam	int done;
1759138568Ssam	char savedc = '\0';
1760138568Ssam	union {
1761138568Ssam		struct ufs_args ua;
1762138568Ssam		struct iso_args ia;
1763138568Ssam		struct msdosfs_args da;
1764138568Ssam		struct ntfs_args na;
1765138568Ssam	} args;
1766138568Ssam
1767138568Ssam	bzero(&args, sizeof args);
1768138568Ssam	/* XXX, we assume that all xx_args look like ufs_args. */
1769138568Ssam	args.ua.fspec = 0;
1770138568Ssam	eap = &args.ua.export;
1771138568Ssam
1772138568Ssam	eap->ex_flags = exflags;
1773120483Ssam	eap->ex_anon = *anoncrp;
1774120483Ssam	eap->ex_indexfile = ep->ex_indexfile;
1775120483Ssam	if (grp->gr_type == GT_HOST)
1776138568Ssam		ai = grp->gr_ptr.gt_addrinfo;
1777138568Ssam	else
1778116742Ssam		ai = NULL;
1779138568Ssam	done = FALSE;
1780120483Ssam	while (!done) {
1781138568Ssam		switch (grp->gr_type) {
1782148320Ssam		case GT_HOST:
1783116742Ssam			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
1784148320Ssam				goto skip;
1785148320Ssam			eap->ex_addr = ai->ai_addr;
1786138568Ssam			eap->ex_addrlen = ai->ai_addrlen;
1787138568Ssam			eap->ex_masklen = 0;
1788138568Ssam			break;
1789140766Ssam		case GT_NET:
1790120483Ssam			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
1791138568Ssam			    have_v6 == 0)
1792138568Ssam				goto skip;
1793120483Ssam			eap->ex_addr =
1794120483Ssam			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
1795120483Ssam			eap->ex_addrlen = args.ua.export.ex_addr->sa_len;
1796138568Ssam			eap->ex_mask =
1797147788Ssam			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
1798147788Ssam			eap->ex_masklen = args.ua.export.ex_mask->sa_len;
1799147788Ssam			break;
1800147788Ssam		case GT_DEFAULT:
1801147788Ssam			eap->ex_addr = NULL;
1802148323Ssam			eap->ex_addrlen = 0;
1803148323Ssam			eap->ex_mask = NULL;
1804147788Ssam			eap->ex_masklen = 0;
1805147788Ssam			break;
1806138568Ssam		case GT_IGNORE:
1807138568Ssam			return(0);
1808138568Ssam			break;
1809138568Ssam		default:
1810138568Ssam			syslog(LOG_ERR, "bad grouptype");
1811138568Ssam			if (cp)
1812138568Ssam				*cp = savedc;
1813138568Ssam			return (1);
1814138568Ssam		};
1815140498Ssam
1816140498Ssam		/*
1817140498Ssam		 * XXX:
1818140498Ssam		 * Maybe I should just use the fsb->f_mntonname path instead
1819140498Ssam		 * of looping back up the dirp to the mount point??
1820140498Ssam		 * Also, needs to know how to export all types of local
1821138568Ssam		 * exportable filesystems and not just "ufs".
1822148320Ssam		 */
1823119150Ssam		while (mount(fsb->f_fstypename, dirp,
1824138568Ssam		    fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1825138568Ssam			if (cp)
1826138568Ssam				*cp-- = savedc;
1827138568Ssam			else
1828138568Ssam				cp = dirp + dirplen - 1;
1829138568Ssam			if (opt_flags & OP_QUIET)
1830138568Ssam				return (1);
1831138568Ssam			if (errno == EPERM) {
1832138568Ssam				if (debug)
1833138568Ssam					warnx("can't change attributes for %s",
1834138568Ssam					    dirp);
1835138568Ssam				syslog(LOG_ERR,
1836138568Ssam				   "can't change attributes for %s", dirp);
1837138568Ssam				return (1);
1838138568Ssam			}
1839138568Ssam			if (opt_flags & OP_ALLDIRS) {
1840138568Ssam				if (errno == EINVAL)
1841138568Ssam					syslog(LOG_ERR,
1842138568Ssam		"-alldirs requested but %s is not a filesystem mountpoint",
1843138568Ssam						dirp);
1844138568Ssam				else
1845138568Ssam					syslog(LOG_ERR,
1846138568Ssam						"could not remount %s: %m",
1847138568Ssam						dirp);
1848138568Ssam				return (1);
1849138568Ssam			}
1850138568Ssam			/* back up over the last component */
1851138568Ssam			while (*cp == '/' && cp > dirp)
1852138568Ssam				cp--;
1853138568Ssam			while (*(cp - 1) != '/' && cp > dirp)
1854138568Ssam				cp--;
1855138568Ssam			if (cp == dirp) {
1856138568Ssam				if (debug)
1857148304Ssam					warnx("mnt unsucc");
1858138568Ssam				syslog(LOG_ERR, "can't export %s", dirp);
1859138568Ssam				return (1);
1860138568Ssam			}
1861138568Ssam			savedc = *cp;
1862138568Ssam			*cp = '\0';
1863138568Ssam			/* Check that we're still on the same filesystem. */
1864138568Ssam			if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
1865138568Ssam			    &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
1866139528Ssam				*cp = savedc;
1867139528Ssam				syslog(LOG_ERR, "can't export %s", dirp);
1868148320Ssam				return (1);
1869148320Ssam			}
1870148320Ssam		}
1871148320Ssamskip:
1872148582Ssam		if (ai != NULL)
1873148582Ssam			ai = ai->ai_next;
1874148582Ssam		if (ai == NULL)
1875148582Ssam			done = TRUE;
1876148582Ssam	}
1877148582Ssam	if (cp)
1878148582Ssam		*cp = savedc;
1879148582Ssam	return (0);
1880138568Ssam}
1881148301Ssam
1882138568Ssam/*
1883138568Ssam * Translate a net address.
1884138568Ssam *
1885138568Ssam * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
1886138568Ssam */
1887148320Ssamint
1888148320Ssamget_net(cp, net, maskflg)
1889148320Ssam	char *cp;
1890148320Ssam	struct netmsk *net;
1891138568Ssam	int maskflg;
1892138568Ssam{
1893138568Ssam	struct netent *np = NULL;
1894138568Ssam	char *name, *p, *prefp;
1895138568Ssam	struct sockaddr_in sin;
1896138568Ssam	struct sockaddr *sa = NULL;
1897138568Ssam	struct addrinfo hints, *ai = NULL;
1898138568Ssam	char netname[NI_MAXHOST];
1899138568Ssam	long preflen;
1900138568Ssam
1901120483Ssam	p = prefp = NULL;
1902138568Ssam	if ((opt_flags & OP_MASKLEN) && !maskflg) {
1903138568Ssam		p = strchr(cp, '/');
1904138568Ssam		*p = '\0';
1905119150Ssam		prefp = p + 1;
1906138568Ssam	}
1907138568Ssam
1908138568Ssam	/*
1909138568Ssam	 * Check for a numeric address first. We wish to avoid
1910138568Ssam	 * possible DNS lookups in getnetbyname().
1911138568Ssam	 */
1912138568Ssam	if (isxdigit(*cp) || *cp == ':') {
1913121180Ssam		memset(&hints, 0, sizeof hints);
1914120483Ssam		/* Ensure the mask and the network have the same family. */
1915120483Ssam		if (maskflg && (opt_flags & OP_NET))
1916116742Ssam			hints.ai_family = net->nt_net.ss_family;
1917138568Ssam		else if (!maskflg && (opt_flags & OP_HAVEMASK))
1918138568Ssam			hints.ai_family = net->nt_mask.ss_family;
1919138568Ssam		else
1920138568Ssam			hints.ai_family = AF_UNSPEC;
1921138568Ssam		hints.ai_flags = AI_NUMERICHOST;
1922116742Ssam		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
1923116742Ssam			sa = ai->ai_addr;
1924116742Ssam		if (sa != NULL && ai->ai_family == AF_INET) {
1925138568Ssam			/*
1926116742Ssam			 * The address in `cp' is really a network address, so
1927116742Ssam			 * use inet_network() to re-interpret this correctly.
1928138568Ssam			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
1929116742Ssam			 */
1930138568Ssam			bzero(&sin, sizeof sin);
1931138568Ssam			sin.sin_family = AF_INET;
1932138568Ssam			sin.sin_len = sizeof sin;
1933138568Ssam			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
1934138568Ssam			if (debug)
1935138568Ssam				fprintf(stderr, "get_net: v4 addr %s\n",
1936138568Ssam				    inet_ntoa(sin.sin_addr));
1937138568Ssam			sa = (struct sockaddr *)&sin;
1938138568Ssam		}
1939138568Ssam	}
1940138568Ssam	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
1941138568Ssam		bzero(&sin, sizeof sin);
1942138568Ssam		sin.sin_family = AF_INET;
1943138568Ssam		sin.sin_len = sizeof sin;
1944138568Ssam		sin.sin_addr = inet_makeaddr(np->n_net, 0);
1945138568Ssam		sa = (struct sockaddr *)&sin;
1946138568Ssam	}
1947116742Ssam	if (sa == NULL)
1948138568Ssam		goto fail;
1949138568Ssam
1950138568Ssam	if (maskflg) {
1951138568Ssam		/* The specified sockaddr is a mask. */
1952138568Ssam		if (checkmask(sa) != 0)
1953138568Ssam			goto fail;
1954138568Ssam		bcopy(sa, &net->nt_mask, sa->sa_len);
1955138568Ssam		opt_flags |= OP_HAVEMASK;
1956138568Ssam	} else {
1957138568Ssam		/* The specified sockaddr is a network address. */
1958138568Ssam		bcopy(sa, &net->nt_net, sa->sa_len);
1959138568Ssam
1960138568Ssam		/* Get a network name for the export list. */
1961138568Ssam		if (np) {
1962138568Ssam			name = np->n_name;
1963138568Ssam		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
1964138568Ssam		   NULL, 0, ninumeric) == 0) {
1965138568Ssam			name = netname;
1966138568Ssam		} else {
1967138568Ssam			goto fail;
1968138568Ssam		}
1969138568Ssam		if ((net->nt_name = strdup(name)) == NULL)
1970138568Ssam			out_of_mem();
1971138568Ssam
1972138568Ssam		/*
1973138568Ssam		 * Extract a mask from either a "/<masklen>" suffix, or
1974138568Ssam		 * from the class of an IPv4 address.
1975138568Ssam		 */
1976138568Ssam		if (opt_flags & OP_MASKLEN) {
1977138568Ssam			preflen = strtol(prefp, NULL, 10);
1978138568Ssam			if (preflen < 0L || preflen == LONG_MAX)
1979138568Ssam				goto fail;
1980138568Ssam			bcopy(sa, &net->nt_mask, sa->sa_len);
1981138568Ssam			if (makemask(&net->nt_mask, (int)preflen) != 0)
1982138568Ssam				goto fail;
1983138568Ssam			opt_flags |= OP_HAVEMASK;
1984138568Ssam			*p = '/';
1985138568Ssam		} else if (sa->sa_family == AF_INET &&
1986138568Ssam		    (opt_flags & OP_MASK) == 0) {
1987138568Ssam			in_addr_t addr;
1988138568Ssam
1989138568Ssam			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
1990138568Ssam			if (IN_CLASSA(addr))
1991138568Ssam				preflen = 8;
1992138568Ssam			else if (IN_CLASSB(addr))
1993138568Ssam				preflen = 16;
1994138568Ssam			else if (IN_CLASSC(addr))
1995138568Ssam				preflen = 24;
1996138568Ssam			else if (IN_CLASSD(addr))
1997138568Ssam				preflen = 28;
1998138568Ssam			else
1999138568Ssam				preflen = 32;	/* XXX */
2000138568Ssam
2001138568Ssam			bcopy(sa, &net->nt_mask, sa->sa_len);
2002138568Ssam			makemask(&net->nt_mask, (int)preflen);
2003138568Ssam			opt_flags |= OP_HAVEMASK;
2004138568Ssam		}
2005138568Ssam	}
2006138568Ssam
2007138568Ssam	if (ai)
2008138568Ssam		freeaddrinfo(ai);
2009138568Ssam	return 0;
2010138568Ssam
2011138568Ssamfail:
2012138568Ssam	if (ai)
2013138568Ssam		freeaddrinfo(ai);
2014138568Ssam	return 1;
2015138568Ssam}
2016138568Ssam
2017138568Ssam/*
2018138568Ssam * Parse out the next white space separated field
2019138568Ssam */
2020138568Ssamvoid
2021138568Ssamnextfield(cp, endcp)
2022138568Ssam	char **cp;
2023138568Ssam	char **endcp;
2024138568Ssam{
2025138568Ssam	char *p;
2026138568Ssam
2027138568Ssam	p = *cp;
2028138568Ssam	while (*p == ' ' || *p == '\t')
2029138568Ssam		p++;
2030138568Ssam	if (*p == '\n' || *p == '\0')
2031153973Ssam		*cp = *endcp = p;
2032153973Ssam	else {
2033138568Ssam		*cp = p++;
2034138568Ssam		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2035138568Ssam			p++;
2036138568Ssam		*endcp = p;
2037138568Ssam	}
2038138568Ssam}
2039138568Ssam
2040138568Ssam/*
2041138568Ssam * Get an exports file line. Skip over blank lines and handle line
2042138568Ssam * continuations.
2043138568Ssam */
2044138568Ssamint
2045138568Ssamget_line()
2046138568Ssam{
2047138568Ssam	char *p, *cp;
2048138568Ssam	size_t len;
2049138568Ssam	int totlen, cont_line;
2050138568Ssam
2051138568Ssam	/*
2052138568Ssam	 * Loop around ignoring blank lines and getting all continuation lines.
2053138568Ssam	 */
2054138568Ssam	p = line;
2055138568Ssam	totlen = 0;
2056138568Ssam	do {
2057138568Ssam		if ((p = fgetln(exp_file, &len)) == NULL)
2058138568Ssam			return (0);
2059138568Ssam		cp = p + len - 1;
2060138568Ssam		cont_line = 0;
2061138568Ssam		while (cp >= p &&
2062138568Ssam		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2063138568Ssam			if (*cp == '\\')
2064149031Ssam				cont_line = 1;
2065138568Ssam			cp--;
2066138568Ssam			len--;
2067138568Ssam		}
2068138568Ssam		if (cont_line) {
2069138568Ssam			*++cp = ' ';
2070139523Ssam			len++;
2071139523Ssam		}
2072139523Ssam		if (linesize < len + totlen + 1) {
2073139523Ssam			linesize = len + totlen + 1;
2074139523Ssam			line = realloc(line, linesize);
2075139523Ssam			if (line == NULL)
2076139523Ssam				out_of_mem();
2077139523Ssam		}
2078138568Ssam		memcpy(line + totlen, p, len);
2079138568Ssam		totlen += len;
2080139524Ssam		line[totlen] = '\0';
2081148307Ssam	} while (totlen == 0 || cont_line);
2082139528Ssam	return (1);
2083139528Ssam}
2084138568Ssam
2085138568Ssam/*
2086138568Ssam * Parse a description of a credential.
2087138568Ssam */
2088138568Ssamvoid
2089138568Ssamparsecred(namelist, cr)
2090138568Ssam	char *namelist;
2091138568Ssam	struct xucred *cr;
2092138568Ssam{
2093138568Ssam	char *name;
2094138568Ssam	int cnt;
2095138568Ssam	char *names;
2096138568Ssam	struct passwd *pw;
2097138568Ssam	struct group *gr;
2098149031Ssam	int ngroups, groups[NGROUPS + 1];
2099148941Ssam
2100148941Ssam	cr->cr_version = XUCRED_VERSION;
2101138568Ssam	/*
2102138568Ssam	 * Set up the unprivileged user.
2103138568Ssam	 */
2104138568Ssam	cr->cr_uid = -2;
2105138568Ssam	cr->cr_groups[0] = -2;
2106138568Ssam	cr->cr_ngroups = 1;
2107138568Ssam	/*
2108138568Ssam	 * Get the user's password table entry.
2109138568Ssam	 */
2110138568Ssam	names = strsep(&namelist, " \t\n");
2111138568Ssam	name = strsep(&names, ":");
2112138568Ssam	if (isdigit(*name) || *name == '-')
2113138568Ssam		pw = getpwuid(atoi(name));
2114138568Ssam	else
2115138568Ssam		pw = getpwnam(name);
2116138568Ssam	/*
2117138568Ssam	 * Credentials specified as those of a user.
2118138568Ssam	 */
2119138568Ssam	if (names == NULL) {
2120138568Ssam		if (pw == NULL) {
2121138568Ssam			syslog(LOG_ERR, "unknown user: %s", name);
2122138568Ssam			return;
2123138568Ssam		}
2124138568Ssam		cr->cr_uid = pw->pw_uid;
2125138568Ssam		ngroups = NGROUPS + 1;
2126138568Ssam		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2127138568Ssam			syslog(LOG_ERR, "too many groups");
2128138568Ssam		/*
2129138568Ssam		 * Convert from int's to gid_t's and compress out duplicate
2130138568Ssam		 */
2131138568Ssam		cr->cr_ngroups = ngroups - 1;
2132138568Ssam		cr->cr_groups[0] = groups[0];
2133138568Ssam		for (cnt = 2; cnt < ngroups; cnt++)
2134138568Ssam			cr->cr_groups[cnt - 1] = groups[cnt];
2135138568Ssam		return;
2136138568Ssam	}
2137138568Ssam	/*
2138138568Ssam	 * Explicit credential specified as a colon separated list:
2139138568Ssam	 *	uid:gid:gid:...
2140138568Ssam	 */
2141138568Ssam	if (pw != NULL)
2142138568Ssam		cr->cr_uid = pw->pw_uid;
2143138568Ssam	else if (isdigit(*name) || *name == '-')
2144138568Ssam		cr->cr_uid = atoi(name);
2145138568Ssam	else {
2146138568Ssam		syslog(LOG_ERR, "unknown user: %s", name);
2147138568Ssam		return;
2148153973Ssam	}
2149138568Ssam	cr->cr_ngroups = 0;
2150138568Ssam	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2151138568Ssam		name = strsep(&names, ":");
2152138568Ssam		if (isdigit(*name) || *name == '-') {
2153138568Ssam			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2154138568Ssam		} else {
2155138568Ssam			if ((gr = getgrnam(name)) == NULL) {
2156138568Ssam				syslog(LOG_ERR, "unknown group: %s", name);
2157138568Ssam				continue;
2158138568Ssam			}
2159138568Ssam			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2160140499Ssam		}
2161138568Ssam	}
2162138568Ssam	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2163138568Ssam		syslog(LOG_ERR, "too many groups");
2164138568Ssam}
2165138568Ssam
2166138568Ssam#define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2167138568Ssam/*
2168138568Ssam * Routines that maintain the remote mounttab
2169138568Ssam */
2170138568Ssamvoid
2171138568Ssamget_mountlist()
2172138568Ssam{
2173138568Ssam	struct mountlist *mlp, **mlpp;
2174138568Ssam	char *host, *dirp, *cp;
2175138568Ssam	char str[STRSIZ];
2176138568Ssam	FILE *mlfile;
2177138568Ssam
2178138568Ssam	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2179138568Ssam		if (errno == ENOENT)
2180138568Ssam			return;
2181138568Ssam		else {
2182138568Ssam			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2183138568Ssam			return;
2184138568Ssam		}
2185138568Ssam	}
2186138568Ssam	mlpp = &mlhead;
2187138568Ssam	while (fgets(str, STRSIZ, mlfile) != NULL) {
2188138568Ssam		cp = str;
2189149031Ssam		host = strsep(&cp, " \t\n");
2190138568Ssam		dirp = strsep(&cp, " \t\n");
2191138568Ssam		if (host == NULL || dirp == NULL)
2192138568Ssam			continue;
2193138568Ssam		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2194138568Ssam		if (mlp == (struct mountlist *)NULL)
2195138568Ssam			out_of_mem();
2196138568Ssam		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2197138568Ssam		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2198138568Ssam		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2199140499Ssam		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2200140499Ssam		mlp->ml_next = (struct mountlist *)NULL;
2201140499Ssam		*mlpp = mlp;
2202140499Ssam		mlpp = &mlp->ml_next;
2203140499Ssam	}
2204140499Ssam	fclose(mlfile);
2205140499Ssam}
2206140499Ssam
2207140499Ssamvoid
2208140499Ssamdel_mlist(char *hostp, char *dirp)
2209140499Ssam{
2210140499Ssam	struct mountlist *mlp, **mlpp;
2211138568Ssam	struct mountlist *mlp2;
2212138568Ssam	FILE *mlfile;
2213138568Ssam	int fnd = 0;
2214138568Ssam
2215138568Ssam	mlpp = &mlhead;
2216138568Ssam	mlp = mlhead;
2217140753Ssam	while (mlp) {
2218138568Ssam		if (!strcmp(mlp->ml_host, hostp) &&
2219138568Ssam		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2220138568Ssam			fnd = 1;
2221138568Ssam			mlp2 = mlp;
2222138568Ssam			*mlpp = mlp = mlp->ml_next;
2223138568Ssam			free((caddr_t)mlp2);
2224138568Ssam		} else {
2225138568Ssam			mlpp = &mlp->ml_next;
2226140753Ssam			mlp = mlp->ml_next;
2227138568Ssam		}
2228138568Ssam	}
2229138568Ssam	if (fnd) {
2230138568Ssam		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2231138568Ssam			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2232138568Ssam			return;
2233138568Ssam		}
2234140753Ssam		mlp = mlhead;
2235138568Ssam		while (mlp) {
2236138568Ssam			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2237138568Ssam			mlp = mlp->ml_next;
2238138568Ssam		}
2239138568Ssam		fclose(mlfile);
2240138568Ssam	}
2241140753Ssam}
2242138568Ssam
2243138568Ssamvoid
2244138568Ssamadd_mlist(hostp, dirp)
2245138568Ssam	char *hostp, *dirp;
2246138568Ssam{
2247138568Ssam	struct mountlist *mlp, **mlpp;
2248138568Ssam	FILE *mlfile;
2249138568Ssam
2250138568Ssam	mlpp = &mlhead;
2251138568Ssam	mlp = mlhead;
2252138568Ssam	while (mlp) {
2253138568Ssam		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2254138568Ssam			return;
2255138568Ssam		mlpp = &mlp->ml_next;
2256138568Ssam		mlp = mlp->ml_next;
2257138568Ssam	}
2258138568Ssam	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2259138568Ssam	if (mlp == (struct mountlist *)NULL)
2260138568Ssam		out_of_mem();
2261138568Ssam	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2262138568Ssam	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2263148304Ssam	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2264138568Ssam	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2265148304Ssam	mlp->ml_next = (struct mountlist *)NULL;
2266138568Ssam	*mlpp = mlp;
2267138568Ssam	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2268138568Ssam		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2269138568Ssam		return;
2270138568Ssam	}
2271138568Ssam	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2272138568Ssam	fclose(mlfile);
2273138568Ssam}
2274138568Ssam
2275138568Ssam/*
2276138568Ssam * Free up a group list.
2277138568Ssam */
2278138568Ssamvoid
2279138568Ssamfree_grp(grp)
2280138568Ssam	struct grouplist *grp;
2281138568Ssam{
2282138568Ssam	if (grp->gr_type == GT_HOST) {
2283138568Ssam		if (grp->gr_ptr.gt_addrinfo != NULL)
2284138568Ssam			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2285138568Ssam	} else if (grp->gr_type == GT_NET) {
2286138568Ssam		if (grp->gr_ptr.gt_net.nt_name)
2287138568Ssam			free(grp->gr_ptr.gt_net.nt_name);
2288138568Ssam	}
2289138568Ssam	free((caddr_t)grp);
2290138568Ssam}
2291138568Ssam
2292138568Ssam#ifdef DEBUG
2293138568Ssamvoid
2294138568SsamSYSLOG(int pri, const char *fmt, ...)
2295138568Ssam{
2296138568Ssam	va_list ap;
2297148863Ssam
2298138568Ssam	va_start(ap, fmt);
2299138568Ssam	vfprintf(stderr, fmt, ap);
2300138568Ssam	va_end(ap);
2301138568Ssam}
2302138568Ssam#endif /* DEBUG */
2303138568Ssam
2304138568Ssam/*
2305138568Ssam * Check options for consistency.
2306138568Ssam */
2307138568Ssamint
2308138568Ssamcheck_options(dp)
2309138568Ssam	struct dirlist *dp;
2310138568Ssam{
2311138568Ssam
2312138568Ssam	if (dp == (struct dirlist *)NULL)
2313148863Ssam	    return (1);
2314148863Ssam	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2315148863Ssam	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2316148863Ssam	    return (1);
2317148863Ssam	}
2318148863Ssam	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2319148863Ssam		syslog(LOG_ERR, "-mask requires -network");
2320148863Ssam		return (1);
2321148863Ssam	}
2322148863Ssam	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2323148863Ssam		syslog(LOG_ERR, "-network requires mask specification");
2324138568Ssam		return (1);
2325138568Ssam	}
2326138568Ssam	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
2327138568Ssam		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
2328138568Ssam		return (1);
2329138568Ssam	}
2330138568Ssam	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2331138568Ssam	    syslog(LOG_ERR, "-alldirs has multiple directories");
2332138568Ssam	    return (1);
2333138568Ssam	}
2334138568Ssam	return (0);
2335138568Ssam}
2336138568Ssam
2337138568Ssam/*
2338138568Ssam * Check an absolute directory path for any symbolic links. Return true
2339138568Ssam */
2340138568Ssamint
2341138568Ssamcheck_dirpath(dirp)
2342138568Ssam	char *dirp;
2343138568Ssam{
2344138568Ssam	char *cp;
2345138568Ssam	int ret = 1;
2346148863Ssam	struct stat sb;
2347138568Ssam
2348148863Ssam	cp = dirp + 1;
2349148863Ssam	while (*cp && ret) {
2350148863Ssam		if (*cp == '/') {
2351148863Ssam			*cp = '\0';
2352148863Ssam			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2353148863Ssam				ret = 0;
2354148863Ssam			*cp = '/';
2355148863Ssam		}
2356148863Ssam		cp++;
2357148863Ssam	}
2358138568Ssam	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2359138568Ssam		ret = 0;
2360138568Ssam	return (ret);
2361}
2362
2363/*
2364 * Make a netmask according to the specified prefix length. The ss_family
2365 * and other non-address fields must be initialised before calling this.
2366 */
2367int
2368makemask(struct sockaddr_storage *ssp, int bitlen)
2369{
2370	u_char *p;
2371	int bits, i, len;
2372
2373	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
2374		return (-1);
2375	if (bitlen > len * CHAR_BIT)
2376		return (-1);
2377
2378	for (i = 0; i < len; i++) {
2379		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
2380		*p++ = (1 << bits) - 1;
2381		bitlen -= bits;
2382	}
2383	return 0;
2384}
2385
2386/*
2387 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
2388 * is acceptable (i.e. of the form 1...10....0).
2389 */
2390int
2391checkmask(struct sockaddr *sa)
2392{
2393	u_char *mask;
2394	int i, len;
2395
2396	if ((mask = sa_rawaddr(sa, &len)) == NULL)
2397		return (-1);
2398
2399	for (i = 0; i < len; i++)
2400		if (mask[i] != 0xff)
2401			break;
2402	if (i < len) {
2403		if (~mask[i] & (u_char)(~mask[i] + 1))
2404			return (-1);
2405		i++;
2406	}
2407	for (; i < len; i++)
2408		if (mask[i] != 0)
2409			return (-1);
2410	return (0);
2411}
2412
2413/*
2414 * Compare two sockaddrs according to a specified mask. Return zero if
2415 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
2416 * If samask is NULL, perform a full comparision.
2417 */
2418int
2419sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
2420{
2421	unsigned char *p1, *p2, *mask;
2422	int len, i;
2423
2424	if (sa1->sa_family != sa2->sa_family ||
2425	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
2426	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
2427		return (1);
2428
2429	switch (sa1->sa_family) {
2430	case AF_INET6:
2431		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
2432		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
2433			return (1);
2434		break;
2435	}
2436
2437	/* Simple binary comparison if no mask specified. */
2438	if (samask == NULL)
2439		return (memcmp(p1, p2, len));
2440
2441	/* Set up the mask, and do a mask-based comparison. */
2442	if (sa1->sa_family != samask->sa_family ||
2443	    (mask = sa_rawaddr(samask, NULL)) == NULL)
2444		return (1);
2445
2446	for (i = 0; i < len; i++)
2447		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
2448			return (1);
2449	return (0);
2450}
2451
2452/*
2453 * Return a pointer to the part of the sockaddr that contains the
2454 * raw address, and set *nbytes to its length in bytes. Returns
2455 * NULL if the address family is unknown.
2456 */
2457void *
2458sa_rawaddr(struct sockaddr *sa, int *nbytes) {
2459	void *p;
2460	int len;
2461
2462	switch (sa->sa_family) {
2463	case AF_INET:
2464		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
2465		p = &((struct sockaddr_in *)sa)->sin_addr;
2466		break;
2467	case AF_INET6:
2468		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
2469		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
2470		break;
2471	default:
2472		p = NULL;
2473		len = 0;
2474	}
2475
2476	if (nbytes != NULL)
2477		*nbytes = len;
2478	return (p);
2479}
2480
2481void
2482huphandler(int sig)
2483{
2484	got_sighup = 1;
2485}
2486
2487void terminate(sig)
2488int sig;
2489{
2490	close(mountdlockfd);
2491	unlink(MOUNTDLOCK);
2492	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
2493	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
2494	exit (0);
2495}
2496