mountd.c revision 222623
11558Srgrimes/*
21558Srgrimes * Copyright (c) 1989, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Herb Hasler and Rick Macklem at The University of Guelph.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 4. Neither the name of the University nor the names of its contributors
171558Srgrimes *    may be used to endorse or promote products derived from this software
181558Srgrimes *    without specific prior written permission.
191558Srgrimes *
201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301558Srgrimes * SUCH DAMAGE.
311558Srgrimes */
321558Srgrimes
331558Srgrimes#ifndef lint
3437663Scharnierstatic const char copyright[] =
351558Srgrimes"@(#) Copyright (c) 1989, 1993\n\
361558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
372999Swollman#endif /*not lint*/
381558Srgrimes
39105267Scharnier#if 0
401558Srgrimes#ifndef lint
4137663Scharnierstatic char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
42105267Scharnier#endif /*not lint*/
4337663Scharnier#endif
441558Srgrimes
45105267Scharnier#include <sys/cdefs.h>
46105267Scharnier__FBSDID("$FreeBSD: head/usr.sbin/mountd/mountd.c 222623 2011-06-02 19:33:33Z rmacklem $");
47105267Scharnier
481558Srgrimes#include <sys/param.h>
49192934Srmacklem#include <sys/fcntl.h>
50192934Srmacklem#include <sys/linker.h>
51192934Srmacklem#include <sys/module.h>
521558Srgrimes#include <sys/mount.h>
531558Srgrimes#include <sys/stat.h>
54192934Srmacklem#include <sys/sysctl.h>
551558Srgrimes#include <sys/syslog.h>
561558Srgrimes
571558Srgrimes#include <rpc/rpc.h>
58109363Smbr#include <rpc/rpc_com.h>
591558Srgrimes#include <rpc/pmap_clnt.h>
6074462Salfred#include <rpc/pmap_prot.h>
6174462Salfred#include <rpcsvc/mount.h>
629336Sdfr#include <nfs/nfsproto.h>
63192934Srmacklem#include <nfs/nfssvc.h>
6483653Speter#include <nfsserver/nfs.h>
651558Srgrimes
66192934Srmacklem#include <fs/nfs/nfsport.h>
67192934Srmacklem
681558Srgrimes#include <arpa/inet.h>
691558Srgrimes
701558Srgrimes#include <ctype.h>
7137663Scharnier#include <err.h>
721558Srgrimes#include <errno.h>
731558Srgrimes#include <grp.h>
74149433Spjd#include <libutil.h>
75103949Smike#include <limits.h>
761558Srgrimes#include <netdb.h>
771558Srgrimes#include <pwd.h>
781558Srgrimes#include <signal.h>
791558Srgrimes#include <stdio.h>
801558Srgrimes#include <stdlib.h>
811558Srgrimes#include <string.h>
821558Srgrimes#include <unistd.h>
831558Srgrimes#include "pathnames.h"
84158857Srodrigc#include "mntopts.h"
851558Srgrimes
861558Srgrimes#ifdef DEBUG
871558Srgrimes#include <stdarg.h>
881558Srgrimes#endif
891558Srgrimes
901558Srgrimes/*
911558Srgrimes * Structures for keeping the mount list and export list
921558Srgrimes */
931558Srgrimesstruct mountlist {
941558Srgrimes	struct mountlist *ml_next;
95194880Sdfr	char	ml_host[MNTNAMLEN+1];
96194880Sdfr	char	ml_dirp[MNTPATHLEN+1];
971558Srgrimes};
981558Srgrimes
991558Srgrimesstruct dirlist {
1001558Srgrimes	struct dirlist	*dp_left;
1011558Srgrimes	struct dirlist	*dp_right;
1021558Srgrimes	int		dp_flag;
1031558Srgrimes	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
1041558Srgrimes	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
1051558Srgrimes};
1061558Srgrimes/* dp_flag bits */
1071558Srgrimes#define	DP_DEFSET	0x1
1089336Sdfr#define DP_HOSTSET	0x2
1091558Srgrimes
1101558Srgrimesstruct exportlist {
1111558Srgrimes	struct exportlist *ex_next;
1121558Srgrimes	struct dirlist	*ex_dirl;
1131558Srgrimes	struct dirlist	*ex_defdir;
1141558Srgrimes	int		ex_flag;
1151558Srgrimes	fsid_t		ex_fs;
1161558Srgrimes	char		*ex_fsdir;
11727447Sdfr	char		*ex_indexfile;
118184588Sdfr	int		ex_numsecflavors;
119184588Sdfr	int		ex_secflavors[MAXSECFLAVORS];
1201558Srgrimes};
1211558Srgrimes/* ex_flag bits */
1221558Srgrimes#define	EX_LINKED	0x1
1231558Srgrimes
1241558Srgrimesstruct netmsk {
12574462Salfred	struct sockaddr_storage nt_net;
12675801Siedowse	struct sockaddr_storage nt_mask;
12742144Sdfr	char		*nt_name;
1281558Srgrimes};
1291558Srgrimes
1301558Srgrimesunion grouptypes {
13174462Salfred	struct addrinfo *gt_addrinfo;
1321558Srgrimes	struct netmsk	gt_net;
1331558Srgrimes};
1341558Srgrimes
1351558Srgrimesstruct grouplist {
1361558Srgrimes	int gr_type;
1371558Srgrimes	union grouptypes gr_ptr;
1381558Srgrimes	struct grouplist *gr_next;
1391558Srgrimes};
1401558Srgrimes/* Group types */
1411558Srgrimes#define	GT_NULL		0x0
1421558Srgrimes#define	GT_HOST		0x1
1431558Srgrimes#define	GT_NET		0x2
14475641Siedowse#define	GT_DEFAULT	0x3
1457401Swpaul#define GT_IGNORE	0x5
1461558Srgrimes
1471558Srgrimesstruct hostlist {
1489336Sdfr	int		 ht_flag;	/* Uses DP_xx bits */
1491558Srgrimes	struct grouplist *ht_grp;
1501558Srgrimes	struct hostlist	 *ht_next;
1511558Srgrimes};
1521558Srgrimes
1539336Sdfrstruct fhreturn {
1549336Sdfr	int	fhr_flag;
1559336Sdfr	int	fhr_vers;
1569336Sdfr	nfsfh_t	fhr_fh;
157184588Sdfr	int	fhr_numsecflavors;
158184588Sdfr	int	*fhr_secflavors;
1599336Sdfr};
1609336Sdfr
161222623Srmacklem#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
162222623Srmacklem
1631558Srgrimes/* Global defs */
16492882Simpchar	*add_expdir(struct dirlist **, char *, int);
16592882Simpvoid	add_dlist(struct dirlist **, struct dirlist *,
16692882Simp				struct grouplist *, int);
16792882Simpvoid	add_mlist(char *, char *);
16892882Simpint	check_dirpath(char *);
16992882Simpint	check_options(struct dirlist *);
17075801Siedowseint	checkmask(struct sockaddr *sa);
17192882Simpint	chk_host(struct dirlist *, struct sockaddr *, int *, int *);
172222623Srmacklemstatic int	create_service(struct netconfig *nconf);
173222623Srmacklemstatic void	complete_service(struct netconfig *nconf, char *port_str);
174222623Srmacklemstatic void	clearout_service(void);
17575635Siedowsevoid	del_mlist(char *hostp, char *dirp);
17692882Simpstruct dirlist *dirp_search(struct dirlist *, char *);
17792882Simpint	do_mount(struct exportlist *, struct grouplist *, int,
17892882Simp		struct xucred *, char *, int, struct statfs *);
17992882Simpint	do_opt(char **, char **, struct exportlist *, struct grouplist *,
18092882Simp				int *, int *, struct xucred *);
18192882Simpstruct	exportlist *ex_search(fsid_t *);
18292882Simpstruct	exportlist *get_exp(void);
18392882Simpvoid	free_dir(struct dirlist *);
18492882Simpvoid	free_exp(struct exportlist *);
18592882Simpvoid	free_grp(struct grouplist *);
18692882Simpvoid	free_host(struct hostlist *);
18792882Simpvoid	get_exportlist(void);
18892882Simpint	get_host(char *, struct grouplist *, struct grouplist *);
18992882Simpstruct hostlist *get_ht(void);
19092882Simpint	get_line(void);
19192882Simpvoid	get_mountlist(void);
19292882Simpint	get_net(char *, struct netmsk *, int);
19392882Simpvoid	getexp_err(struct exportlist *, struct grouplist *);
19492882Simpstruct grouplist *get_grp(void);
19592882Simpvoid	hang_dirp(struct dirlist *, struct grouplist *,
19692882Simp				struct exportlist *, int);
19775754Siedowsevoid	huphandler(int sig);
19875801Siedowseint	makemask(struct sockaddr_storage *ssp, int bitlen);
19992882Simpvoid	mntsrv(struct svc_req *, SVCXPRT *);
20092882Simpvoid	nextfield(char **, char **);
20192882Simpvoid	out_of_mem(void);
20292882Simpvoid	parsecred(char *, struct xucred *);
203216587Scharnierint	parsesec(char *, struct exportlist *);
204100117Salfredint	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
20575801Siedowsevoid	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
20675801Siedowseint	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
20775801Siedowse    struct sockaddr *samask);
20892882Simpint	scan_tree(struct dirlist *, struct sockaddr *);
20992882Simpstatic void usage(void);
21092882Simpint	xdr_dir(XDR *, char *);
21192882Simpint	xdr_explist(XDR *, caddr_t);
212100117Salfredint	xdr_explist_brief(XDR *, caddr_t);
213216587Scharnierint	xdr_explist_common(XDR *, caddr_t, int);
21492882Simpint	xdr_fhs(XDR *, caddr_t);
21592882Simpint	xdr_mlist(XDR *, caddr_t);
21692882Simpvoid	terminate(int);
2171558Srgrimes
2181558Srgrimesstruct exportlist *exphead;
2191558Srgrimesstruct mountlist *mlhead;
2201558Srgrimesstruct grouplist *grphead;
221166440Spjdchar *exnames_default[2] = { _PATH_EXPORTS, NULL };
222166440Spjdchar **exnames;
223172827Smatteochar **hosts = NULL;
22472650Sgreenstruct xucred def_anon = {
22591354Sdd	XUCRED_VERSION,
22672650Sgreen	(uid_t)-2,
2271558Srgrimes	1,
22872650Sgreen	{ (gid_t)-2 },
22972650Sgreen	NULL
2301558Srgrimes};
23125087Sdfrint force_v2 = 0;
2329336Sdfrint resvport_only = 1;
233172827Smatteoint nhosts = 0;
2349336Sdfrint dir_only = 1;
235121767Speterint dolog = 0;
23675754Siedowseint got_sighup = 0;
237172827Smatteoint xcreated = 0;
23874462Salfred
239172827Smatteochar *svcport_str = NULL;
240222623Srmacklemstatic int	mallocd_svcport = 0;
241222623Srmacklemstatic int	*sock_fd;
242222623Srmacklemstatic int	sock_fdcnt;
243222623Srmacklemstatic int	sock_fdpos;
244172827Smatteo
2451558Srgrimesint opt_flags;
24674462Salfredstatic int have_v6 = 1;
24774462Salfred
248192934Srmacklemint v4root_phase = 0;
249192934Srmacklemchar v4root_dirpath[PATH_MAX + 1];
250220980Srmacklemint run_v4server = 1;
251192934Srmacklemint has_publicfh = 0;
252192934Srmacklem
253149433Spjdstruct pidfh *pfh = NULL;
25475801Siedowse/* Bits for opt_flags above */
2551558Srgrimes#define	OP_MAPROOT	0x01
2561558Srgrimes#define	OP_MAPALL	0x02
25783653Speter/* 0x4 free */
2581558Srgrimes#define	OP_MASK		0x08
2591558Srgrimes#define	OP_NET		0x10
2601558Srgrimes#define	OP_ALLDIRS	0x40
26175801Siedowse#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
262100336Sjoerg#define	OP_QUIET	0x100
26374462Salfred#define OP_MASKLEN	0x200
264184588Sdfr#define OP_SEC		0x400
2651558Srgrimes
2661558Srgrimes#ifdef DEBUG
2671558Srgrimesint debug = 1;
26892882Simpvoid	SYSLOG(int, const char *, ...) __printflike(2, 3);
2691558Srgrimes#define syslog SYSLOG
2701558Srgrimes#else
2711558Srgrimesint debug = 0;
2721558Srgrimes#endif
2731558Srgrimes
2741558Srgrimes/*
2751558Srgrimes * Mountd server for NFS mount protocol as described in:
2761558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A
2771558Srgrimes * The optional arguments are the exports file name
2781558Srgrimes * default: _PATH_EXPORTS
2791558Srgrimes * and "-n" to allow nonroot mount.
2801558Srgrimes */
2811558Srgrimesint
282216587Scharniermain(int argc, char **argv)
2831558Srgrimes{
28475754Siedowse	fd_set readfds;
285172827Smatteo	struct netconfig *nconf;
286172827Smatteo	char *endptr, **hosts_bak;
287172827Smatteo	void *nc_handle;
288149433Spjd	pid_t otherpid;
289172827Smatteo	in_port_t svcport;
290172827Smatteo	int c, k, s;
291109363Smbr	int maxrec = RPC_MAXDATASIZE;
292222623Srmacklem	int attempt_cnt, port_len, port_pos, ret;
293222623Srmacklem	char **port_list;
2941558Srgrimes
29574462Salfred	/* Check that another mountd isn't already running. */
296150214Spjd	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
297149433Spjd	if (pfh == NULL) {
298149433Spjd		if (errno == EEXIST)
299149433Spjd			errx(1, "mountd already running, pid: %d.", otherpid);
300149433Spjd		warn("cannot open or create pidfile");
301149433Spjd	}
30274462Salfred
30374462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
30474462Salfred	if (s < 0)
30574462Salfred		have_v6 = 0;
30674462Salfred	else
30774462Salfred		close(s);
3082999Swollman
309220980Srmacklem	while ((c = getopt(argc, argv, "2deh:lnop:r")) != -1)
3101558Srgrimes		switch (c) {
31125087Sdfr		case '2':
31225087Sdfr			force_v2 = 1;
31325087Sdfr			break;
314192993Srmacklem		case 'e':
315220980Srmacklem			/* now a no-op, since this is the default */
316192934Srmacklem			break;
3179336Sdfr		case 'n':
3189336Sdfr			resvport_only = 0;
3199336Sdfr			break;
3209336Sdfr		case 'r':
3219336Sdfr			dir_only = 0;
3229336Sdfr			break;
3238688Sphk		case 'd':
3248688Sphk			debug = debug ? 0 : 1;
3258688Sphk			break;
32631656Sguido		case 'l':
327121767Speter			dolog = 1;
32831656Sguido			break;
329220980Srmacklem		case 'o':
330220980Srmacklem			run_v4server = 0;
331220980Srmacklem			break;
332126572Sbms		case 'p':
333126572Sbms			endptr = NULL;
334126572Sbms			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
335126572Sbms			if (endptr == NULL || *endptr != '\0' ||
336126572Sbms			    svcport == 0 || svcport >= IPPORT_MAX)
337126572Sbms				usage();
338172827Smatteo			svcport_str = strdup(optarg);
339126572Sbms			break;
340172827Smatteo		case 'h':
341172827Smatteo			++nhosts;
342172827Smatteo			hosts_bak = hosts;
343172827Smatteo			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
344172827Smatteo			if (hosts_bak == NULL) {
345172827Smatteo				if (hosts != NULL) {
346172827Smatteo					for (k = 0; k < nhosts; k++)
347172827Smatteo						free(hosts[k]);
348172827Smatteo					free(hosts);
349172827Smatteo					out_of_mem();
350172827Smatteo				}
351172827Smatteo			}
352172827Smatteo			hosts = hosts_bak;
353172827Smatteo			hosts[nhosts - 1] = strdup(optarg);
354172827Smatteo			if (hosts[nhosts - 1] == NULL) {
355172827Smatteo				for (k = 0; k < (nhosts - 1); k++)
356172827Smatteo					free(hosts[k]);
357172827Smatteo				free(hosts);
358172827Smatteo				out_of_mem();
359172827Smatteo			}
360172827Smatteo			break;
3611558Srgrimes		default:
36237663Scharnier			usage();
3631558Srgrimes		};
364192934Srmacklem
365192934Srmacklem	/*
366220980Srmacklem	 * Unless the "-o" option was specified, try and run "nfsd".
367220980Srmacklem	 * If "-o" was specified, try and run "nfsserver".
368192934Srmacklem	 */
369192934Srmacklem	if (run_v4server > 0) {
370192934Srmacklem		if (modfind("nfsd") < 0) {
371192934Srmacklem			/* Not present in kernel, try loading it */
372192934Srmacklem			if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
373192934Srmacklem				errx(1, "NFS server is not available");
374192934Srmacklem		}
375192934Srmacklem	} else if (modfind("nfsserver") < 0) {
376192934Srmacklem		/* Not present in kernel, try loading it */
377192934Srmacklem		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
378192934Srmacklem			errx(1, "NFS server is not available");
379192934Srmacklem	}
380192934Srmacklem
3811558Srgrimes	argc -= optind;
3821558Srgrimes	argv += optind;
3831558Srgrimes	grphead = (struct grouplist *)NULL;
3841558Srgrimes	exphead = (struct exportlist *)NULL;
3851558Srgrimes	mlhead = (struct mountlist *)NULL;
386166440Spjd	if (argc > 0)
387166440Spjd		exnames = argv;
388166440Spjd	else
389166440Spjd		exnames = exnames_default;
3901558Srgrimes	openlog("mountd", LOG_PID, LOG_DAEMON);
3911558Srgrimes	if (debug)
39237663Scharnier		warnx("getting export list");
3931558Srgrimes	get_exportlist();
3941558Srgrimes	if (debug)
39537663Scharnier		warnx("getting mount list");
3961558Srgrimes	get_mountlist();
3971558Srgrimes	if (debug)
39837663Scharnier		warnx("here we go");
3991558Srgrimes	if (debug == 0) {
4001558Srgrimes		daemon(0, 0);
4011558Srgrimes		signal(SIGINT, SIG_IGN);
4021558Srgrimes		signal(SIGQUIT, SIG_IGN);
4031558Srgrimes	}
40475754Siedowse	signal(SIGHUP, huphandler);
40574462Salfred	signal(SIGTERM, terminate);
406164394Srodrigc	signal(SIGPIPE, SIG_IGN);
407149433Spjd
408149433Spjd	pidfile_write(pfh);
409149433Spjd
410194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
411194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
412109363Smbr	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
413109363Smbr
41424759Sguido	if (!resvport_only) {
41583687Speter		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
41683687Speter		    &resvport_only, sizeof(resvport_only)) != 0 &&
41783687Speter		    errno != ENOENT) {
41824759Sguido			syslog(LOG_ERR, "sysctl: %m");
41924759Sguido			exit(1);
42024759Sguido		}
42124330Sguido	}
422126572Sbms
423172827Smatteo	/*
424172827Smatteo	 * If no hosts were specified, add a wildcard entry to bind to
425172827Smatteo	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
426172827Smatteo	 * list.
427172827Smatteo	 */
428172827Smatteo	if (nhosts == 0) {
429172827Smatteo		hosts = malloc(sizeof(char**));
430172827Smatteo		if (hosts == NULL)
431172827Smatteo			out_of_mem();
432172827Smatteo		hosts[0] = "*";
433172827Smatteo		nhosts = 1;
434172827Smatteo	} else {
435172827Smatteo		hosts_bak = hosts;
436172827Smatteo		if (have_v6) {
437172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 2) *
438172827Smatteo			    sizeof(char *));
439172827Smatteo			if (hosts_bak == NULL) {
440172827Smatteo				for (k = 0; k < nhosts; k++)
441172827Smatteo					free(hosts[k]);
442172827Smatteo		    		free(hosts);
443172827Smatteo		    		out_of_mem();
444172827Smatteo			} else
445172827Smatteo				hosts = hosts_bak;
446172827Smatteo			nhosts += 2;
447172827Smatteo			hosts[nhosts - 2] = "::1";
448172827Smatteo		} else {
449172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
450172827Smatteo			if (hosts_bak == NULL) {
451172827Smatteo				for (k = 0; k < nhosts; k++)
452172827Smatteo					free(hosts[k]);
453172827Smatteo				free(hosts);
454172827Smatteo				out_of_mem();
455172827Smatteo			} else {
456172827Smatteo				nhosts += 1;
457172827Smatteo				hosts = hosts_bak;
458126572Sbms			}
459172827Smatteo		}
46074462Salfred
461172827Smatteo		hosts[nhosts - 1] = "127.0.0.1";
46274462Salfred	}
46374462Salfred
464222623Srmacklem	attempt_cnt = 1;
465222623Srmacklem	sock_fdcnt = 0;
466222623Srmacklem	sock_fd = NULL;
467222623Srmacklem	port_list = NULL;
468222623Srmacklem	port_len = 0;
469172827Smatteo	nc_handle = setnetconfig();
470172827Smatteo	while ((nconf = getnetconfig(nc_handle))) {
471172827Smatteo		if (nconf->nc_flag & NC_VISIBLE) {
472172827Smatteo			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
473172827Smatteo			    "inet6") == 0) {
474172827Smatteo				/* DO NOTHING */
475222623Srmacklem			} else {
476222623Srmacklem				ret = create_service(nconf);
477222623Srmacklem				if (ret == 1)
478222623Srmacklem					/* Ignore this call */
479222623Srmacklem					continue;
480222623Srmacklem				if (ret < 0) {
481222623Srmacklem					/*
482222623Srmacklem					 * Failed to bind port, so close off
483222623Srmacklem					 * all sockets created and try again
484222623Srmacklem					 * if the port# was dynamically
485222623Srmacklem					 * assigned via bind(2).
486222623Srmacklem					 */
487222623Srmacklem					clearout_service();
488222623Srmacklem					if (mallocd_svcport != 0 &&
489222623Srmacklem					    attempt_cnt < GETPORT_MAXTRY) {
490222623Srmacklem						free(svcport_str);
491222623Srmacklem						svcport_str = NULL;
492222623Srmacklem						mallocd_svcport = 0;
493222623Srmacklem					} else {
494222623Srmacklem						errno = EADDRINUSE;
495222623Srmacklem						syslog(LOG_ERR,
496222623Srmacklem						    "bindresvport_sa: %m");
497222623Srmacklem						exit(1);
498222623Srmacklem					}
499222623Srmacklem
500222623Srmacklem					/* Start over at the first service. */
501222623Srmacklem					free(sock_fd);
502222623Srmacklem					sock_fdcnt = 0;
503222623Srmacklem					sock_fd = NULL;
504222623Srmacklem					nc_handle = setnetconfig();
505222623Srmacklem					attempt_cnt++;
506222623Srmacklem				} else if (mallocd_svcport != 0 &&
507222623Srmacklem				    attempt_cnt == GETPORT_MAXTRY) {
508222623Srmacklem					/*
509222623Srmacklem					 * For the last attempt, allow
510222623Srmacklem					 * different port #s for each nconf
511222623Srmacklem					 * by saving the svcport_str and
512222623Srmacklem					 * setting it back to NULL.
513222623Srmacklem					 */
514222623Srmacklem					port_list = realloc(port_list,
515222623Srmacklem					    (port_len + 1) * sizeof(char *));
516222623Srmacklem					if (port_list == NULL)
517222623Srmacklem						out_of_mem();
518222623Srmacklem					port_list[port_len++] = svcport_str;
519222623Srmacklem					svcport_str = NULL;
520222623Srmacklem					mallocd_svcport = 0;
521222623Srmacklem				}
522222623Srmacklem			}
523222623Srmacklem		}
524222623Srmacklem	}
525222623Srmacklem
526222623Srmacklem	/*
527222623Srmacklem	 * Successfully bound the ports, so call complete_service() to
528222623Srmacklem	 * do the rest of the setup on the service(s).
529222623Srmacklem	 */
530222623Srmacklem	sock_fdpos = 0;
531222623Srmacklem	port_pos = 0;
532222623Srmacklem	nc_handle = setnetconfig();
533222623Srmacklem	while ((nconf = getnetconfig(nc_handle))) {
534222623Srmacklem		if (nconf->nc_flag & NC_VISIBLE) {
535222623Srmacklem			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
536222623Srmacklem			    "inet6") == 0) {
537222623Srmacklem				/* DO NOTHING */
538222623Srmacklem			} else if (port_list != NULL) {
539222623Srmacklem				if (port_pos >= port_len) {
540222623Srmacklem					syslog(LOG_ERR, "too many port#s");
541222623Srmacklem					exit(1);
542222623Srmacklem				}
543222623Srmacklem				complete_service(nconf, port_list[port_pos++]);
544172827Smatteo			} else
545222623Srmacklem				complete_service(nconf, svcport_str);
546172827Smatteo		}
54774462Salfred	}
548172827Smatteo	endnetconfig(nc_handle);
549222623Srmacklem	free(sock_fd);
550222623Srmacklem	if (port_list != NULL) {
551222623Srmacklem		for (port_pos = 0; port_pos < port_len; port_pos++)
552222623Srmacklem			free(port_list[port_pos]);
553222623Srmacklem		free(port_list);
554222623Srmacklem	}
55574462Salfred
55674462Salfred	if (xcreated == 0) {
55774462Salfred		syslog(LOG_ERR, "could not create any services");
5581558Srgrimes		exit(1);
5591558Srgrimes	}
56075754Siedowse
56175754Siedowse	/* Expand svc_run() here so that we can call get_exportlist(). */
56275754Siedowse	for (;;) {
56375754Siedowse		if (got_sighup) {
56475754Siedowse			get_exportlist();
56575754Siedowse			got_sighup = 0;
56675754Siedowse		}
56775754Siedowse		readfds = svc_fdset;
56875754Siedowse		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
56975754Siedowse		case -1:
57075754Siedowse			if (errno == EINTR)
57175754Siedowse                                continue;
57275754Siedowse			syslog(LOG_ERR, "mountd died: select: %m");
57375754Siedowse			exit(1);
57475754Siedowse		case 0:
57575754Siedowse			continue;
57675754Siedowse		default:
57775754Siedowse			svc_getreqset(&readfds);
57875754Siedowse		}
57975754Siedowse	}
580172827Smatteo}
581172827Smatteo
582172827Smatteo/*
583172827Smatteo * This routine creates and binds sockets on the appropriate
584222623Srmacklem * addresses. It gets called one time for each transport.
585222623Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate
586222623Srmacklem * bind failed with EADDRINUSE.
587222623Srmacklem * Any file descriptors that have been created are stored in sock_fd and
588222623Srmacklem * the total count of them is maintained in sock_fdcnt.
589172827Smatteo */
590222623Srmacklemstatic int
591172827Smatteocreate_service(struct netconfig *nconf)
592172827Smatteo{
593172827Smatteo	struct addrinfo hints, *res = NULL;
594172827Smatteo	struct sockaddr_in *sin;
595172827Smatteo	struct sockaddr_in6 *sin6;
596172827Smatteo	struct __rpc_sockinfo si;
597172827Smatteo	int aicode;
598172827Smatteo	int fd;
599172827Smatteo	int nhostsbak;
600172827Smatteo	int one = 1;
601172827Smatteo	int r;
602172827Smatteo	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
603222623Srmacklem	int mallocd_res;
604172827Smatteo
605172827Smatteo	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
606172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS) &&
607172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
608222623Srmacklem		return (1);	/* not my type */
609172827Smatteo
610172827Smatteo	/*
611172827Smatteo	 * XXX - using RPC library internal functions.
612172827Smatteo	 */
613172827Smatteo	if (!__rpc_nconf2sockinfo(nconf, &si)) {
614172827Smatteo		syslog(LOG_ERR, "cannot get information for %s",
615172827Smatteo		    nconf->nc_netid);
616222623Srmacklem		return (1);
617172827Smatteo	}
618172827Smatteo
619172827Smatteo	/* Get mountd's address on this transport */
620172827Smatteo	memset(&hints, 0, sizeof hints);
621172827Smatteo	hints.ai_flags = AI_PASSIVE;
622172827Smatteo	hints.ai_family = si.si_af;
623172827Smatteo	hints.ai_socktype = si.si_socktype;
624172827Smatteo	hints.ai_protocol = si.si_proto;
625172827Smatteo
626172827Smatteo	/*
627172827Smatteo	 * Bind to specific IPs if asked to
628172827Smatteo	 */
629172827Smatteo	nhostsbak = nhosts;
630172827Smatteo	while (nhostsbak > 0) {
631172827Smatteo		--nhostsbak;
632222623Srmacklem		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
633222623Srmacklem		if (sock_fd == NULL)
634222623Srmacklem			out_of_mem();
635222623Srmacklem		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
636222623Srmacklem		mallocd_res = 0;
637222623Srmacklem
638172827Smatteo		/*
639172827Smatteo		 * XXX - using RPC library internal functions.
640172827Smatteo		 */
641172827Smatteo		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
642172827Smatteo			int non_fatal = 0;
643172827Smatteo	    		if (errno == EPROTONOSUPPORT &&
644172827Smatteo			    nconf->nc_semantics != NC_TPI_CLTS)
645172827Smatteo				non_fatal = 1;
646172827Smatteo
647172827Smatteo			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
648172827Smatteo			    "cannot create socket for %s", nconf->nc_netid);
649222623Srmacklem			if (non_fatal != 0)
650222623Srmacklem				continue;
651222623Srmacklem			exit(1);
652172827Smatteo		}
653172827Smatteo
654172827Smatteo		switch (hints.ai_family) {
655172827Smatteo		case AF_INET:
656172827Smatteo			if (inet_pton(AF_INET, hosts[nhostsbak],
657172827Smatteo			    host_addr) == 1) {
658222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
659172827Smatteo			} else {
660172827Smatteo				/*
661172827Smatteo				 * Skip if we have an AF_INET6 address.
662172827Smatteo				 */
663172827Smatteo				if (inet_pton(AF_INET6, hosts[nhostsbak],
664172827Smatteo				    host_addr) == 1) {
665172827Smatteo					close(fd);
666172827Smatteo					continue;
667172827Smatteo				}
668172827Smatteo			}
669172827Smatteo			break;
670172827Smatteo		case AF_INET6:
671172827Smatteo			if (inet_pton(AF_INET6, hosts[nhostsbak],
672172827Smatteo			    host_addr) == 1) {
673222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
674172827Smatteo			} else {
675172827Smatteo				/*
676172827Smatteo				 * Skip if we have an AF_INET address.
677172827Smatteo				 */
678172827Smatteo				if (inet_pton(AF_INET, hosts[nhostsbak],
679172827Smatteo				    host_addr) == 1) {
680172827Smatteo					close(fd);
681172827Smatteo					continue;
682172827Smatteo				}
683172827Smatteo			}
684172827Smatteo
685172827Smatteo			/*
686172827Smatteo			 * We're doing host-based access checks here, so don't
687172827Smatteo			 * allow v4-in-v6 to confuse things. The kernel will
688172827Smatteo			 * disable it by default on NFS sockets too.
689172827Smatteo			 */
690172827Smatteo			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
691172827Smatteo			    sizeof one) < 0) {
692172827Smatteo				syslog(LOG_ERR,
693172827Smatteo				    "can't disable v4-in-v6 on IPv6 socket");
694172827Smatteo				exit(1);
695172827Smatteo			}
696172827Smatteo			break;
697172827Smatteo		default:
698172827Smatteo			break;
699172827Smatteo		}
700172827Smatteo
701172827Smatteo		/*
702172827Smatteo		 * If no hosts were specified, just bind to INADDR_ANY
703172827Smatteo		 */
704172827Smatteo		if (strcmp("*", hosts[nhostsbak]) == 0) {
705172827Smatteo			if (svcport_str == NULL) {
706172827Smatteo				res = malloc(sizeof(struct addrinfo));
707172827Smatteo				if (res == NULL)
708172827Smatteo					out_of_mem();
709222623Srmacklem				mallocd_res = 1;
710172827Smatteo				res->ai_flags = hints.ai_flags;
711172827Smatteo				res->ai_family = hints.ai_family;
712172827Smatteo				res->ai_protocol = hints.ai_protocol;
713172827Smatteo				switch (res->ai_family) {
714172827Smatteo				case AF_INET:
715172827Smatteo					sin = malloc(sizeof(struct sockaddr_in));
716172827Smatteo					if (sin == NULL)
717172827Smatteo						out_of_mem();
718172827Smatteo					sin->sin_family = AF_INET;
719172827Smatteo					sin->sin_port = htons(0);
720172827Smatteo					sin->sin_addr.s_addr = htonl(INADDR_ANY);
721172827Smatteo					res->ai_addr = (struct sockaddr*) sin;
722172827Smatteo					res->ai_addrlen = (socklen_t)
723222623Srmacklem					    sizeof(struct sockaddr_in);
724172827Smatteo					break;
725172827Smatteo				case AF_INET6:
726172827Smatteo					sin6 = malloc(sizeof(struct sockaddr_in6));
727173056Ssimon					if (sin6 == NULL)
728172827Smatteo						out_of_mem();
729172827Smatteo					sin6->sin6_family = AF_INET6;
730172827Smatteo					sin6->sin6_port = htons(0);
731172827Smatteo					sin6->sin6_addr = in6addr_any;
732172827Smatteo					res->ai_addr = (struct sockaddr*) sin6;
733172827Smatteo					res->ai_addrlen = (socklen_t)
734222623Srmacklem					    sizeof(struct sockaddr_in6);
735222623Srmacklem					break;
736172827Smatteo				default:
737222623Srmacklem					syslog(LOG_ERR, "bad addr fam %d",
738222623Srmacklem					    res->ai_family);
739222623Srmacklem					exit(1);
740172827Smatteo				}
741172827Smatteo			} else {
742172827Smatteo				if ((aicode = getaddrinfo(NULL, svcport_str,
743172827Smatteo				    &hints, &res)) != 0) {
744172827Smatteo					syslog(LOG_ERR,
745172827Smatteo					    "cannot get local address for %s: %s",
746172827Smatteo					    nconf->nc_netid,
747172827Smatteo					    gai_strerror(aicode));
748222623Srmacklem					close(fd);
749172827Smatteo					continue;
750172827Smatteo				}
751172827Smatteo			}
752172827Smatteo		} else {
753172827Smatteo			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
754172827Smatteo			    &hints, &res)) != 0) {
755172827Smatteo				syslog(LOG_ERR,
756172827Smatteo				    "cannot get local address for %s: %s",
757172827Smatteo				    nconf->nc_netid, gai_strerror(aicode));
758222623Srmacklem				close(fd);
759172827Smatteo				continue;
760172827Smatteo			}
761172827Smatteo		}
762172827Smatteo
763222623Srmacklem		/* Store the fd. */
764222623Srmacklem		sock_fd[sock_fdcnt - 1] = fd;
765222623Srmacklem
766222623Srmacklem		/* Now, attempt the bind. */
767172827Smatteo		r = bindresvport_sa(fd, res->ai_addr);
768172827Smatteo		if (r != 0) {
769222623Srmacklem			if (errno == EADDRINUSE && mallocd_svcport != 0) {
770222623Srmacklem				if (mallocd_res != 0) {
771222623Srmacklem					free(res->ai_addr);
772222623Srmacklem					free(res);
773222623Srmacklem				} else
774222623Srmacklem					freeaddrinfo(res);
775222623Srmacklem				return (-1);
776222623Srmacklem			}
777172827Smatteo			syslog(LOG_ERR, "bindresvport_sa: %m");
778172827Smatteo			exit(1);
779172827Smatteo		}
780172827Smatteo
781222623Srmacklem		if (svcport_str == NULL) {
782222623Srmacklem			svcport_str = malloc(NI_MAXSERV * sizeof(char));
783222623Srmacklem			if (svcport_str == NULL)
784222623Srmacklem				out_of_mem();
785222623Srmacklem			mallocd_svcport = 1;
786222623Srmacklem
787222623Srmacklem			if (getnameinfo(res->ai_addr,
788222623Srmacklem			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
789222623Srmacklem			    svcport_str, NI_MAXSERV * sizeof(char),
790222623Srmacklem			    NI_NUMERICHOST | NI_NUMERICSERV))
791222623Srmacklem				errx(1, "Cannot get port number");
792222623Srmacklem		}
793222623Srmacklem		if (mallocd_res != 0) {
794222623Srmacklem			free(res->ai_addr);
795222623Srmacklem			free(res);
796222623Srmacklem		} else
797222623Srmacklem			freeaddrinfo(res);
798222623Srmacklem		res = NULL;
799222623Srmacklem	}
800222623Srmacklem	return (0);
801222623Srmacklem}
802222623Srmacklem
803222623Srmacklem/*
804222623Srmacklem * Called after all the create_service() calls have succeeded, to complete
805222623Srmacklem * the setup and registration.
806222623Srmacklem */
807222623Srmacklemstatic void
808222623Srmacklemcomplete_service(struct netconfig *nconf, char *port_str)
809222623Srmacklem{
810222623Srmacklem	struct addrinfo hints, *res = NULL;
811222623Srmacklem	struct __rpc_sockinfo si;
812222623Srmacklem	struct netbuf servaddr;
813222623Srmacklem	SVCXPRT	*transp = NULL;
814222623Srmacklem	int aicode, fd, nhostsbak;
815222623Srmacklem	int registered = 0;
816222623Srmacklem
817222623Srmacklem	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
818222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS) &&
819222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
820222623Srmacklem		return;	/* not my type */
821222623Srmacklem
822222623Srmacklem	/*
823222623Srmacklem	 * XXX - using RPC library internal functions.
824222623Srmacklem	 */
825222623Srmacklem	if (!__rpc_nconf2sockinfo(nconf, &si)) {
826222623Srmacklem		syslog(LOG_ERR, "cannot get information for %s",
827222623Srmacklem		    nconf->nc_netid);
828222623Srmacklem		return;
829222623Srmacklem	}
830222623Srmacklem
831222623Srmacklem	nhostsbak = nhosts;
832222623Srmacklem	while (nhostsbak > 0) {
833222623Srmacklem		--nhostsbak;
834222623Srmacklem		if (sock_fdpos >= sock_fdcnt) {
835222623Srmacklem			/* Should never happen. */
836222623Srmacklem			syslog(LOG_ERR, "Ran out of socket fd's");
837222623Srmacklem			return;
838222623Srmacklem		}
839222623Srmacklem		fd = sock_fd[sock_fdpos++];
840222623Srmacklem		if (fd < 0)
841222623Srmacklem			continue;
842222623Srmacklem
843172827Smatteo		if (nconf->nc_semantics != NC_TPI_CLTS)
844172827Smatteo			listen(fd, SOMAXCONN);
845172827Smatteo
846172827Smatteo		if (nconf->nc_semantics == NC_TPI_CLTS )
847172827Smatteo			transp = svc_dg_create(fd, 0, 0);
848172827Smatteo		else
849172827Smatteo			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
850172827Smatteo			    RPC_MAXDATASIZE);
851172827Smatteo
852172827Smatteo		if (transp != (SVCXPRT *) NULL) {
853194880Sdfr			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
854172827Smatteo			    NULL))
855172827Smatteo				syslog(LOG_ERR,
856194880Sdfr				    "can't register %s MOUNTVERS service",
857172827Smatteo				    nconf->nc_netid);
858172827Smatteo			if (!force_v2) {
859194880Sdfr				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
860172827Smatteo				    mntsrv, NULL))
861172827Smatteo					syslog(LOG_ERR,
862194880Sdfr					    "can't register %s MOUNTVERS3 service",
863172827Smatteo					    nconf->nc_netid);
864172827Smatteo			}
865172827Smatteo		} else
866172827Smatteo			syslog(LOG_WARNING, "can't create %s services",
867172827Smatteo			    nconf->nc_netid);
868172827Smatteo
869172827Smatteo		if (registered == 0) {
870172827Smatteo			registered = 1;
871172827Smatteo			memset(&hints, 0, sizeof hints);
872172827Smatteo			hints.ai_flags = AI_PASSIVE;
873172827Smatteo			hints.ai_family = si.si_af;
874172827Smatteo			hints.ai_socktype = si.si_socktype;
875172827Smatteo			hints.ai_protocol = si.si_proto;
876172827Smatteo
877222623Srmacklem			if ((aicode = getaddrinfo(NULL, port_str, &hints,
878172827Smatteo			    &res)) != 0) {
879172827Smatteo				syslog(LOG_ERR, "cannot get local address: %s",
880172827Smatteo				    gai_strerror(aicode));
881172827Smatteo				exit(1);
882172827Smatteo			}
883172827Smatteo
884172827Smatteo			servaddr.buf = malloc(res->ai_addrlen);
885172827Smatteo			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
886172827Smatteo			servaddr.len = res->ai_addrlen;
887172827Smatteo
888194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
889194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
890172827Smatteo
891172827Smatteo			xcreated++;
892172827Smatteo			freeaddrinfo(res);
893172827Smatteo		}
894172827Smatteo	} /* end while */
8951558Srgrimes}
8961558Srgrimes
897222623Srmacklem/*
898222623Srmacklem * Clear out sockets after a failure to bind one of them, so that the
899222623Srmacklem * cycle of socket creation/binding can start anew.
900222623Srmacklem */
90137663Scharnierstatic void
902222623Srmacklemclearout_service(void)
903222623Srmacklem{
904222623Srmacklem	int i;
905222623Srmacklem
906222623Srmacklem	for (i = 0; i < sock_fdcnt; i++) {
907222623Srmacklem		if (sock_fd[i] >= 0) {
908222623Srmacklem			shutdown(sock_fd[i], SHUT_RDWR);
909222623Srmacklem			close(sock_fd[i]);
910222623Srmacklem		}
911222623Srmacklem	}
912222623Srmacklem}
913222623Srmacklem
914222623Srmacklemstatic void
915216587Scharnierusage(void)
91637663Scharnier{
91737663Scharnier	fprintf(stderr,
918192993Srmacklem		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
919172827Smatteo		"[-h <bindip>] [export_file ...]\n");
92037663Scharnier	exit(1);
92137663Scharnier}
92237663Scharnier
9231558Srgrimes/*
9241558Srgrimes * The mount rpc service
9251558Srgrimes */
9261558Srgrimesvoid
927216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp)
9281558Srgrimes{
9291558Srgrimes	struct exportlist *ep;
9301558Srgrimes	struct dirlist *dp;
9319336Sdfr	struct fhreturn fhr;
9321558Srgrimes	struct stat stb;
9331558Srgrimes	struct statfs fsb;
93474462Salfred	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
93574462Salfred	int lookup_failed = 1;
93674462Salfred	struct sockaddr *saddr;
9379336Sdfr	u_short sport;
938194880Sdfr	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
93928911Sguido	int bad = 0, defset, hostset;
9409336Sdfr	sigset_t sighup_mask;
9411558Srgrimes
9429336Sdfr	sigemptyset(&sighup_mask);
9439336Sdfr	sigaddset(&sighup_mask, SIGHUP);
94474462Salfred	saddr = svc_getrpccaller(transp)->buf;
94574462Salfred	switch (saddr->sa_family) {
94674462Salfred	case AF_INET6:
94775635Siedowse		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
94874462Salfred		break;
94974462Salfred	case AF_INET:
95075635Siedowse		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
95174462Salfred		break;
95274462Salfred	default:
95374462Salfred		syslog(LOG_ERR, "request from unknown address family");
95474462Salfred		return;
95574462Salfred	}
95674462Salfred	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
95774462Salfred	    NULL, 0, 0);
95874462Salfred	getnameinfo(saddr, saddr->sa_len, numerichost,
95974462Salfred	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
9601558Srgrimes	switch (rqstp->rq_proc) {
9611558Srgrimes	case NULLPROC:
962121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
96337663Scharnier			syslog(LOG_ERR, "can't send reply");
9641558Srgrimes		return;
965194880Sdfr	case MOUNTPROC_MNT:
9669336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
96731656Sguido			syslog(LOG_NOTICE,
96831656Sguido			    "mount request from %s from unprivileged port",
96974462Salfred			    numerichost);
9701558Srgrimes			svcerr_weakauth(transp);
9711558Srgrimes			return;
9721558Srgrimes		}
973121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
97431656Sguido			syslog(LOG_NOTICE, "undecodable mount request from %s",
97574462Salfred			    numerichost);
9761558Srgrimes			svcerr_decode(transp);
9771558Srgrimes			return;
9781558Srgrimes		}
9791558Srgrimes
9801558Srgrimes		/*
9811558Srgrimes		 * Get the real pathname and make sure it is a directory
9829336Sdfr		 * or a regular file if the -r option was specified
9839336Sdfr		 * and it exists.
9841558Srgrimes		 */
98551968Salfred		if (realpath(rpcpath, dirpath) == NULL ||
9861558Srgrimes		    stat(dirpath, &stb) < 0 ||
9879336Sdfr		    (!S_ISDIR(stb.st_mode) &&
98874462Salfred		    (dir_only || !S_ISREG(stb.st_mode))) ||
9891558Srgrimes		    statfs(dirpath, &fsb) < 0) {
9901558Srgrimes			chdir("/");	/* Just in case realpath doesn't */
99131656Sguido			syslog(LOG_NOTICE,
99237663Scharnier			    "mount request from %s for non existent path %s",
99374462Salfred			    numerichost, dirpath);
9941558Srgrimes			if (debug)
99537663Scharnier				warnx("stat failed on %s", dirpath);
99628911Sguido			bad = ENOENT;	/* We will send error reply later */
9971558Srgrimes		}
9981558Srgrimes
9991558Srgrimes		/* Check in the exports list */
10009336Sdfr		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
10011558Srgrimes		ep = ex_search(&fsb.f_fsid);
10029336Sdfr		hostset = defset = 0;
10039336Sdfr		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
10041558Srgrimes		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
100574462Salfred		      chk_host(dp, saddr, &defset, &hostset)) ||
100674462Salfred		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
100774462Salfred		     scan_tree(ep->ex_dirl, saddr) == 0))) {
100828911Sguido			if (bad) {
1009121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
101028911Sguido				    (caddr_t)&bad))
101137663Scharnier					syslog(LOG_ERR, "can't send reply");
101228911Sguido				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
101328911Sguido				return;
101428911Sguido			}
10159336Sdfr			if (hostset & DP_HOSTSET)
10169336Sdfr				fhr.fhr_flag = hostset;
10179336Sdfr			else
10189336Sdfr				fhr.fhr_flag = defset;
10199336Sdfr			fhr.fhr_vers = rqstp->rq_vers;
10201558Srgrimes			/* Get the file handle */
102123681Speter			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
10229336Sdfr			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
10231558Srgrimes				bad = errno;
102437663Scharnier				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1025121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
10261558Srgrimes				    (caddr_t)&bad))
102737663Scharnier					syslog(LOG_ERR, "can't send reply");
10289336Sdfr				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
10291558Srgrimes				return;
10301558Srgrimes			}
1031184588Sdfr			fhr.fhr_numsecflavors = ep->ex_numsecflavors;
1032184588Sdfr			fhr.fhr_secflavors = ep->ex_secflavors;
1033121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1034121556Speter			    (caddr_t)&fhr))
103537663Scharnier				syslog(LOG_ERR, "can't send reply");
103674462Salfred			if (!lookup_failed)
103774462Salfred				add_mlist(host, dirpath);
10381558Srgrimes			else
103974462Salfred				add_mlist(numerichost, dirpath);
10401558Srgrimes			if (debug)
104137663Scharnier				warnx("mount successful");
1042121767Speter			if (dolog)
104331656Sguido				syslog(LOG_NOTICE,
104431656Sguido				    "mount request succeeded from %s for %s",
104574462Salfred				    numerichost, dirpath);
104631656Sguido		} else {
10471558Srgrimes			bad = EACCES;
104831656Sguido			syslog(LOG_NOTICE,
104931656Sguido			    "mount request denied from %s for %s",
105074462Salfred			    numerichost, dirpath);
105131656Sguido		}
105228911Sguido
1053121556Speter		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1054121556Speter		    (caddr_t)&bad))
105537663Scharnier			syslog(LOG_ERR, "can't send reply");
10569336Sdfr		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
10571558Srgrimes		return;
1058194880Sdfr	case MOUNTPROC_DUMP:
1059121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
106037663Scharnier			syslog(LOG_ERR, "can't send reply");
1061121767Speter		else if (dolog)
106231656Sguido			syslog(LOG_NOTICE,
106331656Sguido			    "dump request succeeded from %s",
106474462Salfred			    numerichost);
10651558Srgrimes		return;
1066194880Sdfr	case MOUNTPROC_UMNT:
10679336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
106831656Sguido			syslog(LOG_NOTICE,
106931656Sguido			    "umount request from %s from unprivileged port",
107074462Salfred			    numerichost);
10711558Srgrimes			svcerr_weakauth(transp);
10721558Srgrimes			return;
10731558Srgrimes		}
1074121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
107531656Sguido			syslog(LOG_NOTICE, "undecodable umount request from %s",
107674462Salfred			    numerichost);
10771558Srgrimes			svcerr_decode(transp);
10781558Srgrimes			return;
10791558Srgrimes		}
108051968Salfred		if (realpath(rpcpath, dirpath) == NULL) {
108151968Salfred			syslog(LOG_NOTICE, "umount request from %s "
108251968Salfred			    "for non existent path %s",
108374462Salfred			    numerichost, dirpath);
108451968Salfred		}
1085121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
108637663Scharnier			syslog(LOG_ERR, "can't send reply");
108774462Salfred		if (!lookup_failed)
108875635Siedowse			del_mlist(host, dirpath);
108975635Siedowse		del_mlist(numerichost, dirpath);
1090121767Speter		if (dolog)
109131656Sguido			syslog(LOG_NOTICE,
109231656Sguido			    "umount request succeeded from %s for %s",
109374462Salfred			    numerichost, dirpath);
10941558Srgrimes		return;
1095194880Sdfr	case MOUNTPROC_UMNTALL:
10969336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
109731656Sguido			syslog(LOG_NOTICE,
109831656Sguido			    "umountall request from %s from unprivileged port",
109974462Salfred			    numerichost);
11001558Srgrimes			svcerr_weakauth(transp);
11011558Srgrimes			return;
11021558Srgrimes		}
1103121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
110437663Scharnier			syslog(LOG_ERR, "can't send reply");
110574462Salfred		if (!lookup_failed)
110675635Siedowse			del_mlist(host, NULL);
110775635Siedowse		del_mlist(numerichost, NULL);
1108121767Speter		if (dolog)
110931656Sguido			syslog(LOG_NOTICE,
111031656Sguido			    "umountall request succeeded from %s",
111174462Salfred			    numerichost);
11121558Srgrimes		return;
1113194880Sdfr	case MOUNTPROC_EXPORT:
1114121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1115121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1116121556Speter			    (caddr_t)NULL))
1117100117Salfred				syslog(LOG_ERR, "can't send reply");
1118121767Speter		if (dolog)
111931656Sguido			syslog(LOG_NOTICE,
112031656Sguido			    "export request succeeded from %s",
112174462Salfred			    numerichost);
11221558Srgrimes		return;
11231558Srgrimes	default:
11241558Srgrimes		svcerr_noproc(transp);
11251558Srgrimes		return;
11261558Srgrimes	}
11271558Srgrimes}
11281558Srgrimes
11291558Srgrimes/*
11301558Srgrimes * Xdr conversion for a dirpath string
11311558Srgrimes */
11321558Srgrimesint
1133216587Scharnierxdr_dir(XDR *xdrsp, char *dirp)
11341558Srgrimes{
1135194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
11361558Srgrimes}
11371558Srgrimes
11381558Srgrimes/*
11399336Sdfr * Xdr routine to generate file handle reply
11401558Srgrimes */
11411558Srgrimesint
1142216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp)
11431558Srgrimes{
114492806Sobrien	struct fhreturn *fhrp = (struct fhreturn *)cp;
11459336Sdfr	u_long ok = 0, len, auth;
1146184588Sdfr	int i;
11471558Srgrimes
11481558Srgrimes	if (!xdr_long(xdrsp, &ok))
11491558Srgrimes		return (0);
11509336Sdfr	switch (fhrp->fhr_vers) {
11519336Sdfr	case 1:
11529336Sdfr		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
11539336Sdfr	case 3:
11549336Sdfr		len = NFSX_V3FH;
11559336Sdfr		if (!xdr_long(xdrsp, &len))
11569336Sdfr			return (0);
11579336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
11589336Sdfr			return (0);
1159184588Sdfr		if (fhrp->fhr_numsecflavors) {
1160184588Sdfr			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1161184588Sdfr				return (0);
1162184588Sdfr			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1163184588Sdfr				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1164184588Sdfr					return (0);
1165184588Sdfr			return (1);
1166184588Sdfr		} else {
1167184588Sdfr			auth = AUTH_SYS;
1168184588Sdfr			len = 1;
1169184588Sdfr			if (!xdr_long(xdrsp, &len))
1170184588Sdfr				return (0);
1171184588Sdfr			return (xdr_long(xdrsp, &auth));
1172184588Sdfr		}
11739336Sdfr	};
11749336Sdfr	return (0);
11751558Srgrimes}
11761558Srgrimes
11771558Srgrimesint
1178216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused)
11791558Srgrimes{
11801558Srgrimes	struct mountlist *mlp;
11811558Srgrimes	int true = 1;
11821558Srgrimes	int false = 0;
11831558Srgrimes	char *strp;
11841558Srgrimes
11851558Srgrimes	mlp = mlhead;
11861558Srgrimes	while (mlp) {
11871558Srgrimes		if (!xdr_bool(xdrsp, &true))
11881558Srgrimes			return (0);
11891558Srgrimes		strp = &mlp->ml_host[0];
1190194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
11911558Srgrimes			return (0);
11921558Srgrimes		strp = &mlp->ml_dirp[0];
1193194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
11941558Srgrimes			return (0);
11951558Srgrimes		mlp = mlp->ml_next;
11961558Srgrimes	}
11971558Srgrimes	if (!xdr_bool(xdrsp, &false))
11981558Srgrimes		return (0);
11991558Srgrimes	return (1);
12001558Srgrimes}
12011558Srgrimes
12021558Srgrimes/*
12031558Srgrimes * Xdr conversion for export list
12041558Srgrimes */
12051558Srgrimesint
1206216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
12071558Srgrimes{
12081558Srgrimes	struct exportlist *ep;
12091558Srgrimes	int false = 0;
12109336Sdfr	int putdef;
12119336Sdfr	sigset_t sighup_mask;
12121558Srgrimes
12139336Sdfr	sigemptyset(&sighup_mask);
12149336Sdfr	sigaddset(&sighup_mask, SIGHUP);
12159336Sdfr	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
12161558Srgrimes	ep = exphead;
12171558Srgrimes	while (ep) {
12181558Srgrimes		putdef = 0;
1219100117Salfred		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1220100117Salfred			       &putdef, brief))
12211558Srgrimes			goto errout;
12221558Srgrimes		if (ep->ex_defdir && putdef == 0 &&
12231558Srgrimes			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1224100117Salfred			&putdef, brief))
12251558Srgrimes			goto errout;
12261558Srgrimes		ep = ep->ex_next;
12271558Srgrimes	}
12289336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
12291558Srgrimes	if (!xdr_bool(xdrsp, &false))
12301558Srgrimes		return (0);
12311558Srgrimes	return (1);
12321558Srgrimeserrout:
12339336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
12341558Srgrimes	return (0);
12351558Srgrimes}
12361558Srgrimes
12371558Srgrimes/*
12381558Srgrimes * Called from xdr_explist() to traverse the tree and export the
12391558Srgrimes * directory paths.
12401558Srgrimes */
12411558Srgrimesint
1242216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1243216587Scharnier	int brief)
12441558Srgrimes{
12451558Srgrimes	struct grouplist *grp;
12461558Srgrimes	struct hostlist *hp;
12471558Srgrimes	int true = 1;
12481558Srgrimes	int false = 0;
12491558Srgrimes	int gotalldir = 0;
12501558Srgrimes	char *strp;
12511558Srgrimes
12521558Srgrimes	if (dp) {
1253100117Salfred		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
12541558Srgrimes			return (1);
12551558Srgrimes		if (!xdr_bool(xdrsp, &true))
12561558Srgrimes			return (1);
12571558Srgrimes		strp = dp->dp_dirp;
1258194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
12591558Srgrimes			return (1);
12601558Srgrimes		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
12611558Srgrimes			gotalldir = 1;
12621558Srgrimes			*putdefp = 1;
12631558Srgrimes		}
1264100117Salfred		if (brief) {
1265100117Salfred			if (!xdr_bool(xdrsp, &true))
1266100117Salfred				return (1);
1267100117Salfred			strp = "(...)";
1268194880Sdfr			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1269100117Salfred				return (1);
1270100117Salfred		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
12711558Srgrimes		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
12721558Srgrimes			hp = dp->dp_hosts;
12731558Srgrimes			while (hp) {
12741558Srgrimes				grp = hp->ht_grp;
12751558Srgrimes				if (grp->gr_type == GT_HOST) {
12761558Srgrimes					if (!xdr_bool(xdrsp, &true))
12771558Srgrimes						return (1);
127874462Salfred					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
12798871Srgrimes					if (!xdr_string(xdrsp, &strp,
1280194880Sdfr					    MNTNAMLEN))
12811558Srgrimes						return (1);
12821558Srgrimes				} else if (grp->gr_type == GT_NET) {
12831558Srgrimes					if (!xdr_bool(xdrsp, &true))
12841558Srgrimes						return (1);
12851558Srgrimes					strp = grp->gr_ptr.gt_net.nt_name;
12868871Srgrimes					if (!xdr_string(xdrsp, &strp,
1287194880Sdfr					    MNTNAMLEN))
12881558Srgrimes						return (1);
12891558Srgrimes				}
12901558Srgrimes				hp = hp->ht_next;
12911558Srgrimes				if (gotalldir && hp == (struct hostlist *)NULL) {
12921558Srgrimes					hp = adp->dp_hosts;
12931558Srgrimes					gotalldir = 0;
12941558Srgrimes				}
12951558Srgrimes			}
12961558Srgrimes		}
12971558Srgrimes		if (!xdr_bool(xdrsp, &false))
12981558Srgrimes			return (1);
1299100117Salfred		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
13001558Srgrimes			return (1);
13011558Srgrimes	}
13021558Srgrimes	return (0);
13031558Srgrimes}
13041558Srgrimes
1305100117Salfredint
1306216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp)
1307100117Salfred{
1308100117Salfred
1309100117Salfred	return xdr_explist_common(xdrsp, cp, 0);
1310100117Salfred}
1311100117Salfred
1312100117Salfredint
1313216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp)
1314100117Salfred{
1315100117Salfred
1316100117Salfred	return xdr_explist_common(xdrsp, cp, 1);
1317100117Salfred}
1318100117Salfred
131996622Siedowsechar *line;
132096622Siedowseint linesize;
13211558SrgrimesFILE *exp_file;
13221558Srgrimes
13231558Srgrimes/*
1324166440Spjd * Get the export list from one, currently open file
13251558Srgrimes */
1326166440Spjdstatic void
1327216587Scharnierget_exportlist_one(void)
13281558Srgrimes{
13291558Srgrimes	struct exportlist *ep, *ep2;
13301558Srgrimes	struct grouplist *grp, *tgrp;
13311558Srgrimes	struct exportlist **epp;
13321558Srgrimes	struct dirlist *dirhead;
1333166440Spjd	struct statfs fsb;
133472650Sgreen	struct xucred anon;
13351558Srgrimes	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1336166440Spjd	int len, has_host, exflags, got_nondir, dirplen, netgrp;
13371558Srgrimes
1338192934Srmacklem	v4root_phase = 0;
13391558Srgrimes	dirhead = (struct dirlist *)NULL;
13401558Srgrimes	while (get_line()) {
13411558Srgrimes		if (debug)
134237663Scharnier			warnx("got line %s", line);
13431558Srgrimes		cp = line;
13441558Srgrimes		nextfield(&cp, &endcp);
13451558Srgrimes		if (*cp == '#')
13461558Srgrimes			goto nextline;
13471558Srgrimes
13481558Srgrimes		/*
13491558Srgrimes		 * Set defaults.
13501558Srgrimes		 */
13511558Srgrimes		has_host = FALSE;
13521558Srgrimes		anon = def_anon;
13531558Srgrimes		exflags = MNT_EXPORTED;
13541558Srgrimes		got_nondir = 0;
13551558Srgrimes		opt_flags = 0;
13561558Srgrimes		ep = (struct exportlist *)NULL;
1357192934Srmacklem		dirp = NULL;
13581558Srgrimes
13591558Srgrimes		/*
1360192934Srmacklem		 * Handle the V4 root dir.
1361192934Srmacklem		 */
1362192934Srmacklem		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1363192934Srmacklem			/*
1364192934Srmacklem			 * V4: just indicates that it is the v4 root point,
1365192934Srmacklem			 * so skip over that and set v4root_phase.
1366192934Srmacklem			 */
1367192934Srmacklem			if (v4root_phase > 0) {
1368192934Srmacklem				syslog(LOG_ERR, "V4:duplicate line, ignored");
1369192934Srmacklem				goto nextline;
1370192934Srmacklem			}
1371192934Srmacklem			v4root_phase = 1;
1372192934Srmacklem			cp += 3;
1373192934Srmacklem			nextfield(&cp, &endcp);
1374192934Srmacklem		}
1375192934Srmacklem
1376192934Srmacklem		/*
13771558Srgrimes		 * Create new exports list entry
13781558Srgrimes		 */
13791558Srgrimes		len = endcp-cp;
13801558Srgrimes		tgrp = grp = get_grp();
13811558Srgrimes		while (len > 0) {
1382194880Sdfr			if (len > MNTNAMLEN) {
13831558Srgrimes			    getexp_err(ep, tgrp);
13841558Srgrimes			    goto nextline;
13851558Srgrimes			}
13861558Srgrimes			if (*cp == '-') {
13871558Srgrimes			    if (ep == (struct exportlist *)NULL) {
13881558Srgrimes				getexp_err(ep, tgrp);
13891558Srgrimes				goto nextline;
13901558Srgrimes			    }
13911558Srgrimes			    if (debug)
139237663Scharnier				warnx("doing opt %s", cp);
13931558Srgrimes			    got_nondir = 1;
13941558Srgrimes			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
13951558Srgrimes				&exflags, &anon)) {
13961558Srgrimes				getexp_err(ep, tgrp);
13971558Srgrimes				goto nextline;
13981558Srgrimes			    }
13991558Srgrimes			} else if (*cp == '/') {
14001558Srgrimes			    savedc = *endcp;
14011558Srgrimes			    *endcp = '\0';
1402192934Srmacklem			    if (v4root_phase > 1) {
1403192934Srmacklem				    if (dirp != NULL) {
1404192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
1405192934Srmacklem					getexp_err(ep, tgrp);
1406192934Srmacklem					goto nextline;
1407192934Srmacklem				    }
1408192934Srmacklem			    }
14091558Srgrimes			    if (check_dirpath(cp) &&
14101558Srgrimes				statfs(cp, &fsb) >= 0) {
14111558Srgrimes				if (got_nondir) {
141237663Scharnier				    syslog(LOG_ERR, "dirs must be first");
14131558Srgrimes				    getexp_err(ep, tgrp);
14141558Srgrimes				    goto nextline;
14151558Srgrimes				}
1416192934Srmacklem				if (v4root_phase == 1) {
1417192934Srmacklem				    if (dirp != NULL) {
1418192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
14191558Srgrimes					getexp_err(ep, tgrp);
14201558Srgrimes					goto nextline;
14211558Srgrimes				    }
1422192934Srmacklem				    if (strlen(v4root_dirpath) == 0) {
1423192934Srmacklem					strlcpy(v4root_dirpath, cp,
1424192934Srmacklem					    sizeof (v4root_dirpath));
1425192934Srmacklem				    } else if (strcmp(v4root_dirpath, cp)
1426192934Srmacklem					!= 0) {
1427192934Srmacklem					syslog(LOG_ERR,
1428192934Srmacklem					    "different V4 dirpath %s", cp);
1429192934Srmacklem					getexp_err(ep, tgrp);
1430192934Srmacklem					goto nextline;
1431192934Srmacklem				    }
1432192934Srmacklem				    dirp = cp;
1433192934Srmacklem				    v4root_phase = 2;
1434192934Srmacklem				    got_nondir = 1;
1435192934Srmacklem				    ep = get_exp();
14361558Srgrimes				} else {
1437192934Srmacklem				    if (ep) {
1438192934Srmacklem					if (ep->ex_fs.val[0] !=
1439192934Srmacklem					    fsb.f_fsid.val[0] ||
1440192934Srmacklem					    ep->ex_fs.val[1] !=
1441192934Srmacklem					    fsb.f_fsid.val[1]) {
1442192934Srmacklem						getexp_err(ep, tgrp);
1443192934Srmacklem						goto nextline;
1444192934Srmacklem					}
1445192934Srmacklem				    } else {
1446192934Srmacklem					/*
1447192934Srmacklem					 * See if this directory is already
1448192934Srmacklem					 * in the list.
1449192934Srmacklem					 */
1450192934Srmacklem					ep = ex_search(&fsb.f_fsid);
1451192934Srmacklem					if (ep == (struct exportlist *)NULL) {
1452192934Srmacklem					    ep = get_exp();
1453192934Srmacklem					    ep->ex_fs = fsb.f_fsid;
1454192934Srmacklem					    ep->ex_fsdir = (char *)malloc
1455192934Srmacklem					        (strlen(fsb.f_mntonname) + 1);
1456192934Srmacklem					    if (ep->ex_fsdir)
1457192934Srmacklem						strcpy(ep->ex_fsdir,
1458192934Srmacklem						    fsb.f_mntonname);
1459192934Srmacklem					    else
1460192934Srmacklem						out_of_mem();
1461192934Srmacklem					    if (debug)
1462192934Srmacklem						warnx(
1463192934Srmacklem						  "making new ep fs=0x%x,0x%x",
1464192934Srmacklem						  fsb.f_fsid.val[0],
1465192934Srmacklem						  fsb.f_fsid.val[1]);
1466192934Srmacklem					} else if (debug)
1467192934Srmacklem					    warnx("found ep fs=0x%x,0x%x",
1468192934Srmacklem						fsb.f_fsid.val[0],
1469192934Srmacklem						fsb.f_fsid.val[1]);
1470192934Srmacklem				    }
1471192934Srmacklem
14721558Srgrimes				    /*
1473192934Srmacklem				     * Add dirpath to export mount point.
14741558Srgrimes				     */
1475192934Srmacklem				    dirp = add_expdir(&dirhead, cp, len);
1476192934Srmacklem				    dirplen = len;
14771558Srgrimes				}
14781558Srgrimes			    } else {
14791558Srgrimes				getexp_err(ep, tgrp);
14801558Srgrimes				goto nextline;
14811558Srgrimes			    }
14821558Srgrimes			    *endcp = savedc;
14831558Srgrimes			} else {
14841558Srgrimes			    savedc = *endcp;
14851558Srgrimes			    *endcp = '\0';
14861558Srgrimes			    got_nondir = 1;
14871558Srgrimes			    if (ep == (struct exportlist *)NULL) {
14881558Srgrimes				getexp_err(ep, tgrp);
14891558Srgrimes				goto nextline;
14901558Srgrimes			    }
14911558Srgrimes
14921558Srgrimes			    /*
14931558Srgrimes			     * Get the host or netgroup.
14941558Srgrimes			     */
14951558Srgrimes			    setnetgrent(cp);
14961558Srgrimes			    netgrp = getnetgrent(&hst, &usr, &dom);
14971558Srgrimes			    do {
14981558Srgrimes				if (has_host) {
14991558Srgrimes				    grp->gr_next = get_grp();
15001558Srgrimes				    grp = grp->gr_next;
15011558Srgrimes				}
15021558Srgrimes				if (netgrp) {
150337003Sjoerg				    if (hst == 0) {
150437663Scharnier					syslog(LOG_ERR,
150537663Scharnier				"null hostname in netgroup %s, skipping", cp);
150637004Sjoerg					grp->gr_type = GT_IGNORE;
150737003Sjoerg				    } else if (get_host(hst, grp, tgrp)) {
150837663Scharnier					syslog(LOG_ERR,
150937663Scharnier			"bad host %s in netgroup %s, skipping", hst, cp);
151029317Sjlemon					grp->gr_type = GT_IGNORE;
15111558Srgrimes				    }
15127401Swpaul				} else if (get_host(cp, grp, tgrp)) {
151337663Scharnier				    syslog(LOG_ERR, "bad host %s, skipping", cp);
151429317Sjlemon				    grp->gr_type = GT_IGNORE;
15151558Srgrimes				}
15161558Srgrimes				has_host = TRUE;
15171558Srgrimes			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
15181558Srgrimes			    endnetgrent();
15191558Srgrimes			    *endcp = savedc;
15201558Srgrimes			}
15211558Srgrimes			cp = endcp;
15221558Srgrimes			nextfield(&cp, &endcp);
15231558Srgrimes			len = endcp - cp;
15241558Srgrimes		}
15251558Srgrimes		if (check_options(dirhead)) {
15261558Srgrimes			getexp_err(ep, tgrp);
15271558Srgrimes			goto nextline;
15281558Srgrimes		}
15291558Srgrimes		if (!has_host) {
153075641Siedowse			grp->gr_type = GT_DEFAULT;
15311558Srgrimes			if (debug)
153237663Scharnier				warnx("adding a default entry");
15331558Srgrimes
15341558Srgrimes		/*
15351558Srgrimes		 * Don't allow a network export coincide with a list of
15361558Srgrimes		 * host(s) on the same line.
15371558Srgrimes		 */
15381558Srgrimes		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
153975801Siedowse			syslog(LOG_ERR, "network/host conflict");
15401558Srgrimes			getexp_err(ep, tgrp);
15411558Srgrimes			goto nextline;
154229317Sjlemon
154374462Salfred		/*
154474462Salfred		 * If an export list was specified on this line, make sure
154529317Sjlemon		 * that we have at least one valid entry, otherwise skip it.
154629317Sjlemon		 */
154729317Sjlemon		} else {
154829317Sjlemon			grp = tgrp;
154974462Salfred			while (grp && grp->gr_type == GT_IGNORE)
155029317Sjlemon				grp = grp->gr_next;
155129317Sjlemon			if (! grp) {
155229317Sjlemon			    getexp_err(ep, tgrp);
155329317Sjlemon			    goto nextline;
155429317Sjlemon			}
15551558Srgrimes		}
15561558Srgrimes
1557192934Srmacklem		if (v4root_phase == 1) {
1558192934Srmacklem			syslog(LOG_ERR, "V4:root, no dirp, ignored");
1559192934Srmacklem			getexp_err(ep, tgrp);
1560192934Srmacklem			goto nextline;
1561192934Srmacklem		}
1562192934Srmacklem
15631558Srgrimes		/*
15641558Srgrimes		 * Loop through hosts, pushing the exports into the kernel.
15651558Srgrimes		 * After loop, tgrp points to the start of the list and
15661558Srgrimes		 * grp points to the last entry in the list.
15671558Srgrimes		 */
15681558Srgrimes		grp = tgrp;
15691558Srgrimes		do {
157075635Siedowse			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
157175635Siedowse			    &fsb)) {
157275635Siedowse				getexp_err(ep, tgrp);
157375635Siedowse				goto nextline;
157475635Siedowse			}
15751558Srgrimes		} while (grp->gr_next && (grp = grp->gr_next));
15761558Srgrimes
15771558Srgrimes		/*
1578192934Srmacklem		 * For V4: don't enter in mount lists.
1579192934Srmacklem		 */
1580194773Srmacklem		if (v4root_phase > 0 && v4root_phase <= 2) {
1581194773Srmacklem			/*
1582194773Srmacklem			 * Since these structures aren't used by mountd,
1583194773Srmacklem			 * free them up now.
1584194773Srmacklem			 */
1585194773Srmacklem			if (ep != NULL)
1586194773Srmacklem				free_exp(ep);
1587194773Srmacklem			while (tgrp != NULL) {
1588194773Srmacklem				grp = tgrp;
1589194773Srmacklem				tgrp = tgrp->gr_next;
1590194773Srmacklem				free_grp(grp);
1591194773Srmacklem			}
1592192934Srmacklem			goto nextline;
1593194773Srmacklem		}
1594192934Srmacklem
1595192934Srmacklem		/*
15961558Srgrimes		 * Success. Update the data structures.
15971558Srgrimes		 */
15981558Srgrimes		if (has_host) {
15999336Sdfr			hang_dirp(dirhead, tgrp, ep, opt_flags);
16001558Srgrimes			grp->gr_next = grphead;
16011558Srgrimes			grphead = tgrp;
16021558Srgrimes		} else {
16031558Srgrimes			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
16049336Sdfr				opt_flags);
16051558Srgrimes			free_grp(grp);
16061558Srgrimes		}
16071558Srgrimes		dirhead = (struct dirlist *)NULL;
16081558Srgrimes		if ((ep->ex_flag & EX_LINKED) == 0) {
16091558Srgrimes			ep2 = exphead;
16101558Srgrimes			epp = &exphead;
16111558Srgrimes
16121558Srgrimes			/*
16131558Srgrimes			 * Insert in the list in alphabetical order.
16141558Srgrimes			 */
16151558Srgrimes			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
16161558Srgrimes				epp = &ep2->ex_next;
16171558Srgrimes				ep2 = ep2->ex_next;
16181558Srgrimes			}
16191558Srgrimes			if (ep2)
16201558Srgrimes				ep->ex_next = ep2;
16211558Srgrimes			*epp = ep;
16221558Srgrimes			ep->ex_flag |= EX_LINKED;
16231558Srgrimes		}
16241558Srgrimesnextline:
1625192934Srmacklem		v4root_phase = 0;
16261558Srgrimes		if (dirhead) {
16271558Srgrimes			free_dir(dirhead);
16281558Srgrimes			dirhead = (struct dirlist *)NULL;
16291558Srgrimes		}
16301558Srgrimes	}
16311558Srgrimes}
16321558Srgrimes
16331558Srgrimes/*
1634166440Spjd * Get the export list from all specified files
1635166440Spjd */
1636166440Spjdvoid
1637216587Scharnierget_exportlist(void)
1638166440Spjd{
1639166440Spjd	struct exportlist *ep, *ep2;
1640166440Spjd	struct grouplist *grp, *tgrp;
1641166440Spjd	struct export_args export;
1642166440Spjd	struct iovec *iov;
1643166440Spjd	struct statfs *fsp, *mntbufp;
1644166440Spjd	struct xvfsconf vfc;
1645166440Spjd	char *dirp;
1646166440Spjd	char errmsg[255];
1647166440Spjd	int dirplen, num, i;
1648166440Spjd	int iovlen;
1649168684Spjd	int done;
1650192934Srmacklem	struct nfsex_args eargs;
1651166440Spjd
1652192934Srmacklem	v4root_dirpath[0] = '\0';
1653166440Spjd	bzero(&export, sizeof(export));
1654166440Spjd	export.ex_flags = MNT_DELEXPORT;
1655166440Spjd	dirp = NULL;
1656166440Spjd	dirplen = 0;
1657166440Spjd	iov = NULL;
1658166440Spjd	iovlen = 0;
1659166440Spjd	bzero(errmsg, sizeof(errmsg));
1660166440Spjd
1661166440Spjd	/*
1662166440Spjd	 * First, get rid of the old list
1663166440Spjd	 */
1664166440Spjd	ep = exphead;
1665166440Spjd	while (ep) {
1666166440Spjd		ep2 = ep;
1667166440Spjd		ep = ep->ex_next;
1668166440Spjd		free_exp(ep2);
1669166440Spjd	}
1670166440Spjd	exphead = (struct exportlist *)NULL;
1671166440Spjd
1672166440Spjd	grp = grphead;
1673166440Spjd	while (grp) {
1674166440Spjd		tgrp = grp;
1675166440Spjd		grp = grp->gr_next;
1676166440Spjd		free_grp(tgrp);
1677166440Spjd	}
1678166440Spjd	grphead = (struct grouplist *)NULL;
1679166440Spjd
1680166440Spjd	/*
1681192934Srmacklem	 * and the old V4 root dir.
1682192934Srmacklem	 */
1683192934Srmacklem	bzero(&eargs, sizeof (eargs));
1684192934Srmacklem	eargs.export.ex_flags = MNT_DELEXPORT;
1685192934Srmacklem	if (run_v4server > 0 &&
1686192934Srmacklem	    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1687192934Srmacklem	    errno != ENOENT)
1688192934Srmacklem		syslog(LOG_ERR, "Can't delete exports for V4:");
1689192934Srmacklem
1690192934Srmacklem	/*
1691192934Srmacklem	 * and clear flag that notes if a public fh has been exported.
1692192934Srmacklem	 */
1693192934Srmacklem	has_publicfh = 0;
1694192934Srmacklem
1695192934Srmacklem	/*
1696166440Spjd	 * And delete exports that are in the kernel for all local
1697166440Spjd	 * filesystems.
1698166440Spjd	 * XXX: Should know how to handle all local exportable filesystems.
1699166440Spjd	 */
1700166440Spjd	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1701166440Spjd
1702166440Spjd	if (num > 0) {
1703166440Spjd		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1704166440Spjd		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1705166440Spjd		build_iovec(&iov, &iovlen, "from", NULL, 0);
1706166440Spjd		build_iovec(&iov, &iovlen, "update", NULL, 0);
1707166440Spjd		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1708166440Spjd		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1709166440Spjd	}
1710166440Spjd
1711166440Spjd	for (i = 0; i < num; i++) {
1712166440Spjd		fsp = &mntbufp[i];
1713166440Spjd		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1714166440Spjd			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1715166440Spjd			    fsp->f_fstypename);
1716166440Spjd			continue;
1717166440Spjd		}
1718166440Spjd
1719166440Spjd		/*
1720166440Spjd		 * Do not delete export for network filesystem by
1721166440Spjd		 * passing "export" arg to nmount().
1722166440Spjd		 * It only makes sense to do this for local filesystems.
1723166440Spjd		 */
1724166440Spjd		if (vfc.vfc_flags & VFCF_NETWORK)
1725166440Spjd			continue;
1726166440Spjd
1727166440Spjd		iov[1].iov_base = fsp->f_fstypename;
1728166440Spjd		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1729166440Spjd		iov[3].iov_base = fsp->f_mntonname;
1730166440Spjd		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1731166440Spjd		iov[5].iov_base = fsp->f_mntfromname;
1732166440Spjd		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1733166440Spjd
1734166440Spjd		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1735166440Spjd		    errno != ENOENT && errno != ENOTSUP) {
1736166440Spjd			syslog(LOG_ERR,
1737166440Spjd			    "can't delete exports for %s: %m %s",
1738166440Spjd			    fsp->f_mntonname, errmsg);
1739166440Spjd		}
1740166440Spjd	}
1741166440Spjd
1742166440Spjd	if (iov != NULL) {
1743166440Spjd		/* Free strings allocated by strdup() in getmntopts.c */
1744166440Spjd		free(iov[0].iov_base); /* fstype */
1745166440Spjd		free(iov[2].iov_base); /* fspath */
1746166440Spjd		free(iov[4].iov_base); /* from */
1747166440Spjd		free(iov[6].iov_base); /* update */
1748166440Spjd		free(iov[8].iov_base); /* export */
1749166440Spjd		free(iov[10].iov_base); /* errmsg */
1750166440Spjd
1751166440Spjd		/* free iov, allocated by realloc() */
1752166440Spjd		free(iov);
1753166440Spjd		iovlen = 0;
1754166440Spjd	}
1755166440Spjd
1756166440Spjd	/*
1757166440Spjd	 * Read in the exports file and build the list, calling
1758166440Spjd	 * nmount() as we go along to push the export rules into the kernel.
1759166440Spjd	 */
1760168684Spjd	done = 0;
1761166440Spjd	for (i = 0; exnames[i] != NULL; i++) {
1762166440Spjd		if (debug)
1763166440Spjd			warnx("reading exports from %s", exnames[i]);
1764166440Spjd		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1765168684Spjd			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1766168684Spjd			continue;
1767166440Spjd		}
1768166440Spjd		get_exportlist_one();
1769166440Spjd		fclose(exp_file);
1770168684Spjd		done++;
1771166440Spjd	}
1772168684Spjd	if (done == 0) {
1773168684Spjd		syslog(LOG_ERR, "can't open any exports file");
1774168684Spjd		exit(2);
1775168684Spjd	}
1776192934Srmacklem
1777192934Srmacklem	/*
1778192934Srmacklem	 * If there was no public fh, clear any previous one set.
1779192934Srmacklem	 */
1780192934Srmacklem	if (run_v4server > 0 && has_publicfh == 0)
1781192934Srmacklem		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1782166440Spjd}
1783166440Spjd
1784166440Spjd/*
17851558Srgrimes * Allocate an export list element
17861558Srgrimes */
17871558Srgrimesstruct exportlist *
1788216587Scharnierget_exp(void)
17891558Srgrimes{
17901558Srgrimes	struct exportlist *ep;
17911558Srgrimes
17921558Srgrimes	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
17931558Srgrimes	if (ep == (struct exportlist *)NULL)
17941558Srgrimes		out_of_mem();
179523681Speter	memset(ep, 0, sizeof(struct exportlist));
17961558Srgrimes	return (ep);
17971558Srgrimes}
17981558Srgrimes
17991558Srgrimes/*
18001558Srgrimes * Allocate a group list element
18011558Srgrimes */
18021558Srgrimesstruct grouplist *
1803216587Scharnierget_grp(void)
18041558Srgrimes{
18051558Srgrimes	struct grouplist *gp;
18061558Srgrimes
18071558Srgrimes	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
18081558Srgrimes	if (gp == (struct grouplist *)NULL)
18091558Srgrimes		out_of_mem();
181023681Speter	memset(gp, 0, sizeof(struct grouplist));
18111558Srgrimes	return (gp);
18121558Srgrimes}
18131558Srgrimes
18141558Srgrimes/*
18151558Srgrimes * Clean up upon an error in get_exportlist().
18161558Srgrimes */
18171558Srgrimesvoid
1818216587Scharniergetexp_err(struct exportlist *ep, struct grouplist *grp)
18191558Srgrimes{
18201558Srgrimes	struct grouplist *tgrp;
18211558Srgrimes
1822100336Sjoerg	if (!(opt_flags & OP_QUIET))
1823100336Sjoerg		syslog(LOG_ERR, "bad exports list line %s", line);
18241558Srgrimes	if (ep && (ep->ex_flag & EX_LINKED) == 0)
18251558Srgrimes		free_exp(ep);
18261558Srgrimes	while (grp) {
18271558Srgrimes		tgrp = grp;
18281558Srgrimes		grp = grp->gr_next;
18291558Srgrimes		free_grp(tgrp);
18301558Srgrimes	}
18311558Srgrimes}
18321558Srgrimes
18331558Srgrimes/*
18341558Srgrimes * Search the export list for a matching fs.
18351558Srgrimes */
18361558Srgrimesstruct exportlist *
1837216587Scharnierex_search(fsid_t *fsid)
18381558Srgrimes{
18391558Srgrimes	struct exportlist *ep;
18401558Srgrimes
18411558Srgrimes	ep = exphead;
18421558Srgrimes	while (ep) {
18431558Srgrimes		if (ep->ex_fs.val[0] == fsid->val[0] &&
18441558Srgrimes		    ep->ex_fs.val[1] == fsid->val[1])
18451558Srgrimes			return (ep);
18461558Srgrimes		ep = ep->ex_next;
18471558Srgrimes	}
18481558Srgrimes	return (ep);
18491558Srgrimes}
18501558Srgrimes
18511558Srgrimes/*
18521558Srgrimes * Add a directory path to the list.
18531558Srgrimes */
18541558Srgrimeschar *
1855216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len)
18561558Srgrimes{
18571558Srgrimes	struct dirlist *dp;
18581558Srgrimes
18591558Srgrimes	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
186037663Scharnier	if (dp == (struct dirlist *)NULL)
186137663Scharnier		out_of_mem();
18621558Srgrimes	dp->dp_left = *dpp;
18631558Srgrimes	dp->dp_right = (struct dirlist *)NULL;
18641558Srgrimes	dp->dp_flag = 0;
18651558Srgrimes	dp->dp_hosts = (struct hostlist *)NULL;
18661558Srgrimes	strcpy(dp->dp_dirp, cp);
18671558Srgrimes	*dpp = dp;
18681558Srgrimes	return (dp->dp_dirp);
18691558Srgrimes}
18701558Srgrimes
18711558Srgrimes/*
18721558Srgrimes * Hang the dir list element off the dirpath binary tree as required
18731558Srgrimes * and update the entry for host.
18741558Srgrimes */
18751558Srgrimesvoid
1876216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1877216587Scharnier	int flags)
18781558Srgrimes{
18791558Srgrimes	struct hostlist *hp;
18801558Srgrimes	struct dirlist *dp2;
18811558Srgrimes
18829336Sdfr	if (flags & OP_ALLDIRS) {
18831558Srgrimes		if (ep->ex_defdir)
18841558Srgrimes			free((caddr_t)dp);
18851558Srgrimes		else
18861558Srgrimes			ep->ex_defdir = dp;
18879336Sdfr		if (grp == (struct grouplist *)NULL) {
18881558Srgrimes			ep->ex_defdir->dp_flag |= DP_DEFSET;
18899336Sdfr		} else while (grp) {
18901558Srgrimes			hp = get_ht();
18911558Srgrimes			hp->ht_grp = grp;
18921558Srgrimes			hp->ht_next = ep->ex_defdir->dp_hosts;
18931558Srgrimes			ep->ex_defdir->dp_hosts = hp;
18941558Srgrimes			grp = grp->gr_next;
18951558Srgrimes		}
18961558Srgrimes	} else {
18971558Srgrimes
18981558Srgrimes		/*
189937663Scharnier		 * Loop through the directories adding them to the tree.
19001558Srgrimes		 */
19011558Srgrimes		while (dp) {
19021558Srgrimes			dp2 = dp->dp_left;
19039336Sdfr			add_dlist(&ep->ex_dirl, dp, grp, flags);
19041558Srgrimes			dp = dp2;
19051558Srgrimes		}
19061558Srgrimes	}
19071558Srgrimes}
19081558Srgrimes
19091558Srgrimes/*
19101558Srgrimes * Traverse the binary tree either updating a node that is already there
19111558Srgrimes * for the new directory or adding the new node.
19121558Srgrimes */
19131558Srgrimesvoid
1914216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
1915216587Scharnier	int flags)
19161558Srgrimes{
19171558Srgrimes	struct dirlist *dp;
19181558Srgrimes	struct hostlist *hp;
19191558Srgrimes	int cmp;
19201558Srgrimes
19211558Srgrimes	dp = *dpp;
19221558Srgrimes	if (dp) {
19231558Srgrimes		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
19241558Srgrimes		if (cmp > 0) {
19259336Sdfr			add_dlist(&dp->dp_left, newdp, grp, flags);
19261558Srgrimes			return;
19271558Srgrimes		} else if (cmp < 0) {
19289336Sdfr			add_dlist(&dp->dp_right, newdp, grp, flags);
19291558Srgrimes			return;
19301558Srgrimes		} else
19311558Srgrimes			free((caddr_t)newdp);
19321558Srgrimes	} else {
19331558Srgrimes		dp = newdp;
19341558Srgrimes		dp->dp_left = (struct dirlist *)NULL;
19351558Srgrimes		*dpp = dp;
19361558Srgrimes	}
19371558Srgrimes	if (grp) {
19381558Srgrimes
19391558Srgrimes		/*
19401558Srgrimes		 * Hang all of the host(s) off of the directory point.
19411558Srgrimes		 */
19421558Srgrimes		do {
19431558Srgrimes			hp = get_ht();
19441558Srgrimes			hp->ht_grp = grp;
19451558Srgrimes			hp->ht_next = dp->dp_hosts;
19461558Srgrimes			dp->dp_hosts = hp;
19471558Srgrimes			grp = grp->gr_next;
19481558Srgrimes		} while (grp);
19499336Sdfr	} else {
19501558Srgrimes		dp->dp_flag |= DP_DEFSET;
19519336Sdfr	}
19521558Srgrimes}
19531558Srgrimes
19541558Srgrimes/*
19551558Srgrimes * Search for a dirpath on the export point.
19561558Srgrimes */
19571558Srgrimesstruct dirlist *
1958216587Scharnierdirp_search(struct dirlist *dp, char *dirp)
19591558Srgrimes{
19601558Srgrimes	int cmp;
19611558Srgrimes
19621558Srgrimes	if (dp) {
196374462Salfred		cmp = strcmp(dp->dp_dirp, dirp);
19641558Srgrimes		if (cmp > 0)
196574462Salfred			return (dirp_search(dp->dp_left, dirp));
19661558Srgrimes		else if (cmp < 0)
196774462Salfred			return (dirp_search(dp->dp_right, dirp));
19681558Srgrimes		else
19691558Srgrimes			return (dp);
19701558Srgrimes	}
19711558Srgrimes	return (dp);
19721558Srgrimes}
19731558Srgrimes
19741558Srgrimes/*
19751558Srgrimes * Scan for a host match in a directory tree.
19761558Srgrimes */
19771558Srgrimesint
1978216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
1979216587Scharnier	int *hostsetp)
19801558Srgrimes{
19811558Srgrimes	struct hostlist *hp;
19821558Srgrimes	struct grouplist *grp;
198374462Salfred	struct addrinfo *ai;
19841558Srgrimes
19851558Srgrimes	if (dp) {
19861558Srgrimes		if (dp->dp_flag & DP_DEFSET)
19879336Sdfr			*defsetp = dp->dp_flag;
19881558Srgrimes		hp = dp->dp_hosts;
19891558Srgrimes		while (hp) {
19901558Srgrimes			grp = hp->ht_grp;
19911558Srgrimes			switch (grp->gr_type) {
19921558Srgrimes			case GT_HOST:
199374462Salfred				ai = grp->gr_ptr.gt_addrinfo;
199474462Salfred				for (; ai; ai = ai->ai_next) {
199575801Siedowse					if (!sacmp(ai->ai_addr, saddr, NULL)) {
199674462Salfred						*hostsetp =
199774462Salfred						    (hp->ht_flag | DP_HOSTSET);
199874462Salfred						return (1);
199974462Salfred					}
20009336Sdfr				}
200175801Siedowse				break;
20021558Srgrimes			case GT_NET:
200375801Siedowse				if (!sacmp(saddr, (struct sockaddr *)
200475801Siedowse				    &grp->gr_ptr.gt_net.nt_net,
200575801Siedowse				    (struct sockaddr *)
200675801Siedowse				    &grp->gr_ptr.gt_net.nt_mask)) {
200774462Salfred					*hostsetp = (hp->ht_flag | DP_HOSTSET);
200874462Salfred					return (1);
200974462Salfred				}
201075801Siedowse				break;
201175801Siedowse			}
20121558Srgrimes			hp = hp->ht_next;
20131558Srgrimes		}
20141558Srgrimes	}
20151558Srgrimes	return (0);
20161558Srgrimes}
20171558Srgrimes
20181558Srgrimes/*
20191558Srgrimes * Scan tree for a host that matches the address.
20201558Srgrimes */
20211558Srgrimesint
2022216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr)
20231558Srgrimes{
20249336Sdfr	int defset, hostset;
20251558Srgrimes
20261558Srgrimes	if (dp) {
20271558Srgrimes		if (scan_tree(dp->dp_left, saddr))
20281558Srgrimes			return (1);
20299336Sdfr		if (chk_host(dp, saddr, &defset, &hostset))
20301558Srgrimes			return (1);
20311558Srgrimes		if (scan_tree(dp->dp_right, saddr))
20321558Srgrimes			return (1);
20331558Srgrimes	}
20341558Srgrimes	return (0);
20351558Srgrimes}
20361558Srgrimes
20371558Srgrimes/*
20381558Srgrimes * Traverse the dirlist tree and free it up.
20391558Srgrimes */
20401558Srgrimesvoid
2041216587Scharnierfree_dir(struct dirlist *dp)
20421558Srgrimes{
20431558Srgrimes
20441558Srgrimes	if (dp) {
20451558Srgrimes		free_dir(dp->dp_left);
20461558Srgrimes		free_dir(dp->dp_right);
20471558Srgrimes		free_host(dp->dp_hosts);
20481558Srgrimes		free((caddr_t)dp);
20491558Srgrimes	}
20501558Srgrimes}
20511558Srgrimes
20521558Srgrimes/*
2053184588Sdfr * Parse a colon separated list of security flavors
2054184588Sdfr */
2055184588Sdfrint
2056216587Scharnierparsesec(char *seclist, struct exportlist *ep)
2057184588Sdfr{
2058184588Sdfr	char *cp, savedc;
2059184588Sdfr	int flavor;
2060184588Sdfr
2061184588Sdfr	ep->ex_numsecflavors = 0;
2062184588Sdfr	for (;;) {
2063184588Sdfr		cp = strchr(seclist, ':');
2064184588Sdfr		if (cp) {
2065184588Sdfr			savedc = *cp;
2066184588Sdfr			*cp = '\0';
2067184588Sdfr		}
2068184588Sdfr
2069184588Sdfr		if (!strcmp(seclist, "sys"))
2070184588Sdfr			flavor = AUTH_SYS;
2071184588Sdfr		else if (!strcmp(seclist, "krb5"))
2072184588Sdfr			flavor = RPCSEC_GSS_KRB5;
2073184588Sdfr		else if (!strcmp(seclist, "krb5i"))
2074184588Sdfr			flavor = RPCSEC_GSS_KRB5I;
2075184588Sdfr		else if (!strcmp(seclist, "krb5p"))
2076184588Sdfr			flavor = RPCSEC_GSS_KRB5P;
2077184588Sdfr		else {
2078184588Sdfr			if (cp)
2079184588Sdfr				*cp = savedc;
2080184588Sdfr			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2081184588Sdfr			return (1);
2082184588Sdfr		}
2083184588Sdfr		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2084184588Sdfr			if (cp)
2085184588Sdfr				*cp = savedc;
2086184588Sdfr			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2087184588Sdfr			return (1);
2088184588Sdfr		}
2089184588Sdfr		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2090184588Sdfr		ep->ex_numsecflavors++;
2091184588Sdfr		if (cp) {
2092184588Sdfr			*cp = savedc;
2093184588Sdfr			seclist = cp + 1;
2094184588Sdfr		} else {
2095184588Sdfr			break;
2096184588Sdfr		}
2097184588Sdfr	}
2098184588Sdfr	return (0);
2099184588Sdfr}
2100184588Sdfr
2101184588Sdfr/*
21021558Srgrimes * Parse the option string and update fields.
21031558Srgrimes * Option arguments may either be -<option>=<value> or
21041558Srgrimes * -<option> <value>
21051558Srgrimes */
21061558Srgrimesint
2107216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2108216587Scharnier	int *has_hostp, int *exflagsp, struct xucred *cr)
21091558Srgrimes{
21101558Srgrimes	char *cpoptarg, *cpoptend;
21111558Srgrimes	char *cp, *endcp, *cpopt, savedc, savedc2;
21121558Srgrimes	int allflag, usedarg;
21131558Srgrimes
211451968Salfred	savedc2 = '\0';
21151558Srgrimes	cpopt = *cpp;
21161558Srgrimes	cpopt++;
21171558Srgrimes	cp = *endcpp;
21181558Srgrimes	savedc = *cp;
21191558Srgrimes	*cp = '\0';
21201558Srgrimes	while (cpopt && *cpopt) {
21211558Srgrimes		allflag = 1;
21221558Srgrimes		usedarg = -2;
212337663Scharnier		if ((cpoptend = strchr(cpopt, ','))) {
21241558Srgrimes			*cpoptend++ = '\0';
212537663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
21261558Srgrimes				*cpoptarg++ = '\0';
21271558Srgrimes		} else {
212837663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
21291558Srgrimes				*cpoptarg++ = '\0';
21301558Srgrimes			else {
21311558Srgrimes				*cp = savedc;
21321558Srgrimes				nextfield(&cp, &endcp);
21331558Srgrimes				**endcpp = '\0';
21341558Srgrimes				if (endcp > cp && *cp != '-') {
21351558Srgrimes					cpoptarg = cp;
21361558Srgrimes					savedc2 = *endcp;
21371558Srgrimes					*endcp = '\0';
21381558Srgrimes					usedarg = 0;
21391558Srgrimes				}
21401558Srgrimes			}
21411558Srgrimes		}
21421558Srgrimes		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
21431558Srgrimes			*exflagsp |= MNT_EXRDONLY;
21441558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
21451558Srgrimes		    !(allflag = strcmp(cpopt, "mapall")) ||
21461558Srgrimes		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
21471558Srgrimes			usedarg++;
21481558Srgrimes			parsecred(cpoptarg, cr);
21491558Srgrimes			if (allflag == 0) {
21501558Srgrimes				*exflagsp |= MNT_EXPORTANON;
21511558Srgrimes				opt_flags |= OP_MAPALL;
21521558Srgrimes			} else
21531558Srgrimes				opt_flags |= OP_MAPROOT;
21541558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
215575801Siedowse		    !strcmp(cpopt, "m"))) {
21561558Srgrimes			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
215737663Scharnier				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
21581558Srgrimes				return (1);
21591558Srgrimes			}
21601558Srgrimes			usedarg++;
21611558Srgrimes			opt_flags |= OP_MASK;
21621558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
21631558Srgrimes			!strcmp(cpopt, "n"))) {
216474462Salfred			if (strchr(cpoptarg, '/') != NULL) {
216574462Salfred				if (debug)
216674462Salfred					fprintf(stderr, "setting OP_MASKLEN\n");
216774462Salfred				opt_flags |= OP_MASKLEN;
216874462Salfred			}
21691558Srgrimes			if (grp->gr_type != GT_NULL) {
217037663Scharnier				syslog(LOG_ERR, "network/host conflict");
21711558Srgrimes				return (1);
21721558Srgrimes			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
217337663Scharnier				syslog(LOG_ERR, "bad net: %s", cpoptarg);
21741558Srgrimes				return (1);
21751558Srgrimes			}
21761558Srgrimes			grp->gr_type = GT_NET;
21771558Srgrimes			*has_hostp = 1;
21781558Srgrimes			usedarg++;
21791558Srgrimes			opt_flags |= OP_NET;
21801558Srgrimes		} else if (!strcmp(cpopt, "alldirs")) {
21811558Srgrimes			opt_flags |= OP_ALLDIRS;
218227447Sdfr		} else if (!strcmp(cpopt, "public")) {
218327447Sdfr			*exflagsp |= MNT_EXPUBLIC;
218427447Sdfr		} else if (!strcmp(cpopt, "webnfs")) {
218527447Sdfr			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
218627447Sdfr			opt_flags |= OP_MAPALL;
218727447Sdfr		} else if (cpoptarg && !strcmp(cpopt, "index")) {
218827447Sdfr			ep->ex_indexfile = strdup(cpoptarg);
2189100336Sjoerg		} else if (!strcmp(cpopt, "quiet")) {
2190100336Sjoerg			opt_flags |= OP_QUIET;
2191184588Sdfr		} else if (!strcmp(cpopt, "sec")) {
2192184588Sdfr			if (parsesec(cpoptarg, ep))
2193184588Sdfr				return (1);
2194184588Sdfr			opt_flags |= OP_SEC;
2195184588Sdfr			usedarg++;
21961558Srgrimes		} else {
219737663Scharnier			syslog(LOG_ERR, "bad opt %s", cpopt);
21981558Srgrimes			return (1);
21991558Srgrimes		}
22001558Srgrimes		if (usedarg >= 0) {
22011558Srgrimes			*endcp = savedc2;
22021558Srgrimes			**endcpp = savedc;
22031558Srgrimes			if (usedarg > 0) {
22041558Srgrimes				*cpp = cp;
22051558Srgrimes				*endcpp = endcp;
22061558Srgrimes			}
22071558Srgrimes			return (0);
22081558Srgrimes		}
22091558Srgrimes		cpopt = cpoptend;
22101558Srgrimes	}
22111558Srgrimes	**endcpp = savedc;
22121558Srgrimes	return (0);
22131558Srgrimes}
22141558Srgrimes
22151558Srgrimes/*
22161558Srgrimes * Translate a character string to the corresponding list of network
22171558Srgrimes * addresses for a hostname.
22181558Srgrimes */
22191558Srgrimesint
2220216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
22211558Srgrimes{
22227401Swpaul	struct grouplist *checkgrp;
222375635Siedowse	struct addrinfo *ai, *tai, hints;
222474462Salfred	int ecode;
222574462Salfred	char host[NI_MAXHOST];
22261558Srgrimes
222774462Salfred	if (grp->gr_type != GT_NULL) {
222874462Salfred		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
22291558Srgrimes		return (1);
22301558Srgrimes	}
223174462Salfred	memset(&hints, 0, sizeof hints);
223274462Salfred	hints.ai_flags = AI_CANONNAME;
223374462Salfred	hints.ai_protocol = IPPROTO_UDP;
223474462Salfred	ecode = getaddrinfo(cp, NULL, &hints, &ai);
223574462Salfred	if (ecode != 0) {
223675635Siedowse		syslog(LOG_ERR,"can't get address info for host %s", cp);
223774462Salfred		return 1;
223874462Salfred	}
223974462Salfred	grp->gr_ptr.gt_addrinfo = ai;
224074462Salfred	while (ai != NULL) {
224174462Salfred		if (ai->ai_canonname == NULL) {
224274462Salfred			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2243146187Sume			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
224474462Salfred				strlcpy(host, "?", sizeof(host));
224574462Salfred			ai->ai_canonname = strdup(host);
224674462Salfred			ai->ai_flags |= AI_CANONNAME;
224775641Siedowse		}
224874462Salfred		if (debug)
224975635Siedowse			fprintf(stderr, "got host %s\n", ai->ai_canonname);
225075635Siedowse		/*
225175635Siedowse		 * Sanity check: make sure we don't already have an entry
225275635Siedowse		 * for this host in the grouplist.
225375635Siedowse		 */
225475635Siedowse		for (checkgrp = tgrp; checkgrp != NULL;
225575635Siedowse		    checkgrp = checkgrp->gr_next) {
225675635Siedowse			if (checkgrp->gr_type != GT_HOST)
225775635Siedowse				continue;
225875635Siedowse			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
225975635Siedowse			    tai = tai->ai_next) {
226075801Siedowse				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
226175635Siedowse					continue;
226275635Siedowse				if (debug)
226375635Siedowse					fprintf(stderr,
226475635Siedowse					    "ignoring duplicate host %s\n",
226575635Siedowse					    ai->ai_canonname);
226675635Siedowse				grp->gr_type = GT_IGNORE;
226775635Siedowse				return (0);
226875635Siedowse			}
226975635Siedowse		}
227074462Salfred		ai = ai->ai_next;
22711558Srgrimes	}
227275635Siedowse	grp->gr_type = GT_HOST;
22731558Srgrimes	return (0);
22741558Srgrimes}
22751558Srgrimes
22761558Srgrimes/*
22771558Srgrimes * Free up an exports list component
22781558Srgrimes */
22791558Srgrimesvoid
2280216587Scharnierfree_exp(struct exportlist *ep)
22811558Srgrimes{
22821558Srgrimes
22831558Srgrimes	if (ep->ex_defdir) {
22841558Srgrimes		free_host(ep->ex_defdir->dp_hosts);
22851558Srgrimes		free((caddr_t)ep->ex_defdir);
22861558Srgrimes	}
22871558Srgrimes	if (ep->ex_fsdir)
22881558Srgrimes		free(ep->ex_fsdir);
228927447Sdfr	if (ep->ex_indexfile)
229027447Sdfr		free(ep->ex_indexfile);
22911558Srgrimes	free_dir(ep->ex_dirl);
22921558Srgrimes	free((caddr_t)ep);
22931558Srgrimes}
22941558Srgrimes
22951558Srgrimes/*
22961558Srgrimes * Free hosts.
22971558Srgrimes */
22981558Srgrimesvoid
2299216587Scharnierfree_host(struct hostlist *hp)
23001558Srgrimes{
23011558Srgrimes	struct hostlist *hp2;
23021558Srgrimes
23031558Srgrimes	while (hp) {
23041558Srgrimes		hp2 = hp;
23051558Srgrimes		hp = hp->ht_next;
23061558Srgrimes		free((caddr_t)hp2);
23071558Srgrimes	}
23081558Srgrimes}
23091558Srgrimes
23101558Srgrimesstruct hostlist *
2311216587Scharnierget_ht(void)
23121558Srgrimes{
23131558Srgrimes	struct hostlist *hp;
23141558Srgrimes
23151558Srgrimes	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
23161558Srgrimes	if (hp == (struct hostlist *)NULL)
23171558Srgrimes		out_of_mem();
23181558Srgrimes	hp->ht_next = (struct hostlist *)NULL;
23199336Sdfr	hp->ht_flag = 0;
23201558Srgrimes	return (hp);
23211558Srgrimes}
23221558Srgrimes
23231558Srgrimes/*
23241558Srgrimes * Out of memory, fatal
23251558Srgrimes */
23261558Srgrimesvoid
2327216587Scharnierout_of_mem(void)
23281558Srgrimes{
23291558Srgrimes
233037663Scharnier	syslog(LOG_ERR, "out of memory");
23311558Srgrimes	exit(2);
23321558Srgrimes}
23331558Srgrimes
23341558Srgrimes/*
2335158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into
23361558Srgrimes * the kernel.
23371558Srgrimes */
23381558Srgrimesint
2339158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2340158857Srodrigc    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
23411558Srgrimes{
234275841Siedowse	struct statfs fsb1;
234374462Salfred	struct addrinfo *ai;
2344192934Srmacklem	struct export_args ea, *eap;
2345158857Srodrigc	char errmsg[255];
2346158857Srodrigc	char *cp;
23471558Srgrimes	int done;
2348158857Srodrigc	char savedc;
2349158857Srodrigc	struct iovec *iov;
2350184588Sdfr	int i, iovlen;
2351158857Srodrigc	int ret;
2352192934Srmacklem	struct nfsex_args nfsea;
23531558Srgrimes
2354192934Srmacklem	if (run_v4server > 0)
2355192934Srmacklem		eap = &nfsea.export;
2356192934Srmacklem	else
2357192934Srmacklem		eap = &ea;
2358192934Srmacklem
2359158857Srodrigc	cp = NULL;
2360158857Srodrigc	savedc = '\0';
2361158857Srodrigc	iov = NULL;
2362158857Srodrigc	iovlen = 0;
2363158857Srodrigc	ret = 0;
236475801Siedowse
2365192934Srmacklem	bzero(eap, sizeof (struct export_args));
2366158857Srodrigc	bzero(errmsg, sizeof(errmsg));
2367192934Srmacklem	eap->ex_flags = exflags;
2368192934Srmacklem	eap->ex_anon = *anoncrp;
2369192934Srmacklem	eap->ex_indexfile = ep->ex_indexfile;
237075641Siedowse	if (grp->gr_type == GT_HOST)
237174462Salfred		ai = grp->gr_ptr.gt_addrinfo;
237275641Siedowse	else
237375641Siedowse		ai = NULL;
2374192934Srmacklem	eap->ex_numsecflavors = ep->ex_numsecflavors;
2375192934Srmacklem	for (i = 0; i < eap->ex_numsecflavors; i++)
2376192934Srmacklem		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2377192934Srmacklem	if (eap->ex_numsecflavors == 0) {
2378192934Srmacklem		eap->ex_numsecflavors = 1;
2379192934Srmacklem		eap->ex_secflavors[0] = AUTH_SYS;
2380184588Sdfr	}
23811558Srgrimes	done = FALSE;
2382158857Srodrigc
2383192934Srmacklem	if (v4root_phase == 0) {
2384192934Srmacklem		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2385192934Srmacklem		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2386192934Srmacklem		build_iovec(&iov, &iovlen, "from", NULL, 0);
2387192934Srmacklem		build_iovec(&iov, &iovlen, "update", NULL, 0);
2388192934Srmacklem		build_iovec(&iov, &iovlen, "export", eap,
2389192934Srmacklem		    sizeof (struct export_args));
2390192934Srmacklem		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2391192934Srmacklem	}
2392158857Srodrigc
23931558Srgrimes	while (!done) {
23941558Srgrimes		switch (grp->gr_type) {
23951558Srgrimes		case GT_HOST:
239675641Siedowse			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
239774462Salfred				goto skip;
2398192934Srmacklem			eap->ex_addr = ai->ai_addr;
2399192934Srmacklem			eap->ex_addrlen = ai->ai_addrlen;
2400192934Srmacklem			eap->ex_masklen = 0;
24011558Srgrimes			break;
24021558Srgrimes		case GT_NET:
240375801Siedowse			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
240474462Salfred			    have_v6 == 0)
240574462Salfred				goto skip;
2406192934Srmacklem			eap->ex_addr =
240775801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2408192934Srmacklem			eap->ex_addrlen =
2409158857Srodrigc			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2410192934Srmacklem			eap->ex_mask =
241175801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2412192934Srmacklem			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
24131558Srgrimes			break;
241475641Siedowse		case GT_DEFAULT:
2415192934Srmacklem			eap->ex_addr = NULL;
2416192934Srmacklem			eap->ex_addrlen = 0;
2417192934Srmacklem			eap->ex_mask = NULL;
2418192934Srmacklem			eap->ex_masklen = 0;
241975641Siedowse			break;
24207401Swpaul		case GT_IGNORE:
2421158857Srodrigc			ret = 0;
2422158857Srodrigc			goto error_exit;
24237401Swpaul			break;
24241558Srgrimes		default:
242537663Scharnier			syslog(LOG_ERR, "bad grouptype");
24261558Srgrimes			if (cp)
24271558Srgrimes				*cp = savedc;
2428158857Srodrigc			ret = 1;
2429158857Srodrigc			goto error_exit;
24301558Srgrimes		};
24311558Srgrimes
24321558Srgrimes		/*
2433192934Srmacklem		 * For V4:, use the nfssvc() syscall, instead of mount().
24341558Srgrimes		 */
2435192934Srmacklem		if (v4root_phase == 2) {
2436192934Srmacklem			nfsea.fspec = v4root_dirpath;
2437192934Srmacklem			if (run_v4server > 0 &&
2438192934Srmacklem			    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2439192934Srmacklem				syslog(LOG_ERR, "Exporting V4: failed");
2440192934Srmacklem				return (2);
2441158857Srodrigc			}
2442192934Srmacklem		} else {
2443192934Srmacklem			/*
2444192934Srmacklem			 * XXX:
2445192934Srmacklem			 * Maybe I should just use the fsb->f_mntonname path
2446192934Srmacklem			 * instead of looping back up the dirp to the mount
2447192934Srmacklem			 * point??
2448192934Srmacklem			 * Also, needs to know how to export all types of local
2449192934Srmacklem			 * exportable filesystems and not just "ufs".
2450192934Srmacklem			 */
2451192934Srmacklem			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2452192934Srmacklem			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2453192934Srmacklem			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2454192934Srmacklem			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2455192934Srmacklem			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2456192934Srmacklem			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2457192934Srmacklem
2458192934Srmacklem			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2459192934Srmacklem				if (cp)
2460192934Srmacklem					*cp-- = savedc;
2461192934Srmacklem				else
2462192934Srmacklem					cp = dirp + dirplen - 1;
2463192934Srmacklem				if (opt_flags & OP_QUIET) {
2464192934Srmacklem					ret = 1;
2465192934Srmacklem					goto error_exit;
2466192934Srmacklem				}
2467192934Srmacklem				if (errno == EPERM) {
2468192934Srmacklem					if (debug)
2469192934Srmacklem						warnx("can't change attributes for %s",
2470192934Srmacklem						    dirp);
2471192934Srmacklem					syslog(LOG_ERR,
2472192934Srmacklem					   "can't change attributes for %s",
247375635Siedowse					    dirp);
2474192934Srmacklem					ret = 1;
2475192934Srmacklem					goto error_exit;
2476192934Srmacklem				}
2477192934Srmacklem				if (opt_flags & OP_ALLDIRS) {
2478192934Srmacklem					if (errno == EINVAL)
2479192934Srmacklem						syslog(LOG_ERR,
2480100336Sjoerg		"-alldirs requested but %s is not a filesystem mountpoint",
2481192934Srmacklem						    dirp);
2482192934Srmacklem					else
2483192934Srmacklem						syslog(LOG_ERR,
2484192934Srmacklem						    "could not remount %s: %m",
2485192934Srmacklem						    dirp);
2486192934Srmacklem					ret = 1;
2487192934Srmacklem					goto error_exit;
2488192934Srmacklem				}
2489192934Srmacklem				/* back up over the last component */
2490192934Srmacklem				while (*cp == '/' && cp > dirp)
2491192934Srmacklem					cp--;
2492192934Srmacklem				while (*(cp - 1) != '/' && cp > dirp)
2493192934Srmacklem					cp--;
2494192934Srmacklem				if (cp == dirp) {
2495192934Srmacklem					if (debug)
2496192934Srmacklem						warnx("mnt unsucc");
2497192934Srmacklem					syslog(LOG_ERR, "can't export %s %s",
2498192934Srmacklem					    dirp, errmsg);
2499192934Srmacklem					ret = 1;
2500192934Srmacklem					goto error_exit;
2501192934Srmacklem				}
2502192934Srmacklem				savedc = *cp;
2503192934Srmacklem				*cp = '\0';
2504192934Srmacklem				/*
2505192934Srmacklem				 * Check that we're still on the same
2506192934Srmacklem				 * filesystem.
2507192934Srmacklem				 */
2508192934Srmacklem				if (statfs(dirp, &fsb1) != 0 ||
2509192934Srmacklem				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2510192934Srmacklem				    sizeof (fsb1.f_fsid)) != 0) {
2511192934Srmacklem					*cp = savedc;
2512100336Sjoerg					syslog(LOG_ERR,
2513192934Srmacklem					    "can't export %s %s", dirp,
2514192934Srmacklem					    errmsg);
2515192934Srmacklem					ret = 1;
2516192934Srmacklem					goto error_exit;
2517192934Srmacklem				}
25181558Srgrimes			}
25191558Srgrimes		}
2520192934Srmacklem
2521192934Srmacklem		/*
2522192934Srmacklem		 * For the experimental server:
2523192934Srmacklem		 * If this is the public directory, get the file handle
2524192934Srmacklem		 * and load it into the kernel via the nfssvc() syscall.
2525192934Srmacklem		 */
2526192934Srmacklem		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
2527192934Srmacklem			fhandle_t fh;
2528192934Srmacklem			char *public_name;
2529192934Srmacklem
2530192934Srmacklem			if (eap->ex_indexfile != NULL)
2531192934Srmacklem				public_name = eap->ex_indexfile;
2532192934Srmacklem			else
2533192934Srmacklem				public_name = dirp;
2534192934Srmacklem			if (getfh(public_name, &fh) < 0)
2535192934Srmacklem				syslog(LOG_ERR,
2536192934Srmacklem				    "Can't get public fh for %s", public_name);
2537192934Srmacklem			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2538192934Srmacklem				syslog(LOG_ERR,
2539192934Srmacklem				    "Can't set public fh for %s", public_name);
2540192934Srmacklem			else
2541192934Srmacklem				has_publicfh = 1;
2542192934Srmacklem		}
254374462Salfredskip:
254475641Siedowse		if (ai != NULL)
254574462Salfred			ai = ai->ai_next;
254675641Siedowse		if (ai == NULL)
25471558Srgrimes			done = TRUE;
25481558Srgrimes	}
25491558Srgrimes	if (cp)
25501558Srgrimes		*cp = savedc;
2551158857Srodrigcerror_exit:
2552158857Srodrigc	/* free strings allocated by strdup() in getmntopts.c */
2553158857Srodrigc	if (iov != NULL) {
2554158857Srodrigc		free(iov[0].iov_base); /* fstype */
2555158857Srodrigc		free(iov[2].iov_base); /* fspath */
2556158857Srodrigc		free(iov[4].iov_base); /* from */
2557158857Srodrigc		free(iov[6].iov_base); /* update */
2558158857Srodrigc		free(iov[8].iov_base); /* export */
2559158857Srodrigc		free(iov[10].iov_base); /* errmsg */
2560158857Srodrigc
2561158857Srodrigc		/* free iov, allocated by realloc() */
2562158857Srodrigc		free(iov);
2563158857Srodrigc	}
2564158857Srodrigc	return (ret);
25651558Srgrimes}
25661558Srgrimes
25671558Srgrimes/*
25681558Srgrimes * Translate a net address.
256975801Siedowse *
257075801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
25711558Srgrimes */
25721558Srgrimesint
2573216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg)
25741558Srgrimes{
257575861Siedowse	struct netent *np = NULL;
257674462Salfred	char *name, *p, *prefp;
257775801Siedowse	struct sockaddr_in sin;
257875861Siedowse	struct sockaddr *sa = NULL;
257974462Salfred	struct addrinfo hints, *ai = NULL;
258074462Salfred	char netname[NI_MAXHOST];
258174462Salfred	long preflen;
25821558Srgrimes
258375635Siedowse	p = prefp = NULL;
258474462Salfred	if ((opt_flags & OP_MASKLEN) && !maskflg) {
258574462Salfred		p = strchr(cp, '/');
258674462Salfred		*p = '\0';
258774462Salfred		prefp = p + 1;
258874462Salfred	}
258974462Salfred
259075861Siedowse	/*
259175861Siedowse	 * Check for a numeric address first. We wish to avoid
259275861Siedowse	 * possible DNS lookups in getnetbyname().
259375861Siedowse	 */
259475861Siedowse	if (isxdigit(*cp) || *cp == ':') {
259574462Salfred		memset(&hints, 0, sizeof hints);
259675801Siedowse		/* Ensure the mask and the network have the same family. */
259775801Siedowse		if (maskflg && (opt_flags & OP_NET))
259875801Siedowse			hints.ai_family = net->nt_net.ss_family;
259975801Siedowse		else if (!maskflg && (opt_flags & OP_HAVEMASK))
260075801Siedowse			hints.ai_family = net->nt_mask.ss_family;
260175801Siedowse		else
260275801Siedowse			hints.ai_family = AF_UNSPEC;
260374462Salfred		hints.ai_flags = AI_NUMERICHOST;
260475861Siedowse		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
260575861Siedowse			sa = ai->ai_addr;
260675861Siedowse		if (sa != NULL && ai->ai_family == AF_INET) {
260774462Salfred			/*
260875801Siedowse			 * The address in `cp' is really a network address, so
260975801Siedowse			 * use inet_network() to re-interpret this correctly.
261075801Siedowse			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
261174462Salfred			 */
261275801Siedowse			bzero(&sin, sizeof sin);
261374462Salfred			sin.sin_family = AF_INET;
261474462Salfred			sin.sin_len = sizeof sin;
261575801Siedowse			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
261674462Salfred			if (debug)
261775801Siedowse				fprintf(stderr, "get_net: v4 addr %s\n",
261875801Siedowse				    inet_ntoa(sin.sin_addr));
261974462Salfred			sa = (struct sockaddr *)&sin;
262075861Siedowse		}
262175861Siedowse	}
262275861Siedowse	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
262375861Siedowse		bzero(&sin, sizeof sin);
262475861Siedowse		sin.sin_family = AF_INET;
262575861Siedowse		sin.sin_len = sizeof sin;
262675861Siedowse		sin.sin_addr = inet_makeaddr(np->n_net, 0);
262775861Siedowse		sa = (struct sockaddr *)&sin;
262875861Siedowse	}
262975861Siedowse	if (sa == NULL)
263074462Salfred		goto fail;
263125318Spst
263275801Siedowse	if (maskflg) {
263375801Siedowse		/* The specified sockaddr is a mask. */
263475801Siedowse		if (checkmask(sa) != 0)
263575801Siedowse			goto fail;
263675801Siedowse		bcopy(sa, &net->nt_mask, sa->sa_len);
263775801Siedowse		opt_flags |= OP_HAVEMASK;
263875801Siedowse	} else {
263975801Siedowse		/* The specified sockaddr is a network address. */
264075801Siedowse		bcopy(sa, &net->nt_net, sa->sa_len);
264174462Salfred
264275801Siedowse		/* Get a network name for the export list. */
264375801Siedowse		if (np) {
264475801Siedowse			name = np->n_name;
264575801Siedowse		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2646146187Sume		   NULL, 0, NI_NUMERICHOST) == 0) {
264775801Siedowse			name = netname;
264875801Siedowse		} else {
264975801Siedowse			goto fail;
265075801Siedowse		}
265175801Siedowse		if ((net->nt_name = strdup(name)) == NULL)
265275801Siedowse			out_of_mem();
265375801Siedowse
265475801Siedowse		/*
265575801Siedowse		 * Extract a mask from either a "/<masklen>" suffix, or
265675801Siedowse		 * from the class of an IPv4 address.
265775801Siedowse		 */
265874462Salfred		if (opt_flags & OP_MASKLEN) {
265974462Salfred			preflen = strtol(prefp, NULL, 10);
266075801Siedowse			if (preflen < 0L || preflen == LONG_MAX)
266174462Salfred				goto fail;
266275801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
266375801Siedowse			if (makemask(&net->nt_mask, (int)preflen) != 0)
266475801Siedowse				goto fail;
266575801Siedowse			opt_flags |= OP_HAVEMASK;
266674462Salfred			*p = '/';
266775801Siedowse		} else if (sa->sa_family == AF_INET &&
266875801Siedowse		    (opt_flags & OP_MASK) == 0) {
266975801Siedowse			in_addr_t addr;
267074462Salfred
267175801Siedowse			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
267275801Siedowse			if (IN_CLASSA(addr))
267375801Siedowse				preflen = 8;
267475801Siedowse			else if (IN_CLASSB(addr))
267575801Siedowse				preflen = 16;
267675801Siedowse			else if (IN_CLASSC(addr))
267775801Siedowse				preflen = 24;
267875801Siedowse			else if (IN_CLASSD(addr))
267975801Siedowse				preflen = 28;
268075801Siedowse			else
268175801Siedowse				preflen = 32;	/* XXX */
268275801Siedowse
268375801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
268475801Siedowse			makemask(&net->nt_mask, (int)preflen);
268575801Siedowse			opt_flags |= OP_HAVEMASK;
268674462Salfred		}
268774462Salfred	}
268874462Salfred
268974462Salfred	if (ai)
269074462Salfred		freeaddrinfo(ai);
269174462Salfred	return 0;
269274462Salfred
269374462Salfredfail:
269474462Salfred	if (ai)
269574462Salfred		freeaddrinfo(ai);
269674462Salfred	return 1;
26971558Srgrimes}
26981558Srgrimes
26991558Srgrimes/*
27001558Srgrimes * Parse out the next white space separated field
27011558Srgrimes */
27021558Srgrimesvoid
2703216587Scharniernextfield(char **cp, char **endcp)
27041558Srgrimes{
27051558Srgrimes	char *p;
27061558Srgrimes
27071558Srgrimes	p = *cp;
27081558Srgrimes	while (*p == ' ' || *p == '\t')
27091558Srgrimes		p++;
27101558Srgrimes	if (*p == '\n' || *p == '\0')
27111558Srgrimes		*cp = *endcp = p;
27121558Srgrimes	else {
27131558Srgrimes		*cp = p++;
27141558Srgrimes		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
27151558Srgrimes			p++;
27161558Srgrimes		*endcp = p;
27171558Srgrimes	}
27181558Srgrimes}
27191558Srgrimes
27201558Srgrimes/*
27211558Srgrimes * Get an exports file line. Skip over blank lines and handle line
27221558Srgrimes * continuations.
27231558Srgrimes */
27241558Srgrimesint
2725216587Scharnierget_line(void)
27261558Srgrimes{
27271558Srgrimes	char *p, *cp;
272896622Siedowse	size_t len;
27291558Srgrimes	int totlen, cont_line;
27301558Srgrimes
27311558Srgrimes	/*
27321558Srgrimes	 * Loop around ignoring blank lines and getting all continuation lines.
27331558Srgrimes	 */
27341558Srgrimes	p = line;
27351558Srgrimes	totlen = 0;
27361558Srgrimes	do {
273796622Siedowse		if ((p = fgetln(exp_file, &len)) == NULL)
27381558Srgrimes			return (0);
27391558Srgrimes		cp = p + len - 1;
27401558Srgrimes		cont_line = 0;
27411558Srgrimes		while (cp >= p &&
27421558Srgrimes		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
27431558Srgrimes			if (*cp == '\\')
27441558Srgrimes				cont_line = 1;
27451558Srgrimes			cp--;
27461558Srgrimes			len--;
27471558Srgrimes		}
274879117Sdd		if (cont_line) {
274979117Sdd			*++cp = ' ';
275079117Sdd			len++;
275179117Sdd		}
275296622Siedowse		if (linesize < len + totlen + 1) {
275396622Siedowse			linesize = len + totlen + 1;
275496622Siedowse			line = realloc(line, linesize);
275596622Siedowse			if (line == NULL)
275696622Siedowse				out_of_mem();
27571558Srgrimes		}
275896622Siedowse		memcpy(line + totlen, p, len);
275996622Siedowse		totlen += len;
276096622Siedowse		line[totlen] = '\0';
27611558Srgrimes	} while (totlen == 0 || cont_line);
27621558Srgrimes	return (1);
27631558Srgrimes}
27641558Srgrimes
27651558Srgrimes/*
27661558Srgrimes * Parse a description of a credential.
27671558Srgrimes */
27681558Srgrimesvoid
2769216587Scharnierparsecred(char *namelist, struct xucred *cr)
27701558Srgrimes{
27711558Srgrimes	char *name;
27721558Srgrimes	int cnt;
27731558Srgrimes	char *names;
27741558Srgrimes	struct passwd *pw;
27751558Srgrimes	struct group *gr;
2776194498Sbrooks	gid_t groups[XU_NGROUPS + 1];
2777136051Sstefanf	int ngroups;
27781558Srgrimes
277991354Sdd	cr->cr_version = XUCRED_VERSION;
27801558Srgrimes	/*
278137663Scharnier	 * Set up the unprivileged user.
27821558Srgrimes	 */
27831558Srgrimes	cr->cr_uid = -2;
27841558Srgrimes	cr->cr_groups[0] = -2;
27851558Srgrimes	cr->cr_ngroups = 1;
27861558Srgrimes	/*
27871558Srgrimes	 * Get the user's password table entry.
27881558Srgrimes	 */
27891558Srgrimes	names = strsep(&namelist, " \t\n");
27901558Srgrimes	name = strsep(&names, ":");
27911558Srgrimes	if (isdigit(*name) || *name == '-')
27921558Srgrimes		pw = getpwuid(atoi(name));
27931558Srgrimes	else
27941558Srgrimes		pw = getpwnam(name);
27951558Srgrimes	/*
27961558Srgrimes	 * Credentials specified as those of a user.
27971558Srgrimes	 */
27981558Srgrimes	if (names == NULL) {
27991558Srgrimes		if (pw == NULL) {
280037663Scharnier			syslog(LOG_ERR, "unknown user: %s", name);
28011558Srgrimes			return;
28021558Srgrimes		}
28031558Srgrimes		cr->cr_uid = pw->pw_uid;
2804194498Sbrooks		ngroups = XU_NGROUPS + 1;
28051558Srgrimes		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
280637663Scharnier			syslog(LOG_ERR, "too many groups");
28071558Srgrimes		/*
2808136051Sstefanf		 * Compress out duplicate.
28091558Srgrimes		 */
28101558Srgrimes		cr->cr_ngroups = ngroups - 1;
28111558Srgrimes		cr->cr_groups[0] = groups[0];
28121558Srgrimes		for (cnt = 2; cnt < ngroups; cnt++)
28131558Srgrimes			cr->cr_groups[cnt - 1] = groups[cnt];
28141558Srgrimes		return;
28151558Srgrimes	}
28161558Srgrimes	/*
28171558Srgrimes	 * Explicit credential specified as a colon separated list:
28181558Srgrimes	 *	uid:gid:gid:...
28191558Srgrimes	 */
28201558Srgrimes	if (pw != NULL)
28211558Srgrimes		cr->cr_uid = pw->pw_uid;
28221558Srgrimes	else if (isdigit(*name) || *name == '-')
28231558Srgrimes		cr->cr_uid = atoi(name);
28241558Srgrimes	else {
282537663Scharnier		syslog(LOG_ERR, "unknown user: %s", name);
28261558Srgrimes		return;
28271558Srgrimes	}
28281558Srgrimes	cr->cr_ngroups = 0;
2829194498Sbrooks	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
28301558Srgrimes		name = strsep(&names, ":");
28311558Srgrimes		if (isdigit(*name) || *name == '-') {
28321558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
28331558Srgrimes		} else {
28341558Srgrimes			if ((gr = getgrnam(name)) == NULL) {
283537663Scharnier				syslog(LOG_ERR, "unknown group: %s", name);
28361558Srgrimes				continue;
28371558Srgrimes			}
28381558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
28391558Srgrimes		}
28401558Srgrimes	}
2841194498Sbrooks	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
284237663Scharnier		syslog(LOG_ERR, "too many groups");
28431558Srgrimes}
28441558Srgrimes
2845194880Sdfr#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
28461558Srgrimes/*
28471558Srgrimes * Routines that maintain the remote mounttab
28481558Srgrimes */
28491558Srgrimesvoid
2850216587Scharnierget_mountlist(void)
28511558Srgrimes{
28521558Srgrimes	struct mountlist *mlp, **mlpp;
285323681Speter	char *host, *dirp, *cp;
28541558Srgrimes	char str[STRSIZ];
28551558Srgrimes	FILE *mlfile;
28561558Srgrimes
28571558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
285853117Sbillf		if (errno == ENOENT)
285953117Sbillf			return;
286053117Sbillf		else {
286153117Sbillf			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
286253117Sbillf			return;
286353117Sbillf		}
28641558Srgrimes	}
28651558Srgrimes	mlpp = &mlhead;
28661558Srgrimes	while (fgets(str, STRSIZ, mlfile) != NULL) {
286723681Speter		cp = str;
286823681Speter		host = strsep(&cp, " \t\n");
286923681Speter		dirp = strsep(&cp, " \t\n");
287023681Speter		if (host == NULL || dirp == NULL)
28711558Srgrimes			continue;
28721558Srgrimes		mlp = (struct mountlist *)malloc(sizeof (*mlp));
287337663Scharnier		if (mlp == (struct mountlist *)NULL)
287437663Scharnier			out_of_mem();
2875194880Sdfr		strncpy(mlp->ml_host, host, MNTNAMLEN);
2876194880Sdfr		mlp->ml_host[MNTNAMLEN] = '\0';
2877194880Sdfr		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2878194880Sdfr		mlp->ml_dirp[MNTPATHLEN] = '\0';
28791558Srgrimes		mlp->ml_next = (struct mountlist *)NULL;
28801558Srgrimes		*mlpp = mlp;
28811558Srgrimes		mlpp = &mlp->ml_next;
28821558Srgrimes	}
28831558Srgrimes	fclose(mlfile);
28841558Srgrimes}
28851558Srgrimes
288675635Siedowsevoid
288775635Siedowsedel_mlist(char *hostp, char *dirp)
28881558Srgrimes{
28891558Srgrimes	struct mountlist *mlp, **mlpp;
28901558Srgrimes	struct mountlist *mlp2;
28911558Srgrimes	FILE *mlfile;
28921558Srgrimes	int fnd = 0;
28931558Srgrimes
28941558Srgrimes	mlpp = &mlhead;
28951558Srgrimes	mlp = mlhead;
28961558Srgrimes	while (mlp) {
28971558Srgrimes		if (!strcmp(mlp->ml_host, hostp) &&
28981558Srgrimes		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
28991558Srgrimes			fnd = 1;
29001558Srgrimes			mlp2 = mlp;
29011558Srgrimes			*mlpp = mlp = mlp->ml_next;
29021558Srgrimes			free((caddr_t)mlp2);
29031558Srgrimes		} else {
29041558Srgrimes			mlpp = &mlp->ml_next;
29051558Srgrimes			mlp = mlp->ml_next;
29061558Srgrimes		}
29071558Srgrimes	}
29081558Srgrimes	if (fnd) {
29091558Srgrimes		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
291037663Scharnier			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
29111558Srgrimes			return;
29121558Srgrimes		}
29131558Srgrimes		mlp = mlhead;
29141558Srgrimes		while (mlp) {
29151558Srgrimes			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
29161558Srgrimes			mlp = mlp->ml_next;
29171558Srgrimes		}
29181558Srgrimes		fclose(mlfile);
29191558Srgrimes	}
29201558Srgrimes}
29211558Srgrimes
29221558Srgrimesvoid
2923216587Scharnieradd_mlist(char *hostp, char *dirp)
29241558Srgrimes{
29251558Srgrimes	struct mountlist *mlp, **mlpp;
29261558Srgrimes	FILE *mlfile;
29271558Srgrimes
29281558Srgrimes	mlpp = &mlhead;
29291558Srgrimes	mlp = mlhead;
29301558Srgrimes	while (mlp) {
29311558Srgrimes		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
29321558Srgrimes			return;
29331558Srgrimes		mlpp = &mlp->ml_next;
29341558Srgrimes		mlp = mlp->ml_next;
29351558Srgrimes	}
29361558Srgrimes	mlp = (struct mountlist *)malloc(sizeof (*mlp));
293737663Scharnier	if (mlp == (struct mountlist *)NULL)
293837663Scharnier		out_of_mem();
2939194880Sdfr	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
2940194880Sdfr	mlp->ml_host[MNTNAMLEN] = '\0';
2941194880Sdfr	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2942194880Sdfr	mlp->ml_dirp[MNTPATHLEN] = '\0';
29431558Srgrimes	mlp->ml_next = (struct mountlist *)NULL;
29441558Srgrimes	*mlpp = mlp;
29451558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
294637663Scharnier		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
29471558Srgrimes		return;
29481558Srgrimes	}
29491558Srgrimes	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
29501558Srgrimes	fclose(mlfile);
29511558Srgrimes}
29521558Srgrimes
29531558Srgrimes/*
29541558Srgrimes * Free up a group list.
29551558Srgrimes */
29561558Srgrimesvoid
2957216587Scharnierfree_grp(struct grouplist *grp)
29581558Srgrimes{
29591558Srgrimes	if (grp->gr_type == GT_HOST) {
296074462Salfred		if (grp->gr_ptr.gt_addrinfo != NULL)
296174462Salfred			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
29621558Srgrimes	} else if (grp->gr_type == GT_NET) {
29631558Srgrimes		if (grp->gr_ptr.gt_net.nt_name)
29641558Srgrimes			free(grp->gr_ptr.gt_net.nt_name);
29651558Srgrimes	}
29661558Srgrimes	free((caddr_t)grp);
29671558Srgrimes}
29681558Srgrimes
29691558Srgrimes#ifdef DEBUG
29701558Srgrimesvoid
29711558SrgrimesSYSLOG(int pri, const char *fmt, ...)
29721558Srgrimes{
29731558Srgrimes	va_list ap;
29741558Srgrimes
29751558Srgrimes	va_start(ap, fmt);
29761558Srgrimes	vfprintf(stderr, fmt, ap);
29771558Srgrimes	va_end(ap);
29781558Srgrimes}
29791558Srgrimes#endif /* DEBUG */
29801558Srgrimes
29811558Srgrimes/*
29821558Srgrimes * Check options for consistency.
29831558Srgrimes */
29841558Srgrimesint
2985216587Scharniercheck_options(struct dirlist *dp)
29861558Srgrimes{
29871558Srgrimes
2988192934Srmacklem	if (v4root_phase == 0 && dp == NULL)
29891558Srgrimes	    return (1);
299083653Speter	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
299183653Speter	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
29921558Srgrimes	    return (1);
29931558Srgrimes	}
29941558Srgrimes	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
299575801Siedowse		syslog(LOG_ERR, "-mask requires -network");
299675801Siedowse		return (1);
29971558Srgrimes	}
299875801Siedowse	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
299975801Siedowse		syslog(LOG_ERR, "-network requires mask specification");
300075801Siedowse		return (1);
300175801Siedowse	}
300275801Siedowse	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
300375801Siedowse		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
300475801Siedowse		return (1);
300575801Siedowse	}
3006192934Srmacklem	if (v4root_phase > 0 &&
3007192934Srmacklem	    (opt_flags &
3008192934Srmacklem	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3009192934Srmacklem	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3010192934Srmacklem	    return (1);
3011192934Srmacklem	}
3012207689Srmacklem	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3013207689Srmacklem	    syslog(LOG_ERR, "-alldirs has multiple directories");
3014207689Srmacklem	    return (1);
3015207689Srmacklem	}
30161558Srgrimes	return (0);
30171558Srgrimes}
30181558Srgrimes
30191558Srgrimes/*
30201558Srgrimes * Check an absolute directory path for any symbolic links. Return true
30211558Srgrimes */
30221558Srgrimesint
3023216587Scharniercheck_dirpath(char *dirp)
30241558Srgrimes{
30251558Srgrimes	char *cp;
30261558Srgrimes	int ret = 1;
30271558Srgrimes	struct stat sb;
30281558Srgrimes
30291558Srgrimes	cp = dirp + 1;
30301558Srgrimes	while (*cp && ret) {
30311558Srgrimes		if (*cp == '/') {
30321558Srgrimes			*cp = '\0';
30339336Sdfr			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
30341558Srgrimes				ret = 0;
30351558Srgrimes			*cp = '/';
30361558Srgrimes		}
30371558Srgrimes		cp++;
30381558Srgrimes	}
30399336Sdfr	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
30401558Srgrimes		ret = 0;
30411558Srgrimes	return (ret);
30421558Srgrimes}
30439336Sdfr
304475801Siedowse/*
304575801Siedowse * Make a netmask according to the specified prefix length. The ss_family
304675801Siedowse * and other non-address fields must be initialised before calling this.
304775801Siedowse */
304875801Siedowseint
304975801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen)
305074462Salfred{
305175801Siedowse	u_char *p;
305275801Siedowse	int bits, i, len;
305374462Salfred
305475801Siedowse	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
305575801Siedowse		return (-1);
3056103949Smike	if (bitlen > len * CHAR_BIT)
305775801Siedowse		return (-1);
305874462Salfred
305975801Siedowse	for (i = 0; i < len; i++) {
3060103949Smike		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
3061219125Sru		*p++ = (u_char)~0 << (CHAR_BIT - bits);
306275801Siedowse		bitlen -= bits;
306374462Salfred	}
306475801Siedowse	return 0;
306574462Salfred}
306674462Salfred
306775801Siedowse/*
306875801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask
306975801Siedowse * is acceptable (i.e. of the form 1...10....0).
307075801Siedowse */
307175801Siedowseint
307275801Siedowsecheckmask(struct sockaddr *sa)
307374462Salfred{
307475801Siedowse	u_char *mask;
307575801Siedowse	int i, len;
307674462Salfred
307775801Siedowse	if ((mask = sa_rawaddr(sa, &len)) == NULL)
307875801Siedowse		return (-1);
307975801Siedowse
308075801Siedowse	for (i = 0; i < len; i++)
308175801Siedowse		if (mask[i] != 0xff)
308275801Siedowse			break;
308375801Siedowse	if (i < len) {
308475801Siedowse		if (~mask[i] & (u_char)(~mask[i] + 1))
308575801Siedowse			return (-1);
308675801Siedowse		i++;
308774462Salfred	}
308875801Siedowse	for (; i < len; i++)
308975801Siedowse		if (mask[i] != 0)
309075801Siedowse			return (-1);
309175801Siedowse	return (0);
309274462Salfred}
309374462Salfred
309475801Siedowse/*
309575801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if
309675801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'.
309775801Siedowse * If samask is NULL, perform a full comparision.
309875801Siedowse */
309975801Siedowseint
310075801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
310174462Salfred{
310275801Siedowse	unsigned char *p1, *p2, *mask;
310375801Siedowse	int len, i;
310474462Salfred
310575801Siedowse	if (sa1->sa_family != sa2->sa_family ||
310675801Siedowse	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
310775801Siedowse	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
310875801Siedowse		return (1);
310975801Siedowse
311075801Siedowse	switch (sa1->sa_family) {
311174462Salfred	case AF_INET6:
311275801Siedowse		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
311375801Siedowse		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
311475801Siedowse			return (1);
311574462Salfred		break;
311674462Salfred	}
311774462Salfred
311875801Siedowse	/* Simple binary comparison if no mask specified. */
311975801Siedowse	if (samask == NULL)
312075801Siedowse		return (memcmp(p1, p2, len));
312174462Salfred
312275801Siedowse	/* Set up the mask, and do a mask-based comparison. */
312375801Siedowse	if (sa1->sa_family != samask->sa_family ||
312475801Siedowse	    (mask = sa_rawaddr(samask, NULL)) == NULL)
312575801Siedowse		return (1);
312674462Salfred
312775801Siedowse	for (i = 0; i < len; i++)
312875801Siedowse		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
312975801Siedowse			return (1);
313075801Siedowse	return (0);
313174462Salfred}
313274462Salfred
313375801Siedowse/*
313475801Siedowse * Return a pointer to the part of the sockaddr that contains the
313575801Siedowse * raw address, and set *nbytes to its length in bytes. Returns
313675801Siedowse * NULL if the address family is unknown.
313775801Siedowse */
313875801Siedowsevoid *
313975801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) {
314075801Siedowse	void *p;
314174462Salfred	int len;
314274462Salfred
314375801Siedowse	switch (sa->sa_family) {
314474462Salfred	case AF_INET:
314575801Siedowse		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
314675801Siedowse		p = &((struct sockaddr_in *)sa)->sin_addr;
314774462Salfred		break;
314874462Salfred	case AF_INET6:
314975801Siedowse		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
315075801Siedowse		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
315174462Salfred		break;
315274462Salfred	default:
315375801Siedowse		p = NULL;
315475801Siedowse		len = 0;
315574462Salfred	}
315674462Salfred
315775801Siedowse	if (nbytes != NULL)
315875801Siedowse		*nbytes = len;
315975801Siedowse	return (p);
316074462Salfred}
316174462Salfred
316275754Siedowsevoid
3163216587Scharnierhuphandler(int sig __unused)
316475754Siedowse{
316575754Siedowse	got_sighup = 1;
316675754Siedowse}
316775754Siedowse
3168216587Scharniervoid terminate(int sig __unused)
316974462Salfred{
3170149433Spjd	pidfile_remove(pfh);
3171194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3172194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
317374462Salfred	exit (0);
317474462Salfred}
3175192934Srmacklem
3176