mountd.c revision 194880
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 194880 2009-06-24 18:42:21Z dfr $");
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
1611558Srgrimes/* Global defs */
16292882Simpchar	*add_expdir(struct dirlist **, char *, int);
16392882Simpvoid	add_dlist(struct dirlist **, struct dirlist *,
16492882Simp				struct grouplist *, int);
16592882Simpvoid	add_mlist(char *, char *);
16692882Simpint	check_dirpath(char *);
16792882Simpint	check_options(struct dirlist *);
16875801Siedowseint	checkmask(struct sockaddr *sa);
16992882Simpint	chk_host(struct dirlist *, struct sockaddr *, int *, int *);
170172827Smatteovoid	create_service(struct netconfig *nconf);
17175635Siedowsevoid	del_mlist(char *hostp, char *dirp);
17292882Simpstruct dirlist *dirp_search(struct dirlist *, char *);
17392882Simpint	do_mount(struct exportlist *, struct grouplist *, int,
17492882Simp		struct xucred *, char *, int, struct statfs *);
17592882Simpint	do_opt(char **, char **, struct exportlist *, struct grouplist *,
17692882Simp				int *, int *, struct xucred *);
17792882Simpstruct	exportlist *ex_search(fsid_t *);
17892882Simpstruct	exportlist *get_exp(void);
17992882Simpvoid	free_dir(struct dirlist *);
18092882Simpvoid	free_exp(struct exportlist *);
18192882Simpvoid	free_grp(struct grouplist *);
18292882Simpvoid	free_host(struct hostlist *);
18392882Simpvoid	get_exportlist(void);
18492882Simpint	get_host(char *, struct grouplist *, struct grouplist *);
18592882Simpstruct hostlist *get_ht(void);
18692882Simpint	get_line(void);
18792882Simpvoid	get_mountlist(void);
18892882Simpint	get_net(char *, struct netmsk *, int);
18992882Simpvoid	getexp_err(struct exportlist *, struct grouplist *);
19092882Simpstruct grouplist *get_grp(void);
19192882Simpvoid	hang_dirp(struct dirlist *, struct grouplist *,
19292882Simp				struct exportlist *, int);
19375754Siedowsevoid	huphandler(int sig);
19475801Siedowseint	makemask(struct sockaddr_storage *ssp, int bitlen);
19592882Simpvoid	mntsrv(struct svc_req *, SVCXPRT *);
19692882Simpvoid	nextfield(char **, char **);
19792882Simpvoid	out_of_mem(void);
19892882Simpvoid	parsecred(char *, struct xucred *);
199100117Salfredint	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
20075801Siedowsevoid	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
20175801Siedowseint	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
20275801Siedowse    struct sockaddr *samask);
20392882Simpint	scan_tree(struct dirlist *, struct sockaddr *);
20492882Simpstatic void usage(void);
20592882Simpint	xdr_dir(XDR *, char *);
20692882Simpint	xdr_explist(XDR *, caddr_t);
207100117Salfredint	xdr_explist_brief(XDR *, caddr_t);
20892882Simpint	xdr_fhs(XDR *, caddr_t);
20992882Simpint	xdr_mlist(XDR *, caddr_t);
21092882Simpvoid	terminate(int);
2111558Srgrimes
2121558Srgrimesstruct exportlist *exphead;
2131558Srgrimesstruct mountlist *mlhead;
2141558Srgrimesstruct grouplist *grphead;
215166440Spjdchar *exnames_default[2] = { _PATH_EXPORTS, NULL };
216166440Spjdchar **exnames;
217172827Smatteochar **hosts = NULL;
21872650Sgreenstruct xucred def_anon = {
21991354Sdd	XUCRED_VERSION,
22072650Sgreen	(uid_t)-2,
2211558Srgrimes	1,
22272650Sgreen	{ (gid_t)-2 },
22372650Sgreen	NULL
2241558Srgrimes};
22525087Sdfrint force_v2 = 0;
2269336Sdfrint resvport_only = 1;
227172827Smatteoint nhosts = 0;
2289336Sdfrint dir_only = 1;
229121767Speterint dolog = 0;
23075754Siedowseint got_sighup = 0;
231172827Smatteoint xcreated = 0;
23274462Salfred
233172827Smatteochar *svcport_str = NULL;
234172827Smatteo
2351558Srgrimesint opt_flags;
23674462Salfredstatic int have_v6 = 1;
23774462Salfred
238192934Srmacklemint v4root_phase = 0;
239192934Srmacklemchar v4root_dirpath[PATH_MAX + 1];
240192934Srmacklemint run_v4server = 0;
241192934Srmacklemint has_publicfh = 0;
242192934Srmacklem
243149433Spjdstruct pidfh *pfh = NULL;
24475801Siedowse/* Bits for opt_flags above */
2451558Srgrimes#define	OP_MAPROOT	0x01
2461558Srgrimes#define	OP_MAPALL	0x02
24783653Speter/* 0x4 free */
2481558Srgrimes#define	OP_MASK		0x08
2491558Srgrimes#define	OP_NET		0x10
2501558Srgrimes#define	OP_ALLDIRS	0x40
25175801Siedowse#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
252100336Sjoerg#define	OP_QUIET	0x100
25374462Salfred#define OP_MASKLEN	0x200
254184588Sdfr#define OP_SEC		0x400
2551558Srgrimes
2561558Srgrimes#ifdef DEBUG
2571558Srgrimesint debug = 1;
25892882Simpvoid	SYSLOG(int, const char *, ...) __printflike(2, 3);
2591558Srgrimes#define syslog SYSLOG
2601558Srgrimes#else
2611558Srgrimesint debug = 0;
2621558Srgrimes#endif
2631558Srgrimes
2641558Srgrimes/*
2651558Srgrimes * Mountd server for NFS mount protocol as described in:
2661558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A
2671558Srgrimes * The optional arguments are the exports file name
2681558Srgrimes * default: _PATH_EXPORTS
2691558Srgrimes * and "-n" to allow nonroot mount.
2701558Srgrimes */
2711558Srgrimesint
2721558Srgrimesmain(argc, argv)
2731558Srgrimes	int argc;
2741558Srgrimes	char **argv;
2751558Srgrimes{
27675754Siedowse	fd_set readfds;
277172827Smatteo	struct netconfig *nconf;
278172827Smatteo	char *endptr, **hosts_bak;
279172827Smatteo	void *nc_handle;
280149433Spjd	pid_t otherpid;
281172827Smatteo	in_port_t svcport;
282172827Smatteo	int c, k, s;
283109363Smbr	int maxrec = RPC_MAXDATASIZE;
2841558Srgrimes
28574462Salfred	/* Check that another mountd isn't already running. */
286150214Spjd	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
287149433Spjd	if (pfh == NULL) {
288149433Spjd		if (errno == EEXIST)
289149433Spjd			errx(1, "mountd already running, pid: %d.", otherpid);
290149433Spjd		warn("cannot open or create pidfile");
291149433Spjd	}
29274462Salfred
29374462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
29474462Salfred	if (s < 0)
29574462Salfred		have_v6 = 0;
29674462Salfred	else
29774462Salfred		close(s);
2982999Swollman
299192993Srmacklem	while ((c = getopt(argc, argv, "2deh:lnp:r")) != -1)
3001558Srgrimes		switch (c) {
30125087Sdfr		case '2':
30225087Sdfr			force_v2 = 1;
30325087Sdfr			break;
304192993Srmacklem		case 'e':
305192934Srmacklem			run_v4server = 1;
306192934Srmacklem			break;
3079336Sdfr		case 'n':
3089336Sdfr			resvport_only = 0;
3099336Sdfr			break;
3109336Sdfr		case 'r':
3119336Sdfr			dir_only = 0;
3129336Sdfr			break;
3138688Sphk		case 'd':
3148688Sphk			debug = debug ? 0 : 1;
3158688Sphk			break;
31631656Sguido		case 'l':
317121767Speter			dolog = 1;
31831656Sguido			break;
319126572Sbms		case 'p':
320126572Sbms			endptr = NULL;
321126572Sbms			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
322126572Sbms			if (endptr == NULL || *endptr != '\0' ||
323126572Sbms			    svcport == 0 || svcport >= IPPORT_MAX)
324126572Sbms				usage();
325172827Smatteo			svcport_str = strdup(optarg);
326126572Sbms			break;
327172827Smatteo		case 'h':
328172827Smatteo			++nhosts;
329172827Smatteo			hosts_bak = hosts;
330172827Smatteo			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
331172827Smatteo			if (hosts_bak == NULL) {
332172827Smatteo				if (hosts != NULL) {
333172827Smatteo					for (k = 0; k < nhosts; k++)
334172827Smatteo						free(hosts[k]);
335172827Smatteo					free(hosts);
336172827Smatteo					out_of_mem();
337172827Smatteo				}
338172827Smatteo			}
339172827Smatteo			hosts = hosts_bak;
340172827Smatteo			hosts[nhosts - 1] = strdup(optarg);
341172827Smatteo			if (hosts[nhosts - 1] == NULL) {
342172827Smatteo				for (k = 0; k < (nhosts - 1); k++)
343172827Smatteo					free(hosts[k]);
344172827Smatteo				free(hosts);
345172827Smatteo				out_of_mem();
346172827Smatteo			}
347172827Smatteo			break;
3481558Srgrimes		default:
34937663Scharnier			usage();
3501558Srgrimes		};
351192934Srmacklem
352192934Srmacklem	/*
353192993Srmacklem	 * If the "-e" option was specified OR only the nfsd module is
354192934Srmacklem	 * found in the server, run "nfsd".
355192934Srmacklem	 * Otherwise, try and run "nfsserver".
356192934Srmacklem	 */
357192934Srmacklem	if (run_v4server > 0) {
358192934Srmacklem		if (modfind("nfsd") < 0) {
359192934Srmacklem			/* Not present in kernel, try loading it */
360192934Srmacklem			if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
361192934Srmacklem				errx(1, "NFS server is not available");
362192934Srmacklem		}
363192934Srmacklem	} else if (modfind("nfsserver") < 0 && modfind("nfsd") >= 0) {
364192934Srmacklem		run_v4server = 1;
365192934Srmacklem	} else if (modfind("nfsserver") < 0) {
366192934Srmacklem		/* Not present in kernel, try loading it */
367192934Srmacklem		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
368192934Srmacklem			errx(1, "NFS server is not available");
369192934Srmacklem	}
370192934Srmacklem
3711558Srgrimes	argc -= optind;
3721558Srgrimes	argv += optind;
3731558Srgrimes	grphead = (struct grouplist *)NULL;
3741558Srgrimes	exphead = (struct exportlist *)NULL;
3751558Srgrimes	mlhead = (struct mountlist *)NULL;
376166440Spjd	if (argc > 0)
377166440Spjd		exnames = argv;
378166440Spjd	else
379166440Spjd		exnames = exnames_default;
3801558Srgrimes	openlog("mountd", LOG_PID, LOG_DAEMON);
3811558Srgrimes	if (debug)
38237663Scharnier		warnx("getting export list");
3831558Srgrimes	get_exportlist();
3841558Srgrimes	if (debug)
38537663Scharnier		warnx("getting mount list");
3861558Srgrimes	get_mountlist();
3871558Srgrimes	if (debug)
38837663Scharnier		warnx("here we go");
3891558Srgrimes	if (debug == 0) {
3901558Srgrimes		daemon(0, 0);
3911558Srgrimes		signal(SIGINT, SIG_IGN);
3921558Srgrimes		signal(SIGQUIT, SIG_IGN);
3931558Srgrimes	}
39475754Siedowse	signal(SIGHUP, huphandler);
39574462Salfred	signal(SIGTERM, terminate);
396164394Srodrigc	signal(SIGPIPE, SIG_IGN);
397149433Spjd
398149433Spjd	pidfile_write(pfh);
399149433Spjd
400194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
401194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
402109363Smbr	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
403109363Smbr
40424759Sguido	if (!resvport_only) {
40583687Speter		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
40683687Speter		    &resvport_only, sizeof(resvport_only)) != 0 &&
40783687Speter		    errno != ENOENT) {
40824759Sguido			syslog(LOG_ERR, "sysctl: %m");
40924759Sguido			exit(1);
41024759Sguido		}
41124330Sguido	}
412126572Sbms
413172827Smatteo	/*
414172827Smatteo	 * If no hosts were specified, add a wildcard entry to bind to
415172827Smatteo	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
416172827Smatteo	 * list.
417172827Smatteo	 */
418172827Smatteo	if (nhosts == 0) {
419172827Smatteo		hosts = malloc(sizeof(char**));
420172827Smatteo		if (hosts == NULL)
421172827Smatteo			out_of_mem();
422172827Smatteo		hosts[0] = "*";
423172827Smatteo		nhosts = 1;
424172827Smatteo	} else {
425172827Smatteo		hosts_bak = hosts;
426172827Smatteo		if (have_v6) {
427172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 2) *
428172827Smatteo			    sizeof(char *));
429172827Smatteo			if (hosts_bak == NULL) {
430172827Smatteo				for (k = 0; k < nhosts; k++)
431172827Smatteo					free(hosts[k]);
432172827Smatteo		    		free(hosts);
433172827Smatteo		    		out_of_mem();
434172827Smatteo			} else
435172827Smatteo				hosts = hosts_bak;
436172827Smatteo			nhosts += 2;
437172827Smatteo			hosts[nhosts - 2] = "::1";
438172827Smatteo		} else {
439172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
440172827Smatteo			if (hosts_bak == NULL) {
441172827Smatteo				for (k = 0; k < nhosts; k++)
442172827Smatteo					free(hosts[k]);
443172827Smatteo				free(hosts);
444172827Smatteo				out_of_mem();
445172827Smatteo			} else {
446172827Smatteo				nhosts += 1;
447172827Smatteo				hosts = hosts_bak;
448126572Sbms			}
449172827Smatteo		}
45074462Salfred
451172827Smatteo		hosts[nhosts - 1] = "127.0.0.1";
45274462Salfred	}
45374462Salfred
454172827Smatteo	nc_handle = setnetconfig();
455172827Smatteo	while ((nconf = getnetconfig(nc_handle))) {
456172827Smatteo		if (nconf->nc_flag & NC_VISIBLE) {
457172827Smatteo			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
458172827Smatteo			    "inet6") == 0) {
459172827Smatteo				/* DO NOTHING */
460172827Smatteo			} else
461172827Smatteo				create_service(nconf);
462172827Smatteo		}
46374462Salfred	}
464172827Smatteo	endnetconfig(nc_handle);
46574462Salfred
46674462Salfred	if (xcreated == 0) {
46774462Salfred		syslog(LOG_ERR, "could not create any services");
4681558Srgrimes		exit(1);
4691558Srgrimes	}
47075754Siedowse
47175754Siedowse	/* Expand svc_run() here so that we can call get_exportlist(). */
47275754Siedowse	for (;;) {
47375754Siedowse		if (got_sighup) {
47475754Siedowse			get_exportlist();
47575754Siedowse			got_sighup = 0;
47675754Siedowse		}
47775754Siedowse		readfds = svc_fdset;
47875754Siedowse		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
47975754Siedowse		case -1:
48075754Siedowse			if (errno == EINTR)
48175754Siedowse                                continue;
48275754Siedowse			syslog(LOG_ERR, "mountd died: select: %m");
48375754Siedowse			exit(1);
48475754Siedowse		case 0:
48575754Siedowse			continue;
48675754Siedowse		default:
48775754Siedowse			svc_getreqset(&readfds);
48875754Siedowse		}
48975754Siedowse	}
490172827Smatteo}
491172827Smatteo
492172827Smatteo/*
493172827Smatteo * This routine creates and binds sockets on the appropriate
494172827Smatteo * addresses. It gets called one time for each transport and
495172827Smatteo * registrates the service with rpcbind on that trasport.
496172827Smatteo */
497172827Smatteovoid
498172827Smatteocreate_service(struct netconfig *nconf)
499172827Smatteo{
500172827Smatteo	struct addrinfo hints, *res = NULL;
501172827Smatteo	struct sockaddr_in *sin;
502172827Smatteo	struct sockaddr_in6 *sin6;
503172827Smatteo	struct __rpc_sockinfo si;
504172827Smatteo	struct netbuf servaddr;
505172827Smatteo	SVCXPRT	*transp = NULL;
506172827Smatteo	int aicode;
507172827Smatteo	int fd;
508172827Smatteo	int nhostsbak;
509172827Smatteo	int one = 1;
510172827Smatteo	int r;
511172827Smatteo	int registered = 0;
512172827Smatteo	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
513172827Smatteo
514172827Smatteo	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
515172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS) &&
516172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
517172827Smatteo		return;	/* not my type */
518172827Smatteo
519172827Smatteo	/*
520172827Smatteo	 * XXX - using RPC library internal functions.
521172827Smatteo	 */
522172827Smatteo	if (!__rpc_nconf2sockinfo(nconf, &si)) {
523172827Smatteo		syslog(LOG_ERR, "cannot get information for %s",
524172827Smatteo		    nconf->nc_netid);
525172827Smatteo		return;
526172827Smatteo	}
527172827Smatteo
528172827Smatteo	/* Get mountd's address on this transport */
529172827Smatteo	memset(&hints, 0, sizeof hints);
530172827Smatteo	hints.ai_flags = AI_PASSIVE;
531172827Smatteo	hints.ai_family = si.si_af;
532172827Smatteo	hints.ai_socktype = si.si_socktype;
533172827Smatteo	hints.ai_protocol = si.si_proto;
534172827Smatteo
535172827Smatteo	/*
536172827Smatteo	 * Bind to specific IPs if asked to
537172827Smatteo	 */
538172827Smatteo	nhostsbak = nhosts;
539172827Smatteo	while (nhostsbak > 0) {
540172827Smatteo		--nhostsbak;
541172827Smatteo		/*
542172827Smatteo		 * XXX - using RPC library internal functions.
543172827Smatteo		 */
544172827Smatteo		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
545172827Smatteo			int non_fatal = 0;
546172827Smatteo	    		if (errno == EPROTONOSUPPORT &&
547172827Smatteo			    nconf->nc_semantics != NC_TPI_CLTS)
548172827Smatteo				non_fatal = 1;
549172827Smatteo
550172827Smatteo			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
551172827Smatteo			    "cannot create socket for %s", nconf->nc_netid);
552172827Smatteo	    		return;
553172827Smatteo		}
554172827Smatteo
555172827Smatteo		switch (hints.ai_family) {
556172827Smatteo		case AF_INET:
557172827Smatteo			if (inet_pton(AF_INET, hosts[nhostsbak],
558172827Smatteo			    host_addr) == 1) {
559172827Smatteo				hints.ai_flags &= AI_NUMERICHOST;
560172827Smatteo			} else {
561172827Smatteo				/*
562172827Smatteo				 * Skip if we have an AF_INET6 address.
563172827Smatteo				 */
564172827Smatteo				if (inet_pton(AF_INET6, hosts[nhostsbak],
565172827Smatteo				    host_addr) == 1) {
566172827Smatteo					close(fd);
567172827Smatteo					continue;
568172827Smatteo				}
569172827Smatteo			}
570172827Smatteo			break;
571172827Smatteo		case AF_INET6:
572172827Smatteo			if (inet_pton(AF_INET6, hosts[nhostsbak],
573172827Smatteo			    host_addr) == 1) {
574172827Smatteo				hints.ai_flags &= AI_NUMERICHOST;
575172827Smatteo			} else {
576172827Smatteo				/*
577172827Smatteo				 * Skip if we have an AF_INET address.
578172827Smatteo				 */
579172827Smatteo				if (inet_pton(AF_INET, hosts[nhostsbak],
580172827Smatteo				    host_addr) == 1) {
581172827Smatteo					close(fd);
582172827Smatteo					continue;
583172827Smatteo				}
584172827Smatteo			}
585172827Smatteo
586172827Smatteo			/*
587172827Smatteo			 * We're doing host-based access checks here, so don't
588172827Smatteo			 * allow v4-in-v6 to confuse things. The kernel will
589172827Smatteo			 * disable it by default on NFS sockets too.
590172827Smatteo			 */
591172827Smatteo			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
592172827Smatteo			    sizeof one) < 0) {
593172827Smatteo				syslog(LOG_ERR,
594172827Smatteo				    "can't disable v4-in-v6 on IPv6 socket");
595172827Smatteo				exit(1);
596172827Smatteo			}
597172827Smatteo			break;
598172827Smatteo		default:
599172827Smatteo			break;
600172827Smatteo		}
601172827Smatteo
602172827Smatteo		/*
603172827Smatteo		 * If no hosts were specified, just bind to INADDR_ANY
604172827Smatteo		 */
605172827Smatteo		if (strcmp("*", hosts[nhostsbak]) == 0) {
606172827Smatteo			if (svcport_str == NULL) {
607172827Smatteo				res = malloc(sizeof(struct addrinfo));
608172827Smatteo				if (res == NULL)
609172827Smatteo					out_of_mem();
610172827Smatteo				res->ai_flags = hints.ai_flags;
611172827Smatteo				res->ai_family = hints.ai_family;
612172827Smatteo				res->ai_protocol = hints.ai_protocol;
613172827Smatteo				switch (res->ai_family) {
614172827Smatteo				case AF_INET:
615172827Smatteo					sin = malloc(sizeof(struct sockaddr_in));
616172827Smatteo					if (sin == NULL)
617172827Smatteo						out_of_mem();
618172827Smatteo					sin->sin_family = AF_INET;
619172827Smatteo					sin->sin_port = htons(0);
620172827Smatteo					sin->sin_addr.s_addr = htonl(INADDR_ANY);
621172827Smatteo					res->ai_addr = (struct sockaddr*) sin;
622172827Smatteo					res->ai_addrlen = (socklen_t)
623172827Smatteo					    sizeof(res->ai_addr);
624172827Smatteo					break;
625172827Smatteo				case AF_INET6:
626172827Smatteo					sin6 = malloc(sizeof(struct sockaddr_in6));
627173056Ssimon					if (sin6 == NULL)
628172827Smatteo						out_of_mem();
629172827Smatteo					sin6->sin6_family = AF_INET6;
630172827Smatteo					sin6->sin6_port = htons(0);
631172827Smatteo					sin6->sin6_addr = in6addr_any;
632172827Smatteo					res->ai_addr = (struct sockaddr*) sin6;
633172827Smatteo					res->ai_addrlen = (socklen_t)
634172827Smatteo					    sizeof(res->ai_addr);
635172827Smatteo						break;
636172827Smatteo				default:
637172827Smatteo					break;
638172827Smatteo				}
639172827Smatteo			} else {
640172827Smatteo				if ((aicode = getaddrinfo(NULL, svcport_str,
641172827Smatteo				    &hints, &res)) != 0) {
642172827Smatteo					syslog(LOG_ERR,
643172827Smatteo					    "cannot get local address for %s: %s",
644172827Smatteo					    nconf->nc_netid,
645172827Smatteo					    gai_strerror(aicode));
646172827Smatteo					continue;
647172827Smatteo				}
648172827Smatteo			}
649172827Smatteo		} else {
650172827Smatteo			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
651172827Smatteo			    &hints, &res)) != 0) {
652172827Smatteo				syslog(LOG_ERR,
653172827Smatteo				    "cannot get local address for %s: %s",
654172827Smatteo				    nconf->nc_netid, gai_strerror(aicode));
655172827Smatteo				continue;
656172827Smatteo			}
657172827Smatteo		}
658172827Smatteo
659172827Smatteo		r = bindresvport_sa(fd, res->ai_addr);
660172827Smatteo		if (r != 0) {
661172827Smatteo			syslog(LOG_ERR, "bindresvport_sa: %m");
662172827Smatteo			exit(1);
663172827Smatteo		}
664172827Smatteo
665172827Smatteo		if (nconf->nc_semantics != NC_TPI_CLTS)
666172827Smatteo			listen(fd, SOMAXCONN);
667172827Smatteo
668172827Smatteo		if (nconf->nc_semantics == NC_TPI_CLTS )
669172827Smatteo			transp = svc_dg_create(fd, 0, 0);
670172827Smatteo		else
671172827Smatteo			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
672172827Smatteo			    RPC_MAXDATASIZE);
673172827Smatteo
674172827Smatteo		if (transp != (SVCXPRT *) NULL) {
675194880Sdfr			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
676172827Smatteo			    NULL))
677172827Smatteo				syslog(LOG_ERR,
678194880Sdfr				    "can't register %s MOUNTVERS service",
679172827Smatteo				    nconf->nc_netid);
680172827Smatteo			if (!force_v2) {
681194880Sdfr				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
682172827Smatteo				    mntsrv, NULL))
683172827Smatteo					syslog(LOG_ERR,
684194880Sdfr					    "can't register %s MOUNTVERS3 service",
685172827Smatteo					    nconf->nc_netid);
686172827Smatteo			}
687172827Smatteo		} else
688172827Smatteo			syslog(LOG_WARNING, "can't create %s services",
689172827Smatteo			    nconf->nc_netid);
690172827Smatteo
691172827Smatteo		if (registered == 0) {
692172827Smatteo			registered = 1;
693172827Smatteo			memset(&hints, 0, sizeof hints);
694172827Smatteo			hints.ai_flags = AI_PASSIVE;
695172827Smatteo			hints.ai_family = si.si_af;
696172827Smatteo			hints.ai_socktype = si.si_socktype;
697172827Smatteo			hints.ai_protocol = si.si_proto;
698172827Smatteo
699172827Smatteo			if (svcport_str == NULL) {
700172827Smatteo				svcport_str = malloc(NI_MAXSERV * sizeof(char));
701172827Smatteo				if (svcport_str == NULL)
702172827Smatteo					out_of_mem();
703172827Smatteo
704172827Smatteo				if (getnameinfo(res->ai_addr,
705172827Smatteo				    res->ai_addr->sa_len, NULL, NI_MAXHOST,
706172827Smatteo				    svcport_str, NI_MAXSERV * sizeof(char),
707172827Smatteo				    NI_NUMERICHOST | NI_NUMERICSERV))
708172827Smatteo					errx(1, "Cannot get port number");
709172827Smatteo			}
710172827Smatteo
711172827Smatteo			if((aicode = getaddrinfo(NULL, svcport_str, &hints,
712172827Smatteo			    &res)) != 0) {
713172827Smatteo				syslog(LOG_ERR, "cannot get local address: %s",
714172827Smatteo				    gai_strerror(aicode));
715172827Smatteo				exit(1);
716172827Smatteo			}
717172827Smatteo
718172827Smatteo			servaddr.buf = malloc(res->ai_addrlen);
719172827Smatteo			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
720172827Smatteo			servaddr.len = res->ai_addrlen;
721172827Smatteo
722194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
723194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
724172827Smatteo
725172827Smatteo			xcreated++;
726172827Smatteo			freeaddrinfo(res);
727172827Smatteo		}
728172827Smatteo	} /* end while */
7291558Srgrimes}
7301558Srgrimes
73137663Scharnierstatic void
73237663Scharnierusage()
73337663Scharnier{
73437663Scharnier	fprintf(stderr,
735192993Srmacklem		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
736172827Smatteo		"[-h <bindip>] [export_file ...]\n");
73737663Scharnier	exit(1);
73837663Scharnier}
73937663Scharnier
7401558Srgrimes/*
7411558Srgrimes * The mount rpc service
7421558Srgrimes */
7431558Srgrimesvoid
7441558Srgrimesmntsrv(rqstp, transp)
7451558Srgrimes	struct svc_req *rqstp;
7461558Srgrimes	SVCXPRT *transp;
7471558Srgrimes{
7481558Srgrimes	struct exportlist *ep;
7491558Srgrimes	struct dirlist *dp;
7509336Sdfr	struct fhreturn fhr;
7511558Srgrimes	struct stat stb;
7521558Srgrimes	struct statfs fsb;
75374462Salfred	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
75474462Salfred	int lookup_failed = 1;
75574462Salfred	struct sockaddr *saddr;
7569336Sdfr	u_short sport;
757194880Sdfr	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
75828911Sguido	int bad = 0, defset, hostset;
7599336Sdfr	sigset_t sighup_mask;
7601558Srgrimes
7619336Sdfr	sigemptyset(&sighup_mask);
7629336Sdfr	sigaddset(&sighup_mask, SIGHUP);
76374462Salfred	saddr = svc_getrpccaller(transp)->buf;
76474462Salfred	switch (saddr->sa_family) {
76574462Salfred	case AF_INET6:
76675635Siedowse		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
76774462Salfred		break;
76874462Salfred	case AF_INET:
76975635Siedowse		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
77074462Salfred		break;
77174462Salfred	default:
77274462Salfred		syslog(LOG_ERR, "request from unknown address family");
77374462Salfred		return;
77474462Salfred	}
77574462Salfred	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
77674462Salfred	    NULL, 0, 0);
77774462Salfred	getnameinfo(saddr, saddr->sa_len, numerichost,
77874462Salfred	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
7791558Srgrimes	switch (rqstp->rq_proc) {
7801558Srgrimes	case NULLPROC:
781121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
78237663Scharnier			syslog(LOG_ERR, "can't send reply");
7831558Srgrimes		return;
784194880Sdfr	case MOUNTPROC_MNT:
7859336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
78631656Sguido			syslog(LOG_NOTICE,
78731656Sguido			    "mount request from %s from unprivileged port",
78874462Salfred			    numerichost);
7891558Srgrimes			svcerr_weakauth(transp);
7901558Srgrimes			return;
7911558Srgrimes		}
792121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
79331656Sguido			syslog(LOG_NOTICE, "undecodable mount request from %s",
79474462Salfred			    numerichost);
7951558Srgrimes			svcerr_decode(transp);
7961558Srgrimes			return;
7971558Srgrimes		}
7981558Srgrimes
7991558Srgrimes		/*
8001558Srgrimes		 * Get the real pathname and make sure it is a directory
8019336Sdfr		 * or a regular file if the -r option was specified
8029336Sdfr		 * and it exists.
8031558Srgrimes		 */
80451968Salfred		if (realpath(rpcpath, dirpath) == NULL ||
8051558Srgrimes		    stat(dirpath, &stb) < 0 ||
8069336Sdfr		    (!S_ISDIR(stb.st_mode) &&
80774462Salfred		    (dir_only || !S_ISREG(stb.st_mode))) ||
8081558Srgrimes		    statfs(dirpath, &fsb) < 0) {
8091558Srgrimes			chdir("/");	/* Just in case realpath doesn't */
81031656Sguido			syslog(LOG_NOTICE,
81137663Scharnier			    "mount request from %s for non existent path %s",
81274462Salfred			    numerichost, dirpath);
8131558Srgrimes			if (debug)
81437663Scharnier				warnx("stat failed on %s", dirpath);
81528911Sguido			bad = ENOENT;	/* We will send error reply later */
8161558Srgrimes		}
8171558Srgrimes
8181558Srgrimes		/* Check in the exports list */
8199336Sdfr		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
8201558Srgrimes		ep = ex_search(&fsb.f_fsid);
8219336Sdfr		hostset = defset = 0;
8229336Sdfr		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
8231558Srgrimes		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
82474462Salfred		      chk_host(dp, saddr, &defset, &hostset)) ||
82574462Salfred		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
82674462Salfred		     scan_tree(ep->ex_dirl, saddr) == 0))) {
82728911Sguido			if (bad) {
828121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
82928911Sguido				    (caddr_t)&bad))
83037663Scharnier					syslog(LOG_ERR, "can't send reply");
83128911Sguido				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
83228911Sguido				return;
83328911Sguido			}
8349336Sdfr			if (hostset & DP_HOSTSET)
8359336Sdfr				fhr.fhr_flag = hostset;
8369336Sdfr			else
8379336Sdfr				fhr.fhr_flag = defset;
8389336Sdfr			fhr.fhr_vers = rqstp->rq_vers;
8391558Srgrimes			/* Get the file handle */
84023681Speter			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
8419336Sdfr			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
8421558Srgrimes				bad = errno;
84337663Scharnier				syslog(LOG_ERR, "can't get fh for %s", dirpath);
844121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
8451558Srgrimes				    (caddr_t)&bad))
84637663Scharnier					syslog(LOG_ERR, "can't send reply");
8479336Sdfr				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
8481558Srgrimes				return;
8491558Srgrimes			}
850184588Sdfr			fhr.fhr_numsecflavors = ep->ex_numsecflavors;
851184588Sdfr			fhr.fhr_secflavors = ep->ex_secflavors;
852121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
853121556Speter			    (caddr_t)&fhr))
85437663Scharnier				syslog(LOG_ERR, "can't send reply");
85574462Salfred			if (!lookup_failed)
85674462Salfred				add_mlist(host, dirpath);
8571558Srgrimes			else
85874462Salfred				add_mlist(numerichost, dirpath);
8591558Srgrimes			if (debug)
86037663Scharnier				warnx("mount successful");
861121767Speter			if (dolog)
86231656Sguido				syslog(LOG_NOTICE,
86331656Sguido				    "mount request succeeded from %s for %s",
86474462Salfred				    numerichost, dirpath);
86531656Sguido		} else {
8661558Srgrimes			bad = EACCES;
86731656Sguido			syslog(LOG_NOTICE,
86831656Sguido			    "mount request denied from %s for %s",
86974462Salfred			    numerichost, dirpath);
87031656Sguido		}
87128911Sguido
872121556Speter		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
873121556Speter		    (caddr_t)&bad))
87437663Scharnier			syslog(LOG_ERR, "can't send reply");
8759336Sdfr		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
8761558Srgrimes		return;
877194880Sdfr	case MOUNTPROC_DUMP:
878121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
87937663Scharnier			syslog(LOG_ERR, "can't send reply");
880121767Speter		else if (dolog)
88131656Sguido			syslog(LOG_NOTICE,
88231656Sguido			    "dump request succeeded from %s",
88374462Salfred			    numerichost);
8841558Srgrimes		return;
885194880Sdfr	case MOUNTPROC_UMNT:
8869336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
88731656Sguido			syslog(LOG_NOTICE,
88831656Sguido			    "umount request from %s from unprivileged port",
88974462Salfred			    numerichost);
8901558Srgrimes			svcerr_weakauth(transp);
8911558Srgrimes			return;
8921558Srgrimes		}
893121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
89431656Sguido			syslog(LOG_NOTICE, "undecodable umount request from %s",
89574462Salfred			    numerichost);
8961558Srgrimes			svcerr_decode(transp);
8971558Srgrimes			return;
8981558Srgrimes		}
89951968Salfred		if (realpath(rpcpath, dirpath) == NULL) {
90051968Salfred			syslog(LOG_NOTICE, "umount request from %s "
90151968Salfred			    "for non existent path %s",
90274462Salfred			    numerichost, dirpath);
90351968Salfred		}
904121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
90537663Scharnier			syslog(LOG_ERR, "can't send reply");
90674462Salfred		if (!lookup_failed)
90775635Siedowse			del_mlist(host, dirpath);
90875635Siedowse		del_mlist(numerichost, dirpath);
909121767Speter		if (dolog)
91031656Sguido			syslog(LOG_NOTICE,
91131656Sguido			    "umount request succeeded from %s for %s",
91274462Salfred			    numerichost, dirpath);
9131558Srgrimes		return;
914194880Sdfr	case MOUNTPROC_UMNTALL:
9159336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
91631656Sguido			syslog(LOG_NOTICE,
91731656Sguido			    "umountall request from %s from unprivileged port",
91874462Salfred			    numerichost);
9191558Srgrimes			svcerr_weakauth(transp);
9201558Srgrimes			return;
9211558Srgrimes		}
922121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
92337663Scharnier			syslog(LOG_ERR, "can't send reply");
92474462Salfred		if (!lookup_failed)
92575635Siedowse			del_mlist(host, NULL);
92675635Siedowse		del_mlist(numerichost, NULL);
927121767Speter		if (dolog)
92831656Sguido			syslog(LOG_NOTICE,
92931656Sguido			    "umountall request succeeded from %s",
93074462Salfred			    numerichost);
9311558Srgrimes		return;
932194880Sdfr	case MOUNTPROC_EXPORT:
933121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
934121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
935121556Speter			    (caddr_t)NULL))
936100117Salfred				syslog(LOG_ERR, "can't send reply");
937121767Speter		if (dolog)
93831656Sguido			syslog(LOG_NOTICE,
93931656Sguido			    "export request succeeded from %s",
94074462Salfred			    numerichost);
9411558Srgrimes		return;
9421558Srgrimes	default:
9431558Srgrimes		svcerr_noproc(transp);
9441558Srgrimes		return;
9451558Srgrimes	}
9461558Srgrimes}
9471558Srgrimes
9481558Srgrimes/*
9491558Srgrimes * Xdr conversion for a dirpath string
9501558Srgrimes */
9511558Srgrimesint
9521558Srgrimesxdr_dir(xdrsp, dirp)
9531558Srgrimes	XDR *xdrsp;
9541558Srgrimes	char *dirp;
9551558Srgrimes{
956194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
9571558Srgrimes}
9581558Srgrimes
9591558Srgrimes/*
9609336Sdfr * Xdr routine to generate file handle reply
9611558Srgrimes */
9621558Srgrimesint
9639336Sdfrxdr_fhs(xdrsp, cp)
9641558Srgrimes	XDR *xdrsp;
9659336Sdfr	caddr_t cp;
9661558Srgrimes{
96792806Sobrien	struct fhreturn *fhrp = (struct fhreturn *)cp;
9689336Sdfr	u_long ok = 0, len, auth;
969184588Sdfr	int i;
9701558Srgrimes
9711558Srgrimes	if (!xdr_long(xdrsp, &ok))
9721558Srgrimes		return (0);
9739336Sdfr	switch (fhrp->fhr_vers) {
9749336Sdfr	case 1:
9759336Sdfr		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
9769336Sdfr	case 3:
9779336Sdfr		len = NFSX_V3FH;
9789336Sdfr		if (!xdr_long(xdrsp, &len))
9799336Sdfr			return (0);
9809336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
9819336Sdfr			return (0);
982184588Sdfr		if (fhrp->fhr_numsecflavors) {
983184588Sdfr			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
984184588Sdfr				return (0);
985184588Sdfr			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
986184588Sdfr				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
987184588Sdfr					return (0);
988184588Sdfr			return (1);
989184588Sdfr		} else {
990184588Sdfr			auth = AUTH_SYS;
991184588Sdfr			len = 1;
992184588Sdfr			if (!xdr_long(xdrsp, &len))
993184588Sdfr				return (0);
994184588Sdfr			return (xdr_long(xdrsp, &auth));
995184588Sdfr		}
9969336Sdfr	};
9979336Sdfr	return (0);
9981558Srgrimes}
9991558Srgrimes
10001558Srgrimesint
10011558Srgrimesxdr_mlist(xdrsp, cp)
10021558Srgrimes	XDR *xdrsp;
10031558Srgrimes	caddr_t cp;
10041558Srgrimes{
10051558Srgrimes	struct mountlist *mlp;
10061558Srgrimes	int true = 1;
10071558Srgrimes	int false = 0;
10081558Srgrimes	char *strp;
10091558Srgrimes
10101558Srgrimes	mlp = mlhead;
10111558Srgrimes	while (mlp) {
10121558Srgrimes		if (!xdr_bool(xdrsp, &true))
10131558Srgrimes			return (0);
10141558Srgrimes		strp = &mlp->ml_host[0];
1015194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
10161558Srgrimes			return (0);
10171558Srgrimes		strp = &mlp->ml_dirp[0];
1018194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
10191558Srgrimes			return (0);
10201558Srgrimes		mlp = mlp->ml_next;
10211558Srgrimes	}
10221558Srgrimes	if (!xdr_bool(xdrsp, &false))
10231558Srgrimes		return (0);
10241558Srgrimes	return (1);
10251558Srgrimes}
10261558Srgrimes
10271558Srgrimes/*
10281558Srgrimes * Xdr conversion for export list
10291558Srgrimes */
10301558Srgrimesint
1031100117Salfredxdr_explist_common(xdrsp, cp, brief)
10321558Srgrimes	XDR *xdrsp;
10331558Srgrimes	caddr_t cp;
1034100117Salfred	int brief;
10351558Srgrimes{
10361558Srgrimes	struct exportlist *ep;
10371558Srgrimes	int false = 0;
10389336Sdfr	int putdef;
10399336Sdfr	sigset_t sighup_mask;
10401558Srgrimes
10419336Sdfr	sigemptyset(&sighup_mask);
10429336Sdfr	sigaddset(&sighup_mask, SIGHUP);
10439336Sdfr	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
10441558Srgrimes	ep = exphead;
10451558Srgrimes	while (ep) {
10461558Srgrimes		putdef = 0;
1047100117Salfred		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1048100117Salfred			       &putdef, brief))
10491558Srgrimes			goto errout;
10501558Srgrimes		if (ep->ex_defdir && putdef == 0 &&
10511558Srgrimes			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1052100117Salfred			&putdef, brief))
10531558Srgrimes			goto errout;
10541558Srgrimes		ep = ep->ex_next;
10551558Srgrimes	}
10569336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
10571558Srgrimes	if (!xdr_bool(xdrsp, &false))
10581558Srgrimes		return (0);
10591558Srgrimes	return (1);
10601558Srgrimeserrout:
10619336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
10621558Srgrimes	return (0);
10631558Srgrimes}
10641558Srgrimes
10651558Srgrimes/*
10661558Srgrimes * Called from xdr_explist() to traverse the tree and export the
10671558Srgrimes * directory paths.
10681558Srgrimes */
10691558Srgrimesint
1070100117Salfredput_exlist(dp, xdrsp, adp, putdefp, brief)
10711558Srgrimes	struct dirlist *dp;
10721558Srgrimes	XDR *xdrsp;
10731558Srgrimes	struct dirlist *adp;
10741558Srgrimes	int *putdefp;
1075100117Salfred	int brief;
10761558Srgrimes{
10771558Srgrimes	struct grouplist *grp;
10781558Srgrimes	struct hostlist *hp;
10791558Srgrimes	int true = 1;
10801558Srgrimes	int false = 0;
10811558Srgrimes	int gotalldir = 0;
10821558Srgrimes	char *strp;
10831558Srgrimes
10841558Srgrimes	if (dp) {
1085100117Salfred		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
10861558Srgrimes			return (1);
10871558Srgrimes		if (!xdr_bool(xdrsp, &true))
10881558Srgrimes			return (1);
10891558Srgrimes		strp = dp->dp_dirp;
1090194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
10911558Srgrimes			return (1);
10921558Srgrimes		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
10931558Srgrimes			gotalldir = 1;
10941558Srgrimes			*putdefp = 1;
10951558Srgrimes		}
1096100117Salfred		if (brief) {
1097100117Salfred			if (!xdr_bool(xdrsp, &true))
1098100117Salfred				return (1);
1099100117Salfred			strp = "(...)";
1100194880Sdfr			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1101100117Salfred				return (1);
1102100117Salfred		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
11031558Srgrimes		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
11041558Srgrimes			hp = dp->dp_hosts;
11051558Srgrimes			while (hp) {
11061558Srgrimes				grp = hp->ht_grp;
11071558Srgrimes				if (grp->gr_type == GT_HOST) {
11081558Srgrimes					if (!xdr_bool(xdrsp, &true))
11091558Srgrimes						return (1);
111074462Salfred					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
11118871Srgrimes					if (!xdr_string(xdrsp, &strp,
1112194880Sdfr					    MNTNAMLEN))
11131558Srgrimes						return (1);
11141558Srgrimes				} else if (grp->gr_type == GT_NET) {
11151558Srgrimes					if (!xdr_bool(xdrsp, &true))
11161558Srgrimes						return (1);
11171558Srgrimes					strp = grp->gr_ptr.gt_net.nt_name;
11188871Srgrimes					if (!xdr_string(xdrsp, &strp,
1119194880Sdfr					    MNTNAMLEN))
11201558Srgrimes						return (1);
11211558Srgrimes				}
11221558Srgrimes				hp = hp->ht_next;
11231558Srgrimes				if (gotalldir && hp == (struct hostlist *)NULL) {
11241558Srgrimes					hp = adp->dp_hosts;
11251558Srgrimes					gotalldir = 0;
11261558Srgrimes				}
11271558Srgrimes			}
11281558Srgrimes		}
11291558Srgrimes		if (!xdr_bool(xdrsp, &false))
11301558Srgrimes			return (1);
1131100117Salfred		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
11321558Srgrimes			return (1);
11331558Srgrimes	}
11341558Srgrimes	return (0);
11351558Srgrimes}
11361558Srgrimes
1137100117Salfredint
1138100117Salfredxdr_explist(xdrsp, cp)
1139100117Salfred	XDR *xdrsp;
1140100117Salfred	caddr_t cp;
1141100117Salfred{
1142100117Salfred
1143100117Salfred	return xdr_explist_common(xdrsp, cp, 0);
1144100117Salfred}
1145100117Salfred
1146100117Salfredint
1147100117Salfredxdr_explist_brief(xdrsp, cp)
1148100117Salfred	XDR *xdrsp;
1149100117Salfred	caddr_t cp;
1150100117Salfred{
1151100117Salfred
1152100117Salfred	return xdr_explist_common(xdrsp, cp, 1);
1153100117Salfred}
1154100117Salfred
115596622Siedowsechar *line;
115696622Siedowseint linesize;
11571558SrgrimesFILE *exp_file;
11581558Srgrimes
11591558Srgrimes/*
1160166440Spjd * Get the export list from one, currently open file
11611558Srgrimes */
1162166440Spjdstatic void
1163166440Spjdget_exportlist_one()
11641558Srgrimes{
11651558Srgrimes	struct exportlist *ep, *ep2;
11661558Srgrimes	struct grouplist *grp, *tgrp;
11671558Srgrimes	struct exportlist **epp;
11681558Srgrimes	struct dirlist *dirhead;
1169166440Spjd	struct statfs fsb;
117072650Sgreen	struct xucred anon;
11711558Srgrimes	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1172166440Spjd	int len, has_host, exflags, got_nondir, dirplen, netgrp;
11731558Srgrimes
1174192934Srmacklem	v4root_phase = 0;
11751558Srgrimes	dirhead = (struct dirlist *)NULL;
11761558Srgrimes	while (get_line()) {
11771558Srgrimes		if (debug)
117837663Scharnier			warnx("got line %s", line);
11791558Srgrimes		cp = line;
11801558Srgrimes		nextfield(&cp, &endcp);
11811558Srgrimes		if (*cp == '#')
11821558Srgrimes			goto nextline;
11831558Srgrimes
11841558Srgrimes		/*
11851558Srgrimes		 * Set defaults.
11861558Srgrimes		 */
11871558Srgrimes		has_host = FALSE;
11881558Srgrimes		anon = def_anon;
11891558Srgrimes		exflags = MNT_EXPORTED;
11901558Srgrimes		got_nondir = 0;
11911558Srgrimes		opt_flags = 0;
11921558Srgrimes		ep = (struct exportlist *)NULL;
1193192934Srmacklem		dirp = NULL;
11941558Srgrimes
11951558Srgrimes		/*
1196192934Srmacklem		 * Handle the V4 root dir.
1197192934Srmacklem		 */
1198192934Srmacklem		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1199192934Srmacklem			/*
1200192934Srmacklem			 * V4: just indicates that it is the v4 root point,
1201192934Srmacklem			 * so skip over that and set v4root_phase.
1202192934Srmacklem			 */
1203192934Srmacklem			if (v4root_phase > 0) {
1204192934Srmacklem				syslog(LOG_ERR, "V4:duplicate line, ignored");
1205192934Srmacklem				goto nextline;
1206192934Srmacklem			}
1207192934Srmacklem			v4root_phase = 1;
1208192934Srmacklem			cp += 3;
1209192934Srmacklem			nextfield(&cp, &endcp);
1210192934Srmacklem		}
1211192934Srmacklem
1212192934Srmacklem		/*
12131558Srgrimes		 * Create new exports list entry
12141558Srgrimes		 */
12151558Srgrimes		len = endcp-cp;
12161558Srgrimes		tgrp = grp = get_grp();
12171558Srgrimes		while (len > 0) {
1218194880Sdfr			if (len > MNTNAMLEN) {
12191558Srgrimes			    getexp_err(ep, tgrp);
12201558Srgrimes			    goto nextline;
12211558Srgrimes			}
12221558Srgrimes			if (*cp == '-') {
12231558Srgrimes			    if (ep == (struct exportlist *)NULL) {
12241558Srgrimes				getexp_err(ep, tgrp);
12251558Srgrimes				goto nextline;
12261558Srgrimes			    }
12271558Srgrimes			    if (debug)
122837663Scharnier				warnx("doing opt %s", cp);
12291558Srgrimes			    got_nondir = 1;
12301558Srgrimes			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
12311558Srgrimes				&exflags, &anon)) {
12321558Srgrimes				getexp_err(ep, tgrp);
12331558Srgrimes				goto nextline;
12341558Srgrimes			    }
12351558Srgrimes			} else if (*cp == '/') {
12361558Srgrimes			    savedc = *endcp;
12371558Srgrimes			    *endcp = '\0';
1238192934Srmacklem			    if (v4root_phase > 1) {
1239192934Srmacklem				    if (dirp != NULL) {
1240192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
1241192934Srmacklem					getexp_err(ep, tgrp);
1242192934Srmacklem					goto nextline;
1243192934Srmacklem				    }
1244192934Srmacklem			    }
12451558Srgrimes			    if (check_dirpath(cp) &&
12461558Srgrimes				statfs(cp, &fsb) >= 0) {
12471558Srgrimes				if (got_nondir) {
124837663Scharnier				    syslog(LOG_ERR, "dirs must be first");
12491558Srgrimes				    getexp_err(ep, tgrp);
12501558Srgrimes				    goto nextline;
12511558Srgrimes				}
1252192934Srmacklem				if (v4root_phase == 1) {
1253192934Srmacklem				    if (dirp != NULL) {
1254192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
12551558Srgrimes					getexp_err(ep, tgrp);
12561558Srgrimes					goto nextline;
12571558Srgrimes				    }
1258192934Srmacklem				    if (strlen(v4root_dirpath) == 0) {
1259192934Srmacklem					strlcpy(v4root_dirpath, cp,
1260192934Srmacklem					    sizeof (v4root_dirpath));
1261192934Srmacklem				    } else if (strcmp(v4root_dirpath, cp)
1262192934Srmacklem					!= 0) {
1263192934Srmacklem					syslog(LOG_ERR,
1264192934Srmacklem					    "different V4 dirpath %s", cp);
1265192934Srmacklem					getexp_err(ep, tgrp);
1266192934Srmacklem					goto nextline;
1267192934Srmacklem				    }
1268192934Srmacklem				    dirp = cp;
1269192934Srmacklem				    v4root_phase = 2;
1270192934Srmacklem				    got_nondir = 1;
1271192934Srmacklem				    ep = get_exp();
12721558Srgrimes				} else {
1273192934Srmacklem				    if (ep) {
1274192934Srmacklem					if (ep->ex_fs.val[0] !=
1275192934Srmacklem					    fsb.f_fsid.val[0] ||
1276192934Srmacklem					    ep->ex_fs.val[1] !=
1277192934Srmacklem					    fsb.f_fsid.val[1]) {
1278192934Srmacklem						getexp_err(ep, tgrp);
1279192934Srmacklem						goto nextline;
1280192934Srmacklem					}
1281192934Srmacklem				    } else {
1282192934Srmacklem					/*
1283192934Srmacklem					 * See if this directory is already
1284192934Srmacklem					 * in the list.
1285192934Srmacklem					 */
1286192934Srmacklem					ep = ex_search(&fsb.f_fsid);
1287192934Srmacklem					if (ep == (struct exportlist *)NULL) {
1288192934Srmacklem					    ep = get_exp();
1289192934Srmacklem					    ep->ex_fs = fsb.f_fsid;
1290192934Srmacklem					    ep->ex_fsdir = (char *)malloc
1291192934Srmacklem					        (strlen(fsb.f_mntonname) + 1);
1292192934Srmacklem					    if (ep->ex_fsdir)
1293192934Srmacklem						strcpy(ep->ex_fsdir,
1294192934Srmacklem						    fsb.f_mntonname);
1295192934Srmacklem					    else
1296192934Srmacklem						out_of_mem();
1297192934Srmacklem					    if (debug)
1298192934Srmacklem						warnx(
1299192934Srmacklem						  "making new ep fs=0x%x,0x%x",
1300192934Srmacklem						  fsb.f_fsid.val[0],
1301192934Srmacklem						  fsb.f_fsid.val[1]);
1302192934Srmacklem					} else if (debug)
1303192934Srmacklem					    warnx("found ep fs=0x%x,0x%x",
1304192934Srmacklem						fsb.f_fsid.val[0],
1305192934Srmacklem						fsb.f_fsid.val[1]);
1306192934Srmacklem				    }
1307192934Srmacklem
13081558Srgrimes				    /*
1309192934Srmacklem				     * Add dirpath to export mount point.
13101558Srgrimes				     */
1311192934Srmacklem				    dirp = add_expdir(&dirhead, cp, len);
1312192934Srmacklem				    dirplen = len;
13131558Srgrimes				}
13141558Srgrimes			    } else {
13151558Srgrimes				getexp_err(ep, tgrp);
13161558Srgrimes				goto nextline;
13171558Srgrimes			    }
13181558Srgrimes			    *endcp = savedc;
13191558Srgrimes			} else {
13201558Srgrimes			    savedc = *endcp;
13211558Srgrimes			    *endcp = '\0';
13221558Srgrimes			    got_nondir = 1;
13231558Srgrimes			    if (ep == (struct exportlist *)NULL) {
13241558Srgrimes				getexp_err(ep, tgrp);
13251558Srgrimes				goto nextline;
13261558Srgrimes			    }
13271558Srgrimes
13281558Srgrimes			    /*
13291558Srgrimes			     * Get the host or netgroup.
13301558Srgrimes			     */
13311558Srgrimes			    setnetgrent(cp);
13321558Srgrimes			    netgrp = getnetgrent(&hst, &usr, &dom);
13331558Srgrimes			    do {
13341558Srgrimes				if (has_host) {
13351558Srgrimes				    grp->gr_next = get_grp();
13361558Srgrimes				    grp = grp->gr_next;
13371558Srgrimes				}
13381558Srgrimes				if (netgrp) {
133937003Sjoerg				    if (hst == 0) {
134037663Scharnier					syslog(LOG_ERR,
134137663Scharnier				"null hostname in netgroup %s, skipping", cp);
134237004Sjoerg					grp->gr_type = GT_IGNORE;
134337003Sjoerg				    } else if (get_host(hst, grp, tgrp)) {
134437663Scharnier					syslog(LOG_ERR,
134537663Scharnier			"bad host %s in netgroup %s, skipping", hst, cp);
134629317Sjlemon					grp->gr_type = GT_IGNORE;
13471558Srgrimes				    }
13487401Swpaul				} else if (get_host(cp, grp, tgrp)) {
134937663Scharnier				    syslog(LOG_ERR, "bad host %s, skipping", cp);
135029317Sjlemon				    grp->gr_type = GT_IGNORE;
13511558Srgrimes				}
13521558Srgrimes				has_host = TRUE;
13531558Srgrimes			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
13541558Srgrimes			    endnetgrent();
13551558Srgrimes			    *endcp = savedc;
13561558Srgrimes			}
13571558Srgrimes			cp = endcp;
13581558Srgrimes			nextfield(&cp, &endcp);
13591558Srgrimes			len = endcp - cp;
13601558Srgrimes		}
13611558Srgrimes		if (check_options(dirhead)) {
13621558Srgrimes			getexp_err(ep, tgrp);
13631558Srgrimes			goto nextline;
13641558Srgrimes		}
13651558Srgrimes		if (!has_host) {
136675641Siedowse			grp->gr_type = GT_DEFAULT;
13671558Srgrimes			if (debug)
136837663Scharnier				warnx("adding a default entry");
13691558Srgrimes
13701558Srgrimes		/*
13711558Srgrimes		 * Don't allow a network export coincide with a list of
13721558Srgrimes		 * host(s) on the same line.
13731558Srgrimes		 */
13741558Srgrimes		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
137575801Siedowse			syslog(LOG_ERR, "network/host conflict");
13761558Srgrimes			getexp_err(ep, tgrp);
13771558Srgrimes			goto nextline;
137829317Sjlemon
137974462Salfred		/*
138074462Salfred		 * If an export list was specified on this line, make sure
138129317Sjlemon		 * that we have at least one valid entry, otherwise skip it.
138229317Sjlemon		 */
138329317Sjlemon		} else {
138429317Sjlemon			grp = tgrp;
138574462Salfred			while (grp && grp->gr_type == GT_IGNORE)
138629317Sjlemon				grp = grp->gr_next;
138729317Sjlemon			if (! grp) {
138829317Sjlemon			    getexp_err(ep, tgrp);
138929317Sjlemon			    goto nextline;
139029317Sjlemon			}
13911558Srgrimes		}
13921558Srgrimes
1393192934Srmacklem		if (v4root_phase == 1) {
1394192934Srmacklem			syslog(LOG_ERR, "V4:root, no dirp, ignored");
1395192934Srmacklem			getexp_err(ep, tgrp);
1396192934Srmacklem			goto nextline;
1397192934Srmacklem		}
1398192934Srmacklem
13991558Srgrimes		/*
14001558Srgrimes		 * Loop through hosts, pushing the exports into the kernel.
14011558Srgrimes		 * After loop, tgrp points to the start of the list and
14021558Srgrimes		 * grp points to the last entry in the list.
14031558Srgrimes		 */
14041558Srgrimes		grp = tgrp;
14051558Srgrimes		do {
140675635Siedowse			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
140775635Siedowse			    &fsb)) {
140875635Siedowse				getexp_err(ep, tgrp);
140975635Siedowse				goto nextline;
141075635Siedowse			}
14111558Srgrimes		} while (grp->gr_next && (grp = grp->gr_next));
14121558Srgrimes
14131558Srgrimes		/*
1414192934Srmacklem		 * For V4: don't enter in mount lists.
1415192934Srmacklem		 */
1416194773Srmacklem		if (v4root_phase > 0 && v4root_phase <= 2) {
1417194773Srmacklem			/*
1418194773Srmacklem			 * Since these structures aren't used by mountd,
1419194773Srmacklem			 * free them up now.
1420194773Srmacklem			 */
1421194773Srmacklem			if (ep != NULL)
1422194773Srmacklem				free_exp(ep);
1423194773Srmacklem			while (tgrp != NULL) {
1424194773Srmacklem				grp = tgrp;
1425194773Srmacklem				tgrp = tgrp->gr_next;
1426194773Srmacklem				free_grp(grp);
1427194773Srmacklem			}
1428192934Srmacklem			goto nextline;
1429194773Srmacklem		}
1430192934Srmacklem
1431192934Srmacklem		/*
14321558Srgrimes		 * Success. Update the data structures.
14331558Srgrimes		 */
14341558Srgrimes		if (has_host) {
14359336Sdfr			hang_dirp(dirhead, tgrp, ep, opt_flags);
14361558Srgrimes			grp->gr_next = grphead;
14371558Srgrimes			grphead = tgrp;
14381558Srgrimes		} else {
14391558Srgrimes			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
14409336Sdfr				opt_flags);
14411558Srgrimes			free_grp(grp);
14421558Srgrimes		}
14431558Srgrimes		dirhead = (struct dirlist *)NULL;
14441558Srgrimes		if ((ep->ex_flag & EX_LINKED) == 0) {
14451558Srgrimes			ep2 = exphead;
14461558Srgrimes			epp = &exphead;
14471558Srgrimes
14481558Srgrimes			/*
14491558Srgrimes			 * Insert in the list in alphabetical order.
14501558Srgrimes			 */
14511558Srgrimes			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
14521558Srgrimes				epp = &ep2->ex_next;
14531558Srgrimes				ep2 = ep2->ex_next;
14541558Srgrimes			}
14551558Srgrimes			if (ep2)
14561558Srgrimes				ep->ex_next = ep2;
14571558Srgrimes			*epp = ep;
14581558Srgrimes			ep->ex_flag |= EX_LINKED;
14591558Srgrimes		}
14601558Srgrimesnextline:
1461192934Srmacklem		v4root_phase = 0;
14621558Srgrimes		if (dirhead) {
14631558Srgrimes			free_dir(dirhead);
14641558Srgrimes			dirhead = (struct dirlist *)NULL;
14651558Srgrimes		}
14661558Srgrimes	}
14671558Srgrimes}
14681558Srgrimes
14691558Srgrimes/*
1470166440Spjd * Get the export list from all specified files
1471166440Spjd */
1472166440Spjdvoid
1473166440Spjdget_exportlist()
1474166440Spjd{
1475166440Spjd	struct exportlist *ep, *ep2;
1476166440Spjd	struct grouplist *grp, *tgrp;
1477166440Spjd	struct export_args export;
1478166440Spjd	struct iovec *iov;
1479166440Spjd	struct statfs *fsp, *mntbufp;
1480166440Spjd	struct xvfsconf vfc;
1481166440Spjd	char *dirp;
1482166440Spjd	char errmsg[255];
1483166440Spjd	int dirplen, num, i;
1484166440Spjd	int iovlen;
1485168684Spjd	int done;
1486192934Srmacklem	struct nfsex_args eargs;
1487166440Spjd
1488192934Srmacklem	v4root_dirpath[0] = '\0';
1489166440Spjd	bzero(&export, sizeof(export));
1490166440Spjd	export.ex_flags = MNT_DELEXPORT;
1491166440Spjd	dirp = NULL;
1492166440Spjd	dirplen = 0;
1493166440Spjd	iov = NULL;
1494166440Spjd	iovlen = 0;
1495166440Spjd	bzero(errmsg, sizeof(errmsg));
1496166440Spjd
1497166440Spjd	/*
1498166440Spjd	 * First, get rid of the old list
1499166440Spjd	 */
1500166440Spjd	ep = exphead;
1501166440Spjd	while (ep) {
1502166440Spjd		ep2 = ep;
1503166440Spjd		ep = ep->ex_next;
1504166440Spjd		free_exp(ep2);
1505166440Spjd	}
1506166440Spjd	exphead = (struct exportlist *)NULL;
1507166440Spjd
1508166440Spjd	grp = grphead;
1509166440Spjd	while (grp) {
1510166440Spjd		tgrp = grp;
1511166440Spjd		grp = grp->gr_next;
1512166440Spjd		free_grp(tgrp);
1513166440Spjd	}
1514166440Spjd	grphead = (struct grouplist *)NULL;
1515166440Spjd
1516166440Spjd	/*
1517192934Srmacklem	 * and the old V4 root dir.
1518192934Srmacklem	 */
1519192934Srmacklem	bzero(&eargs, sizeof (eargs));
1520192934Srmacklem	eargs.export.ex_flags = MNT_DELEXPORT;
1521192934Srmacklem	if (run_v4server > 0 &&
1522192934Srmacklem	    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1523192934Srmacklem	    errno != ENOENT)
1524192934Srmacklem		syslog(LOG_ERR, "Can't delete exports for V4:");
1525192934Srmacklem
1526192934Srmacklem	/*
1527192934Srmacklem	 * and clear flag that notes if a public fh has been exported.
1528192934Srmacklem	 */
1529192934Srmacklem	has_publicfh = 0;
1530192934Srmacklem
1531192934Srmacklem	/*
1532166440Spjd	 * And delete exports that are in the kernel for all local
1533166440Spjd	 * filesystems.
1534166440Spjd	 * XXX: Should know how to handle all local exportable filesystems.
1535166440Spjd	 */
1536166440Spjd	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1537166440Spjd
1538166440Spjd	if (num > 0) {
1539166440Spjd		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1540166440Spjd		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1541166440Spjd		build_iovec(&iov, &iovlen, "from", NULL, 0);
1542166440Spjd		build_iovec(&iov, &iovlen, "update", NULL, 0);
1543166440Spjd		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1544166440Spjd		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1545166440Spjd	}
1546166440Spjd
1547166440Spjd	for (i = 0; i < num; i++) {
1548166440Spjd		fsp = &mntbufp[i];
1549166440Spjd		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1550166440Spjd			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1551166440Spjd			    fsp->f_fstypename);
1552166440Spjd			continue;
1553166440Spjd		}
1554166440Spjd
1555166440Spjd		/*
1556166440Spjd		 * Do not delete export for network filesystem by
1557166440Spjd		 * passing "export" arg to nmount().
1558166440Spjd		 * It only makes sense to do this for local filesystems.
1559166440Spjd		 */
1560166440Spjd		if (vfc.vfc_flags & VFCF_NETWORK)
1561166440Spjd			continue;
1562166440Spjd
1563166440Spjd		iov[1].iov_base = fsp->f_fstypename;
1564166440Spjd		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1565166440Spjd		iov[3].iov_base = fsp->f_mntonname;
1566166440Spjd		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1567166440Spjd		iov[5].iov_base = fsp->f_mntfromname;
1568166440Spjd		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1569166440Spjd
1570166440Spjd		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1571166440Spjd		    errno != ENOENT && errno != ENOTSUP) {
1572166440Spjd			syslog(LOG_ERR,
1573166440Spjd			    "can't delete exports for %s: %m %s",
1574166440Spjd			    fsp->f_mntonname, errmsg);
1575166440Spjd		}
1576166440Spjd	}
1577166440Spjd
1578166440Spjd	if (iov != NULL) {
1579166440Spjd		/* Free strings allocated by strdup() in getmntopts.c */
1580166440Spjd		free(iov[0].iov_base); /* fstype */
1581166440Spjd		free(iov[2].iov_base); /* fspath */
1582166440Spjd		free(iov[4].iov_base); /* from */
1583166440Spjd		free(iov[6].iov_base); /* update */
1584166440Spjd		free(iov[8].iov_base); /* export */
1585166440Spjd		free(iov[10].iov_base); /* errmsg */
1586166440Spjd
1587166440Spjd		/* free iov, allocated by realloc() */
1588166440Spjd		free(iov);
1589166440Spjd		iovlen = 0;
1590166440Spjd	}
1591166440Spjd
1592166440Spjd	/*
1593166440Spjd	 * Read in the exports file and build the list, calling
1594166440Spjd	 * nmount() as we go along to push the export rules into the kernel.
1595166440Spjd	 */
1596168684Spjd	done = 0;
1597166440Spjd	for (i = 0; exnames[i] != NULL; i++) {
1598166440Spjd		if (debug)
1599166440Spjd			warnx("reading exports from %s", exnames[i]);
1600166440Spjd		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1601168684Spjd			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1602168684Spjd			continue;
1603166440Spjd		}
1604166440Spjd		get_exportlist_one();
1605166440Spjd		fclose(exp_file);
1606168684Spjd		done++;
1607166440Spjd	}
1608168684Spjd	if (done == 0) {
1609168684Spjd		syslog(LOG_ERR, "can't open any exports file");
1610168684Spjd		exit(2);
1611168684Spjd	}
1612192934Srmacklem
1613192934Srmacklem	/*
1614192934Srmacklem	 * If there was no public fh, clear any previous one set.
1615192934Srmacklem	 */
1616192934Srmacklem	if (run_v4server > 0 && has_publicfh == 0)
1617192934Srmacklem		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1618166440Spjd}
1619166440Spjd
1620166440Spjd/*
16211558Srgrimes * Allocate an export list element
16221558Srgrimes */
16231558Srgrimesstruct exportlist *
16241558Srgrimesget_exp()
16251558Srgrimes{
16261558Srgrimes	struct exportlist *ep;
16271558Srgrimes
16281558Srgrimes	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
16291558Srgrimes	if (ep == (struct exportlist *)NULL)
16301558Srgrimes		out_of_mem();
163123681Speter	memset(ep, 0, sizeof(struct exportlist));
16321558Srgrimes	return (ep);
16331558Srgrimes}
16341558Srgrimes
16351558Srgrimes/*
16361558Srgrimes * Allocate a group list element
16371558Srgrimes */
16381558Srgrimesstruct grouplist *
16391558Srgrimesget_grp()
16401558Srgrimes{
16411558Srgrimes	struct grouplist *gp;
16421558Srgrimes
16431558Srgrimes	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
16441558Srgrimes	if (gp == (struct grouplist *)NULL)
16451558Srgrimes		out_of_mem();
164623681Speter	memset(gp, 0, sizeof(struct grouplist));
16471558Srgrimes	return (gp);
16481558Srgrimes}
16491558Srgrimes
16501558Srgrimes/*
16511558Srgrimes * Clean up upon an error in get_exportlist().
16521558Srgrimes */
16531558Srgrimesvoid
16541558Srgrimesgetexp_err(ep, grp)
16551558Srgrimes	struct exportlist *ep;
16561558Srgrimes	struct grouplist *grp;
16571558Srgrimes{
16581558Srgrimes	struct grouplist *tgrp;
16591558Srgrimes
1660100336Sjoerg	if (!(opt_flags & OP_QUIET))
1661100336Sjoerg		syslog(LOG_ERR, "bad exports list line %s", line);
16621558Srgrimes	if (ep && (ep->ex_flag & EX_LINKED) == 0)
16631558Srgrimes		free_exp(ep);
16641558Srgrimes	while (grp) {
16651558Srgrimes		tgrp = grp;
16661558Srgrimes		grp = grp->gr_next;
16671558Srgrimes		free_grp(tgrp);
16681558Srgrimes	}
16691558Srgrimes}
16701558Srgrimes
16711558Srgrimes/*
16721558Srgrimes * Search the export list for a matching fs.
16731558Srgrimes */
16741558Srgrimesstruct exportlist *
16751558Srgrimesex_search(fsid)
16761558Srgrimes	fsid_t *fsid;
16771558Srgrimes{
16781558Srgrimes	struct exportlist *ep;
16791558Srgrimes
16801558Srgrimes	ep = exphead;
16811558Srgrimes	while (ep) {
16821558Srgrimes		if (ep->ex_fs.val[0] == fsid->val[0] &&
16831558Srgrimes		    ep->ex_fs.val[1] == fsid->val[1])
16841558Srgrimes			return (ep);
16851558Srgrimes		ep = ep->ex_next;
16861558Srgrimes	}
16871558Srgrimes	return (ep);
16881558Srgrimes}
16891558Srgrimes
16901558Srgrimes/*
16911558Srgrimes * Add a directory path to the list.
16921558Srgrimes */
16931558Srgrimeschar *
16941558Srgrimesadd_expdir(dpp, cp, len)
16951558Srgrimes	struct dirlist **dpp;
16961558Srgrimes	char *cp;
16971558Srgrimes	int len;
16981558Srgrimes{
16991558Srgrimes	struct dirlist *dp;
17001558Srgrimes
17011558Srgrimes	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
170237663Scharnier	if (dp == (struct dirlist *)NULL)
170337663Scharnier		out_of_mem();
17041558Srgrimes	dp->dp_left = *dpp;
17051558Srgrimes	dp->dp_right = (struct dirlist *)NULL;
17061558Srgrimes	dp->dp_flag = 0;
17071558Srgrimes	dp->dp_hosts = (struct hostlist *)NULL;
17081558Srgrimes	strcpy(dp->dp_dirp, cp);
17091558Srgrimes	*dpp = dp;
17101558Srgrimes	return (dp->dp_dirp);
17111558Srgrimes}
17121558Srgrimes
17131558Srgrimes/*
17141558Srgrimes * Hang the dir list element off the dirpath binary tree as required
17151558Srgrimes * and update the entry for host.
17161558Srgrimes */
17171558Srgrimesvoid
17189336Sdfrhang_dirp(dp, grp, ep, flags)
17191558Srgrimes	struct dirlist *dp;
17201558Srgrimes	struct grouplist *grp;
17211558Srgrimes	struct exportlist *ep;
17229336Sdfr	int flags;
17231558Srgrimes{
17241558Srgrimes	struct hostlist *hp;
17251558Srgrimes	struct dirlist *dp2;
17261558Srgrimes
17279336Sdfr	if (flags & OP_ALLDIRS) {
17281558Srgrimes		if (ep->ex_defdir)
17291558Srgrimes			free((caddr_t)dp);
17301558Srgrimes		else
17311558Srgrimes			ep->ex_defdir = dp;
17329336Sdfr		if (grp == (struct grouplist *)NULL) {
17331558Srgrimes			ep->ex_defdir->dp_flag |= DP_DEFSET;
17349336Sdfr		} else while (grp) {
17351558Srgrimes			hp = get_ht();
17361558Srgrimes			hp->ht_grp = grp;
17371558Srgrimes			hp->ht_next = ep->ex_defdir->dp_hosts;
17381558Srgrimes			ep->ex_defdir->dp_hosts = hp;
17391558Srgrimes			grp = grp->gr_next;
17401558Srgrimes		}
17411558Srgrimes	} else {
17421558Srgrimes
17431558Srgrimes		/*
174437663Scharnier		 * Loop through the directories adding them to the tree.
17451558Srgrimes		 */
17461558Srgrimes		while (dp) {
17471558Srgrimes			dp2 = dp->dp_left;
17489336Sdfr			add_dlist(&ep->ex_dirl, dp, grp, flags);
17491558Srgrimes			dp = dp2;
17501558Srgrimes		}
17511558Srgrimes	}
17521558Srgrimes}
17531558Srgrimes
17541558Srgrimes/*
17551558Srgrimes * Traverse the binary tree either updating a node that is already there
17561558Srgrimes * for the new directory or adding the new node.
17571558Srgrimes */
17581558Srgrimesvoid
17599336Sdfradd_dlist(dpp, newdp, grp, flags)
17601558Srgrimes	struct dirlist **dpp;
17611558Srgrimes	struct dirlist *newdp;
17621558Srgrimes	struct grouplist *grp;
17639336Sdfr	int flags;
17641558Srgrimes{
17651558Srgrimes	struct dirlist *dp;
17661558Srgrimes	struct hostlist *hp;
17671558Srgrimes	int cmp;
17681558Srgrimes
17691558Srgrimes	dp = *dpp;
17701558Srgrimes	if (dp) {
17711558Srgrimes		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
17721558Srgrimes		if (cmp > 0) {
17739336Sdfr			add_dlist(&dp->dp_left, newdp, grp, flags);
17741558Srgrimes			return;
17751558Srgrimes		} else if (cmp < 0) {
17769336Sdfr			add_dlist(&dp->dp_right, newdp, grp, flags);
17771558Srgrimes			return;
17781558Srgrimes		} else
17791558Srgrimes			free((caddr_t)newdp);
17801558Srgrimes	} else {
17811558Srgrimes		dp = newdp;
17821558Srgrimes		dp->dp_left = (struct dirlist *)NULL;
17831558Srgrimes		*dpp = dp;
17841558Srgrimes	}
17851558Srgrimes	if (grp) {
17861558Srgrimes
17871558Srgrimes		/*
17881558Srgrimes		 * Hang all of the host(s) off of the directory point.
17891558Srgrimes		 */
17901558Srgrimes		do {
17911558Srgrimes			hp = get_ht();
17921558Srgrimes			hp->ht_grp = grp;
17931558Srgrimes			hp->ht_next = dp->dp_hosts;
17941558Srgrimes			dp->dp_hosts = hp;
17951558Srgrimes			grp = grp->gr_next;
17961558Srgrimes		} while (grp);
17979336Sdfr	} else {
17981558Srgrimes		dp->dp_flag |= DP_DEFSET;
17999336Sdfr	}
18001558Srgrimes}
18011558Srgrimes
18021558Srgrimes/*
18031558Srgrimes * Search for a dirpath on the export point.
18041558Srgrimes */
18051558Srgrimesstruct dirlist *
180674462Salfreddirp_search(dp, dirp)
18071558Srgrimes	struct dirlist *dp;
180874462Salfred	char *dirp;
18091558Srgrimes{
18101558Srgrimes	int cmp;
18111558Srgrimes
18121558Srgrimes	if (dp) {
181374462Salfred		cmp = strcmp(dp->dp_dirp, dirp);
18141558Srgrimes		if (cmp > 0)
181574462Salfred			return (dirp_search(dp->dp_left, dirp));
18161558Srgrimes		else if (cmp < 0)
181774462Salfred			return (dirp_search(dp->dp_right, dirp));
18181558Srgrimes		else
18191558Srgrimes			return (dp);
18201558Srgrimes	}
18211558Srgrimes	return (dp);
18221558Srgrimes}
18231558Srgrimes
18241558Srgrimes/*
18251558Srgrimes * Scan for a host match in a directory tree.
18261558Srgrimes */
18271558Srgrimesint
18289336Sdfrchk_host(dp, saddr, defsetp, hostsetp)
18291558Srgrimes	struct dirlist *dp;
183074462Salfred	struct sockaddr *saddr;
18311558Srgrimes	int *defsetp;
18329336Sdfr	int *hostsetp;
18331558Srgrimes{
18341558Srgrimes	struct hostlist *hp;
18351558Srgrimes	struct grouplist *grp;
183674462Salfred	struct addrinfo *ai;
18371558Srgrimes
18381558Srgrimes	if (dp) {
18391558Srgrimes		if (dp->dp_flag & DP_DEFSET)
18409336Sdfr			*defsetp = dp->dp_flag;
18411558Srgrimes		hp = dp->dp_hosts;
18421558Srgrimes		while (hp) {
18431558Srgrimes			grp = hp->ht_grp;
18441558Srgrimes			switch (grp->gr_type) {
18451558Srgrimes			case GT_HOST:
184674462Salfred				ai = grp->gr_ptr.gt_addrinfo;
184774462Salfred				for (; ai; ai = ai->ai_next) {
184875801Siedowse					if (!sacmp(ai->ai_addr, saddr, NULL)) {
184974462Salfred						*hostsetp =
185074462Salfred						    (hp->ht_flag | DP_HOSTSET);
185174462Salfred						return (1);
185274462Salfred					}
18539336Sdfr				}
185475801Siedowse				break;
18551558Srgrimes			case GT_NET:
185675801Siedowse				if (!sacmp(saddr, (struct sockaddr *)
185775801Siedowse				    &grp->gr_ptr.gt_net.nt_net,
185875801Siedowse				    (struct sockaddr *)
185975801Siedowse				    &grp->gr_ptr.gt_net.nt_mask)) {
186074462Salfred					*hostsetp = (hp->ht_flag | DP_HOSTSET);
186174462Salfred					return (1);
186274462Salfred				}
186375801Siedowse				break;
186475801Siedowse			}
18651558Srgrimes			hp = hp->ht_next;
18661558Srgrimes		}
18671558Srgrimes	}
18681558Srgrimes	return (0);
18691558Srgrimes}
18701558Srgrimes
18711558Srgrimes/*
18721558Srgrimes * Scan tree for a host that matches the address.
18731558Srgrimes */
18741558Srgrimesint
18751558Srgrimesscan_tree(dp, saddr)
18761558Srgrimes	struct dirlist *dp;
187774462Salfred	struct sockaddr *saddr;
18781558Srgrimes{
18799336Sdfr	int defset, hostset;
18801558Srgrimes
18811558Srgrimes	if (dp) {
18821558Srgrimes		if (scan_tree(dp->dp_left, saddr))
18831558Srgrimes			return (1);
18849336Sdfr		if (chk_host(dp, saddr, &defset, &hostset))
18851558Srgrimes			return (1);
18861558Srgrimes		if (scan_tree(dp->dp_right, saddr))
18871558Srgrimes			return (1);
18881558Srgrimes	}
18891558Srgrimes	return (0);
18901558Srgrimes}
18911558Srgrimes
18921558Srgrimes/*
18931558Srgrimes * Traverse the dirlist tree and free it up.
18941558Srgrimes */
18951558Srgrimesvoid
18961558Srgrimesfree_dir(dp)
18971558Srgrimes	struct dirlist *dp;
18981558Srgrimes{
18991558Srgrimes
19001558Srgrimes	if (dp) {
19011558Srgrimes		free_dir(dp->dp_left);
19021558Srgrimes		free_dir(dp->dp_right);
19031558Srgrimes		free_host(dp->dp_hosts);
19041558Srgrimes		free((caddr_t)dp);
19051558Srgrimes	}
19061558Srgrimes}
19071558Srgrimes
19081558Srgrimes/*
1909184588Sdfr * Parse a colon separated list of security flavors
1910184588Sdfr */
1911184588Sdfrint
1912184588Sdfrparsesec(seclist, ep)
1913184588Sdfr	char *seclist;
1914184588Sdfr	struct exportlist *ep;
1915184588Sdfr{
1916184588Sdfr	char *cp, savedc;
1917184588Sdfr	int flavor;
1918184588Sdfr
1919184588Sdfr	ep->ex_numsecflavors = 0;
1920184588Sdfr	for (;;) {
1921184588Sdfr		cp = strchr(seclist, ':');
1922184588Sdfr		if (cp) {
1923184588Sdfr			savedc = *cp;
1924184588Sdfr			*cp = '\0';
1925184588Sdfr		}
1926184588Sdfr
1927184588Sdfr		if (!strcmp(seclist, "sys"))
1928184588Sdfr			flavor = AUTH_SYS;
1929184588Sdfr		else if (!strcmp(seclist, "krb5"))
1930184588Sdfr			flavor = RPCSEC_GSS_KRB5;
1931184588Sdfr		else if (!strcmp(seclist, "krb5i"))
1932184588Sdfr			flavor = RPCSEC_GSS_KRB5I;
1933184588Sdfr		else if (!strcmp(seclist, "krb5p"))
1934184588Sdfr			flavor = RPCSEC_GSS_KRB5P;
1935184588Sdfr		else {
1936184588Sdfr			if (cp)
1937184588Sdfr				*cp = savedc;
1938184588Sdfr			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
1939184588Sdfr			return (1);
1940184588Sdfr		}
1941184588Sdfr		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
1942184588Sdfr			if (cp)
1943184588Sdfr				*cp = savedc;
1944184588Sdfr			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
1945184588Sdfr			return (1);
1946184588Sdfr		}
1947184588Sdfr		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
1948184588Sdfr		ep->ex_numsecflavors++;
1949184588Sdfr		if (cp) {
1950184588Sdfr			*cp = savedc;
1951184588Sdfr			seclist = cp + 1;
1952184588Sdfr		} else {
1953184588Sdfr			break;
1954184588Sdfr		}
1955184588Sdfr	}
1956184588Sdfr	return (0);
1957184588Sdfr}
1958184588Sdfr
1959184588Sdfr/*
19601558Srgrimes * Parse the option string and update fields.
19611558Srgrimes * Option arguments may either be -<option>=<value> or
19621558Srgrimes * -<option> <value>
19631558Srgrimes */
19641558Srgrimesint
19651558Srgrimesdo_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
19661558Srgrimes	char **cpp, **endcpp;
19671558Srgrimes	struct exportlist *ep;
19681558Srgrimes	struct grouplist *grp;
19691558Srgrimes	int *has_hostp;
19701558Srgrimes	int *exflagsp;
197172650Sgreen	struct xucred *cr;
19721558Srgrimes{
19731558Srgrimes	char *cpoptarg, *cpoptend;
19741558Srgrimes	char *cp, *endcp, *cpopt, savedc, savedc2;
19751558Srgrimes	int allflag, usedarg;
19761558Srgrimes
197751968Salfred	savedc2 = '\0';
19781558Srgrimes	cpopt = *cpp;
19791558Srgrimes	cpopt++;
19801558Srgrimes	cp = *endcpp;
19811558Srgrimes	savedc = *cp;
19821558Srgrimes	*cp = '\0';
19831558Srgrimes	while (cpopt && *cpopt) {
19841558Srgrimes		allflag = 1;
19851558Srgrimes		usedarg = -2;
198637663Scharnier		if ((cpoptend = strchr(cpopt, ','))) {
19871558Srgrimes			*cpoptend++ = '\0';
198837663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
19891558Srgrimes				*cpoptarg++ = '\0';
19901558Srgrimes		} else {
199137663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
19921558Srgrimes				*cpoptarg++ = '\0';
19931558Srgrimes			else {
19941558Srgrimes				*cp = savedc;
19951558Srgrimes				nextfield(&cp, &endcp);
19961558Srgrimes				**endcpp = '\0';
19971558Srgrimes				if (endcp > cp && *cp != '-') {
19981558Srgrimes					cpoptarg = cp;
19991558Srgrimes					savedc2 = *endcp;
20001558Srgrimes					*endcp = '\0';
20011558Srgrimes					usedarg = 0;
20021558Srgrimes				}
20031558Srgrimes			}
20041558Srgrimes		}
20051558Srgrimes		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
20061558Srgrimes			*exflagsp |= MNT_EXRDONLY;
20071558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
20081558Srgrimes		    !(allflag = strcmp(cpopt, "mapall")) ||
20091558Srgrimes		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
20101558Srgrimes			usedarg++;
20111558Srgrimes			parsecred(cpoptarg, cr);
20121558Srgrimes			if (allflag == 0) {
20131558Srgrimes				*exflagsp |= MNT_EXPORTANON;
20141558Srgrimes				opt_flags |= OP_MAPALL;
20151558Srgrimes			} else
20161558Srgrimes				opt_flags |= OP_MAPROOT;
20171558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
201875801Siedowse		    !strcmp(cpopt, "m"))) {
20191558Srgrimes			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
202037663Scharnier				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
20211558Srgrimes				return (1);
20221558Srgrimes			}
20231558Srgrimes			usedarg++;
20241558Srgrimes			opt_flags |= OP_MASK;
20251558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
20261558Srgrimes			!strcmp(cpopt, "n"))) {
202774462Salfred			if (strchr(cpoptarg, '/') != NULL) {
202874462Salfred				if (debug)
202974462Salfred					fprintf(stderr, "setting OP_MASKLEN\n");
203074462Salfred				opt_flags |= OP_MASKLEN;
203174462Salfred			}
20321558Srgrimes			if (grp->gr_type != GT_NULL) {
203337663Scharnier				syslog(LOG_ERR, "network/host conflict");
20341558Srgrimes				return (1);
20351558Srgrimes			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
203637663Scharnier				syslog(LOG_ERR, "bad net: %s", cpoptarg);
20371558Srgrimes				return (1);
20381558Srgrimes			}
20391558Srgrimes			grp->gr_type = GT_NET;
20401558Srgrimes			*has_hostp = 1;
20411558Srgrimes			usedarg++;
20421558Srgrimes			opt_flags |= OP_NET;
20431558Srgrimes		} else if (!strcmp(cpopt, "alldirs")) {
20441558Srgrimes			opt_flags |= OP_ALLDIRS;
204527447Sdfr		} else if (!strcmp(cpopt, "public")) {
204627447Sdfr			*exflagsp |= MNT_EXPUBLIC;
204727447Sdfr		} else if (!strcmp(cpopt, "webnfs")) {
204827447Sdfr			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
204927447Sdfr			opt_flags |= OP_MAPALL;
205027447Sdfr		} else if (cpoptarg && !strcmp(cpopt, "index")) {
205127447Sdfr			ep->ex_indexfile = strdup(cpoptarg);
2052100336Sjoerg		} else if (!strcmp(cpopt, "quiet")) {
2053100336Sjoerg			opt_flags |= OP_QUIET;
2054184588Sdfr		} else if (!strcmp(cpopt, "sec")) {
2055184588Sdfr			if (parsesec(cpoptarg, ep))
2056184588Sdfr				return (1);
2057184588Sdfr			opt_flags |= OP_SEC;
2058184588Sdfr			usedarg++;
20591558Srgrimes		} else {
206037663Scharnier			syslog(LOG_ERR, "bad opt %s", cpopt);
20611558Srgrimes			return (1);
20621558Srgrimes		}
20631558Srgrimes		if (usedarg >= 0) {
20641558Srgrimes			*endcp = savedc2;
20651558Srgrimes			**endcpp = savedc;
20661558Srgrimes			if (usedarg > 0) {
20671558Srgrimes				*cpp = cp;
20681558Srgrimes				*endcpp = endcp;
20691558Srgrimes			}
20701558Srgrimes			return (0);
20711558Srgrimes		}
20721558Srgrimes		cpopt = cpoptend;
20731558Srgrimes	}
20741558Srgrimes	**endcpp = savedc;
20751558Srgrimes	return (0);
20761558Srgrimes}
20771558Srgrimes
20781558Srgrimes/*
20791558Srgrimes * Translate a character string to the corresponding list of network
20801558Srgrimes * addresses for a hostname.
20811558Srgrimes */
20821558Srgrimesint
20837401Swpaulget_host(cp, grp, tgrp)
20841558Srgrimes	char *cp;
20851558Srgrimes	struct grouplist *grp;
20867401Swpaul	struct grouplist *tgrp;
20871558Srgrimes{
20887401Swpaul	struct grouplist *checkgrp;
208975635Siedowse	struct addrinfo *ai, *tai, hints;
209074462Salfred	int ecode;
209174462Salfred	char host[NI_MAXHOST];
20921558Srgrimes
209374462Salfred	if (grp->gr_type != GT_NULL) {
209474462Salfred		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
20951558Srgrimes		return (1);
20961558Srgrimes	}
209774462Salfred	memset(&hints, 0, sizeof hints);
209874462Salfred	hints.ai_flags = AI_CANONNAME;
209974462Salfred	hints.ai_protocol = IPPROTO_UDP;
210074462Salfred	ecode = getaddrinfo(cp, NULL, &hints, &ai);
210174462Salfred	if (ecode != 0) {
210275635Siedowse		syslog(LOG_ERR,"can't get address info for host %s", cp);
210374462Salfred		return 1;
210474462Salfred	}
210574462Salfred	grp->gr_ptr.gt_addrinfo = ai;
210674462Salfred	while (ai != NULL) {
210774462Salfred		if (ai->ai_canonname == NULL) {
210874462Salfred			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2109146187Sume			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
211074462Salfred				strlcpy(host, "?", sizeof(host));
211174462Salfred			ai->ai_canonname = strdup(host);
211274462Salfred			ai->ai_flags |= AI_CANONNAME;
211375641Siedowse		}
211474462Salfred		if (debug)
211575635Siedowse			fprintf(stderr, "got host %s\n", ai->ai_canonname);
211675635Siedowse		/*
211775635Siedowse		 * Sanity check: make sure we don't already have an entry
211875635Siedowse		 * for this host in the grouplist.
211975635Siedowse		 */
212075635Siedowse		for (checkgrp = tgrp; checkgrp != NULL;
212175635Siedowse		    checkgrp = checkgrp->gr_next) {
212275635Siedowse			if (checkgrp->gr_type != GT_HOST)
212375635Siedowse				continue;
212475635Siedowse			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
212575635Siedowse			    tai = tai->ai_next) {
212675801Siedowse				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
212775635Siedowse					continue;
212875635Siedowse				if (debug)
212975635Siedowse					fprintf(stderr,
213075635Siedowse					    "ignoring duplicate host %s\n",
213175635Siedowse					    ai->ai_canonname);
213275635Siedowse				grp->gr_type = GT_IGNORE;
213375635Siedowse				return (0);
213475635Siedowse			}
213575635Siedowse		}
213674462Salfred		ai = ai->ai_next;
21371558Srgrimes	}
213875635Siedowse	grp->gr_type = GT_HOST;
21391558Srgrimes	return (0);
21401558Srgrimes}
21411558Srgrimes
21421558Srgrimes/*
21431558Srgrimes * Free up an exports list component
21441558Srgrimes */
21451558Srgrimesvoid
21461558Srgrimesfree_exp(ep)
21471558Srgrimes	struct exportlist *ep;
21481558Srgrimes{
21491558Srgrimes
21501558Srgrimes	if (ep->ex_defdir) {
21511558Srgrimes		free_host(ep->ex_defdir->dp_hosts);
21521558Srgrimes		free((caddr_t)ep->ex_defdir);
21531558Srgrimes	}
21541558Srgrimes	if (ep->ex_fsdir)
21551558Srgrimes		free(ep->ex_fsdir);
215627447Sdfr	if (ep->ex_indexfile)
215727447Sdfr		free(ep->ex_indexfile);
21581558Srgrimes	free_dir(ep->ex_dirl);
21591558Srgrimes	free((caddr_t)ep);
21601558Srgrimes}
21611558Srgrimes
21621558Srgrimes/*
21631558Srgrimes * Free hosts.
21641558Srgrimes */
21651558Srgrimesvoid
21661558Srgrimesfree_host(hp)
21671558Srgrimes	struct hostlist *hp;
21681558Srgrimes{
21691558Srgrimes	struct hostlist *hp2;
21701558Srgrimes
21711558Srgrimes	while (hp) {
21721558Srgrimes		hp2 = hp;
21731558Srgrimes		hp = hp->ht_next;
21741558Srgrimes		free((caddr_t)hp2);
21751558Srgrimes	}
21761558Srgrimes}
21771558Srgrimes
21781558Srgrimesstruct hostlist *
21791558Srgrimesget_ht()
21801558Srgrimes{
21811558Srgrimes	struct hostlist *hp;
21821558Srgrimes
21831558Srgrimes	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
21841558Srgrimes	if (hp == (struct hostlist *)NULL)
21851558Srgrimes		out_of_mem();
21861558Srgrimes	hp->ht_next = (struct hostlist *)NULL;
21879336Sdfr	hp->ht_flag = 0;
21881558Srgrimes	return (hp);
21891558Srgrimes}
21901558Srgrimes
21911558Srgrimes/*
21921558Srgrimes * Out of memory, fatal
21931558Srgrimes */
21941558Srgrimesvoid
21951558Srgrimesout_of_mem()
21961558Srgrimes{
21971558Srgrimes
219837663Scharnier	syslog(LOG_ERR, "out of memory");
21991558Srgrimes	exit(2);
22001558Srgrimes}
22011558Srgrimes
22021558Srgrimes/*
2203158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into
22041558Srgrimes * the kernel.
22051558Srgrimes */
22061558Srgrimesint
2207158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2208158857Srodrigc    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
22091558Srgrimes{
221075841Siedowse	struct statfs fsb1;
221174462Salfred	struct addrinfo *ai;
2212192934Srmacklem	struct export_args ea, *eap;
2213158857Srodrigc	char errmsg[255];
2214158857Srodrigc	char *cp;
22151558Srgrimes	int done;
2216158857Srodrigc	char savedc;
2217158857Srodrigc	struct iovec *iov;
2218184588Sdfr	int i, iovlen;
2219158857Srodrigc	int ret;
2220192934Srmacklem	struct nfsex_args nfsea;
22211558Srgrimes
2222192934Srmacklem	if (run_v4server > 0)
2223192934Srmacklem		eap = &nfsea.export;
2224192934Srmacklem	else
2225192934Srmacklem		eap = &ea;
2226192934Srmacklem
2227158857Srodrigc	cp = NULL;
2228158857Srodrigc	savedc = '\0';
2229158857Srodrigc	iov = NULL;
2230158857Srodrigc	iovlen = 0;
2231158857Srodrigc	ret = 0;
223275801Siedowse
2233192934Srmacklem	bzero(eap, sizeof (struct export_args));
2234158857Srodrigc	bzero(errmsg, sizeof(errmsg));
2235192934Srmacklem	eap->ex_flags = exflags;
2236192934Srmacklem	eap->ex_anon = *anoncrp;
2237192934Srmacklem	eap->ex_indexfile = ep->ex_indexfile;
223875641Siedowse	if (grp->gr_type == GT_HOST)
223974462Salfred		ai = grp->gr_ptr.gt_addrinfo;
224075641Siedowse	else
224175641Siedowse		ai = NULL;
2242192934Srmacklem	eap->ex_numsecflavors = ep->ex_numsecflavors;
2243192934Srmacklem	for (i = 0; i < eap->ex_numsecflavors; i++)
2244192934Srmacklem		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2245192934Srmacklem	if (eap->ex_numsecflavors == 0) {
2246192934Srmacklem		eap->ex_numsecflavors = 1;
2247192934Srmacklem		eap->ex_secflavors[0] = AUTH_SYS;
2248184588Sdfr	}
22491558Srgrimes	done = FALSE;
2250158857Srodrigc
2251192934Srmacklem	if (v4root_phase == 0) {
2252192934Srmacklem		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2253192934Srmacklem		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2254192934Srmacklem		build_iovec(&iov, &iovlen, "from", NULL, 0);
2255192934Srmacklem		build_iovec(&iov, &iovlen, "update", NULL, 0);
2256192934Srmacklem		build_iovec(&iov, &iovlen, "export", eap,
2257192934Srmacklem		    sizeof (struct export_args));
2258192934Srmacklem		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2259192934Srmacklem	}
2260158857Srodrigc
22611558Srgrimes	while (!done) {
22621558Srgrimes		switch (grp->gr_type) {
22631558Srgrimes		case GT_HOST:
226475641Siedowse			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
226574462Salfred				goto skip;
2266192934Srmacklem			eap->ex_addr = ai->ai_addr;
2267192934Srmacklem			eap->ex_addrlen = ai->ai_addrlen;
2268192934Srmacklem			eap->ex_masklen = 0;
22691558Srgrimes			break;
22701558Srgrimes		case GT_NET:
227175801Siedowse			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
227274462Salfred			    have_v6 == 0)
227374462Salfred				goto skip;
2274192934Srmacklem			eap->ex_addr =
227575801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2276192934Srmacklem			eap->ex_addrlen =
2277158857Srodrigc			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2278192934Srmacklem			eap->ex_mask =
227975801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2280192934Srmacklem			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
22811558Srgrimes			break;
228275641Siedowse		case GT_DEFAULT:
2283192934Srmacklem			eap->ex_addr = NULL;
2284192934Srmacklem			eap->ex_addrlen = 0;
2285192934Srmacklem			eap->ex_mask = NULL;
2286192934Srmacklem			eap->ex_masklen = 0;
228775641Siedowse			break;
22887401Swpaul		case GT_IGNORE:
2289158857Srodrigc			ret = 0;
2290158857Srodrigc			goto error_exit;
22917401Swpaul			break;
22921558Srgrimes		default:
229337663Scharnier			syslog(LOG_ERR, "bad grouptype");
22941558Srgrimes			if (cp)
22951558Srgrimes				*cp = savedc;
2296158857Srodrigc			ret = 1;
2297158857Srodrigc			goto error_exit;
22981558Srgrimes		};
22991558Srgrimes
23001558Srgrimes		/*
2301192934Srmacklem		 * For V4:, use the nfssvc() syscall, instead of mount().
23021558Srgrimes		 */
2303192934Srmacklem		if (v4root_phase == 2) {
2304192934Srmacklem			nfsea.fspec = v4root_dirpath;
2305192934Srmacklem			if (run_v4server > 0 &&
2306192934Srmacklem			    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2307192934Srmacklem				syslog(LOG_ERR, "Exporting V4: failed");
2308192934Srmacklem				return (2);
2309158857Srodrigc			}
2310192934Srmacklem		} else {
2311192934Srmacklem			/*
2312192934Srmacklem			 * XXX:
2313192934Srmacklem			 * Maybe I should just use the fsb->f_mntonname path
2314192934Srmacklem			 * instead of looping back up the dirp to the mount
2315192934Srmacklem			 * point??
2316192934Srmacklem			 * Also, needs to know how to export all types of local
2317192934Srmacklem			 * exportable filesystems and not just "ufs".
2318192934Srmacklem			 */
2319192934Srmacklem			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2320192934Srmacklem			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2321192934Srmacklem			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2322192934Srmacklem			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2323192934Srmacklem			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2324192934Srmacklem			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2325192934Srmacklem
2326192934Srmacklem			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2327192934Srmacklem				if (cp)
2328192934Srmacklem					*cp-- = savedc;
2329192934Srmacklem				else
2330192934Srmacklem					cp = dirp + dirplen - 1;
2331192934Srmacklem				if (opt_flags & OP_QUIET) {
2332192934Srmacklem					ret = 1;
2333192934Srmacklem					goto error_exit;
2334192934Srmacklem				}
2335192934Srmacklem				if (errno == EPERM) {
2336192934Srmacklem					if (debug)
2337192934Srmacklem						warnx("can't change attributes for %s",
2338192934Srmacklem						    dirp);
2339192934Srmacklem					syslog(LOG_ERR,
2340192934Srmacklem					   "can't change attributes for %s",
234175635Siedowse					    dirp);
2342192934Srmacklem					ret = 1;
2343192934Srmacklem					goto error_exit;
2344192934Srmacklem				}
2345192934Srmacklem				if (opt_flags & OP_ALLDIRS) {
2346192934Srmacklem					if (errno == EINVAL)
2347192934Srmacklem						syslog(LOG_ERR,
2348100336Sjoerg		"-alldirs requested but %s is not a filesystem mountpoint",
2349192934Srmacklem						    dirp);
2350192934Srmacklem					else
2351192934Srmacklem						syslog(LOG_ERR,
2352192934Srmacklem						    "could not remount %s: %m",
2353192934Srmacklem						    dirp);
2354192934Srmacklem					ret = 1;
2355192934Srmacklem					goto error_exit;
2356192934Srmacklem				}
2357192934Srmacklem				/* back up over the last component */
2358192934Srmacklem				while (*cp == '/' && cp > dirp)
2359192934Srmacklem					cp--;
2360192934Srmacklem				while (*(cp - 1) != '/' && cp > dirp)
2361192934Srmacklem					cp--;
2362192934Srmacklem				if (cp == dirp) {
2363192934Srmacklem					if (debug)
2364192934Srmacklem						warnx("mnt unsucc");
2365192934Srmacklem					syslog(LOG_ERR, "can't export %s %s",
2366192934Srmacklem					    dirp, errmsg);
2367192934Srmacklem					ret = 1;
2368192934Srmacklem					goto error_exit;
2369192934Srmacklem				}
2370192934Srmacklem				savedc = *cp;
2371192934Srmacklem				*cp = '\0';
2372192934Srmacklem				/*
2373192934Srmacklem				 * Check that we're still on the same
2374192934Srmacklem				 * filesystem.
2375192934Srmacklem				 */
2376192934Srmacklem				if (statfs(dirp, &fsb1) != 0 ||
2377192934Srmacklem				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2378192934Srmacklem				    sizeof (fsb1.f_fsid)) != 0) {
2379192934Srmacklem					*cp = savedc;
2380100336Sjoerg					syslog(LOG_ERR,
2381192934Srmacklem					    "can't export %s %s", dirp,
2382192934Srmacklem					    errmsg);
2383192934Srmacklem					ret = 1;
2384192934Srmacklem					goto error_exit;
2385192934Srmacklem				}
23861558Srgrimes			}
23871558Srgrimes		}
2388192934Srmacklem
2389192934Srmacklem		/*
2390192934Srmacklem		 * For the experimental server:
2391192934Srmacklem		 * If this is the public directory, get the file handle
2392192934Srmacklem		 * and load it into the kernel via the nfssvc() syscall.
2393192934Srmacklem		 */
2394192934Srmacklem		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
2395192934Srmacklem			fhandle_t fh;
2396192934Srmacklem			char *public_name;
2397192934Srmacklem
2398192934Srmacklem			if (eap->ex_indexfile != NULL)
2399192934Srmacklem				public_name = eap->ex_indexfile;
2400192934Srmacklem			else
2401192934Srmacklem				public_name = dirp;
2402192934Srmacklem			if (getfh(public_name, &fh) < 0)
2403192934Srmacklem				syslog(LOG_ERR,
2404192934Srmacklem				    "Can't get public fh for %s", public_name);
2405192934Srmacklem			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2406192934Srmacklem				syslog(LOG_ERR,
2407192934Srmacklem				    "Can't set public fh for %s", public_name);
2408192934Srmacklem			else
2409192934Srmacklem				has_publicfh = 1;
2410192934Srmacklem		}
241174462Salfredskip:
241275641Siedowse		if (ai != NULL)
241374462Salfred			ai = ai->ai_next;
241475641Siedowse		if (ai == NULL)
24151558Srgrimes			done = TRUE;
24161558Srgrimes	}
24171558Srgrimes	if (cp)
24181558Srgrimes		*cp = savedc;
2419158857Srodrigcerror_exit:
2420158857Srodrigc	/* free strings allocated by strdup() in getmntopts.c */
2421158857Srodrigc	if (iov != NULL) {
2422158857Srodrigc		free(iov[0].iov_base); /* fstype */
2423158857Srodrigc		free(iov[2].iov_base); /* fspath */
2424158857Srodrigc		free(iov[4].iov_base); /* from */
2425158857Srodrigc		free(iov[6].iov_base); /* update */
2426158857Srodrigc		free(iov[8].iov_base); /* export */
2427158857Srodrigc		free(iov[10].iov_base); /* errmsg */
2428158857Srodrigc
2429158857Srodrigc		/* free iov, allocated by realloc() */
2430158857Srodrigc		free(iov);
2431158857Srodrigc	}
2432158857Srodrigc	return (ret);
24331558Srgrimes}
24341558Srgrimes
24351558Srgrimes/*
24361558Srgrimes * Translate a net address.
243775801Siedowse *
243875801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
24391558Srgrimes */
24401558Srgrimesint
24411558Srgrimesget_net(cp, net, maskflg)
24421558Srgrimes	char *cp;
24431558Srgrimes	struct netmsk *net;
24441558Srgrimes	int maskflg;
24451558Srgrimes{
244675861Siedowse	struct netent *np = NULL;
244774462Salfred	char *name, *p, *prefp;
244875801Siedowse	struct sockaddr_in sin;
244975861Siedowse	struct sockaddr *sa = NULL;
245074462Salfred	struct addrinfo hints, *ai = NULL;
245174462Salfred	char netname[NI_MAXHOST];
245274462Salfred	long preflen;
24531558Srgrimes
245475635Siedowse	p = prefp = NULL;
245574462Salfred	if ((opt_flags & OP_MASKLEN) && !maskflg) {
245674462Salfred		p = strchr(cp, '/');
245774462Salfred		*p = '\0';
245874462Salfred		prefp = p + 1;
245974462Salfred	}
246074462Salfred
246175861Siedowse	/*
246275861Siedowse	 * Check for a numeric address first. We wish to avoid
246375861Siedowse	 * possible DNS lookups in getnetbyname().
246475861Siedowse	 */
246575861Siedowse	if (isxdigit(*cp) || *cp == ':') {
246674462Salfred		memset(&hints, 0, sizeof hints);
246775801Siedowse		/* Ensure the mask and the network have the same family. */
246875801Siedowse		if (maskflg && (opt_flags & OP_NET))
246975801Siedowse			hints.ai_family = net->nt_net.ss_family;
247075801Siedowse		else if (!maskflg && (opt_flags & OP_HAVEMASK))
247175801Siedowse			hints.ai_family = net->nt_mask.ss_family;
247275801Siedowse		else
247375801Siedowse			hints.ai_family = AF_UNSPEC;
247474462Salfred		hints.ai_flags = AI_NUMERICHOST;
247575861Siedowse		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
247675861Siedowse			sa = ai->ai_addr;
247775861Siedowse		if (sa != NULL && ai->ai_family == AF_INET) {
247874462Salfred			/*
247975801Siedowse			 * The address in `cp' is really a network address, so
248075801Siedowse			 * use inet_network() to re-interpret this correctly.
248175801Siedowse			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
248274462Salfred			 */
248375801Siedowse			bzero(&sin, sizeof sin);
248474462Salfred			sin.sin_family = AF_INET;
248574462Salfred			sin.sin_len = sizeof sin;
248675801Siedowse			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
248774462Salfred			if (debug)
248875801Siedowse				fprintf(stderr, "get_net: v4 addr %s\n",
248975801Siedowse				    inet_ntoa(sin.sin_addr));
249074462Salfred			sa = (struct sockaddr *)&sin;
249175861Siedowse		}
249275861Siedowse	}
249375861Siedowse	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
249475861Siedowse		bzero(&sin, sizeof sin);
249575861Siedowse		sin.sin_family = AF_INET;
249675861Siedowse		sin.sin_len = sizeof sin;
249775861Siedowse		sin.sin_addr = inet_makeaddr(np->n_net, 0);
249875861Siedowse		sa = (struct sockaddr *)&sin;
249975861Siedowse	}
250075861Siedowse	if (sa == NULL)
250174462Salfred		goto fail;
250225318Spst
250375801Siedowse	if (maskflg) {
250475801Siedowse		/* The specified sockaddr is a mask. */
250575801Siedowse		if (checkmask(sa) != 0)
250675801Siedowse			goto fail;
250775801Siedowse		bcopy(sa, &net->nt_mask, sa->sa_len);
250875801Siedowse		opt_flags |= OP_HAVEMASK;
250975801Siedowse	} else {
251075801Siedowse		/* The specified sockaddr is a network address. */
251175801Siedowse		bcopy(sa, &net->nt_net, sa->sa_len);
251274462Salfred
251375801Siedowse		/* Get a network name for the export list. */
251475801Siedowse		if (np) {
251575801Siedowse			name = np->n_name;
251675801Siedowse		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2517146187Sume		   NULL, 0, NI_NUMERICHOST) == 0) {
251875801Siedowse			name = netname;
251975801Siedowse		} else {
252075801Siedowse			goto fail;
252175801Siedowse		}
252275801Siedowse		if ((net->nt_name = strdup(name)) == NULL)
252375801Siedowse			out_of_mem();
252475801Siedowse
252575801Siedowse		/*
252675801Siedowse		 * Extract a mask from either a "/<masklen>" suffix, or
252775801Siedowse		 * from the class of an IPv4 address.
252875801Siedowse		 */
252974462Salfred		if (opt_flags & OP_MASKLEN) {
253074462Salfred			preflen = strtol(prefp, NULL, 10);
253175801Siedowse			if (preflen < 0L || preflen == LONG_MAX)
253274462Salfred				goto fail;
253375801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
253475801Siedowse			if (makemask(&net->nt_mask, (int)preflen) != 0)
253575801Siedowse				goto fail;
253675801Siedowse			opt_flags |= OP_HAVEMASK;
253774462Salfred			*p = '/';
253875801Siedowse		} else if (sa->sa_family == AF_INET &&
253975801Siedowse		    (opt_flags & OP_MASK) == 0) {
254075801Siedowse			in_addr_t addr;
254174462Salfred
254275801Siedowse			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
254375801Siedowse			if (IN_CLASSA(addr))
254475801Siedowse				preflen = 8;
254575801Siedowse			else if (IN_CLASSB(addr))
254675801Siedowse				preflen = 16;
254775801Siedowse			else if (IN_CLASSC(addr))
254875801Siedowse				preflen = 24;
254975801Siedowse			else if (IN_CLASSD(addr))
255075801Siedowse				preflen = 28;
255175801Siedowse			else
255275801Siedowse				preflen = 32;	/* XXX */
255375801Siedowse
255475801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
255575801Siedowse			makemask(&net->nt_mask, (int)preflen);
255675801Siedowse			opt_flags |= OP_HAVEMASK;
255774462Salfred		}
255874462Salfred	}
255974462Salfred
256074462Salfred	if (ai)
256174462Salfred		freeaddrinfo(ai);
256274462Salfred	return 0;
256374462Salfred
256474462Salfredfail:
256574462Salfred	if (ai)
256674462Salfred		freeaddrinfo(ai);
256774462Salfred	return 1;
25681558Srgrimes}
25691558Srgrimes
25701558Srgrimes/*
25711558Srgrimes * Parse out the next white space separated field
25721558Srgrimes */
25731558Srgrimesvoid
25741558Srgrimesnextfield(cp, endcp)
25751558Srgrimes	char **cp;
25761558Srgrimes	char **endcp;
25771558Srgrimes{
25781558Srgrimes	char *p;
25791558Srgrimes
25801558Srgrimes	p = *cp;
25811558Srgrimes	while (*p == ' ' || *p == '\t')
25821558Srgrimes		p++;
25831558Srgrimes	if (*p == '\n' || *p == '\0')
25841558Srgrimes		*cp = *endcp = p;
25851558Srgrimes	else {
25861558Srgrimes		*cp = p++;
25871558Srgrimes		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
25881558Srgrimes			p++;
25891558Srgrimes		*endcp = p;
25901558Srgrimes	}
25911558Srgrimes}
25921558Srgrimes
25931558Srgrimes/*
25941558Srgrimes * Get an exports file line. Skip over blank lines and handle line
25951558Srgrimes * continuations.
25961558Srgrimes */
25971558Srgrimesint
25981558Srgrimesget_line()
25991558Srgrimes{
26001558Srgrimes	char *p, *cp;
260196622Siedowse	size_t len;
26021558Srgrimes	int totlen, cont_line;
26031558Srgrimes
26041558Srgrimes	/*
26051558Srgrimes	 * Loop around ignoring blank lines and getting all continuation lines.
26061558Srgrimes	 */
26071558Srgrimes	p = line;
26081558Srgrimes	totlen = 0;
26091558Srgrimes	do {
261096622Siedowse		if ((p = fgetln(exp_file, &len)) == NULL)
26111558Srgrimes			return (0);
26121558Srgrimes		cp = p + len - 1;
26131558Srgrimes		cont_line = 0;
26141558Srgrimes		while (cp >= p &&
26151558Srgrimes		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
26161558Srgrimes			if (*cp == '\\')
26171558Srgrimes				cont_line = 1;
26181558Srgrimes			cp--;
26191558Srgrimes			len--;
26201558Srgrimes		}
262179117Sdd		if (cont_line) {
262279117Sdd			*++cp = ' ';
262379117Sdd			len++;
262479117Sdd		}
262596622Siedowse		if (linesize < len + totlen + 1) {
262696622Siedowse			linesize = len + totlen + 1;
262796622Siedowse			line = realloc(line, linesize);
262896622Siedowse			if (line == NULL)
262996622Siedowse				out_of_mem();
26301558Srgrimes		}
263196622Siedowse		memcpy(line + totlen, p, len);
263296622Siedowse		totlen += len;
263396622Siedowse		line[totlen] = '\0';
26341558Srgrimes	} while (totlen == 0 || cont_line);
26351558Srgrimes	return (1);
26361558Srgrimes}
26371558Srgrimes
26381558Srgrimes/*
26391558Srgrimes * Parse a description of a credential.
26401558Srgrimes */
26411558Srgrimesvoid
26421558Srgrimesparsecred(namelist, cr)
26431558Srgrimes	char *namelist;
264472650Sgreen	struct xucred *cr;
26451558Srgrimes{
26461558Srgrimes	char *name;
26471558Srgrimes	int cnt;
26481558Srgrimes	char *names;
26491558Srgrimes	struct passwd *pw;
26501558Srgrimes	struct group *gr;
2651194498Sbrooks	gid_t groups[XU_NGROUPS + 1];
2652136051Sstefanf	int ngroups;
26531558Srgrimes
265491354Sdd	cr->cr_version = XUCRED_VERSION;
26551558Srgrimes	/*
265637663Scharnier	 * Set up the unprivileged user.
26571558Srgrimes	 */
26581558Srgrimes	cr->cr_uid = -2;
26591558Srgrimes	cr->cr_groups[0] = -2;
26601558Srgrimes	cr->cr_ngroups = 1;
26611558Srgrimes	/*
26621558Srgrimes	 * Get the user's password table entry.
26631558Srgrimes	 */
26641558Srgrimes	names = strsep(&namelist, " \t\n");
26651558Srgrimes	name = strsep(&names, ":");
26661558Srgrimes	if (isdigit(*name) || *name == '-')
26671558Srgrimes		pw = getpwuid(atoi(name));
26681558Srgrimes	else
26691558Srgrimes		pw = getpwnam(name);
26701558Srgrimes	/*
26711558Srgrimes	 * Credentials specified as those of a user.
26721558Srgrimes	 */
26731558Srgrimes	if (names == NULL) {
26741558Srgrimes		if (pw == NULL) {
267537663Scharnier			syslog(LOG_ERR, "unknown user: %s", name);
26761558Srgrimes			return;
26771558Srgrimes		}
26781558Srgrimes		cr->cr_uid = pw->pw_uid;
2679194498Sbrooks		ngroups = XU_NGROUPS + 1;
26801558Srgrimes		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
268137663Scharnier			syslog(LOG_ERR, "too many groups");
26821558Srgrimes		/*
2683136051Sstefanf		 * Compress out duplicate.
26841558Srgrimes		 */
26851558Srgrimes		cr->cr_ngroups = ngroups - 1;
26861558Srgrimes		cr->cr_groups[0] = groups[0];
26871558Srgrimes		for (cnt = 2; cnt < ngroups; cnt++)
26881558Srgrimes			cr->cr_groups[cnt - 1] = groups[cnt];
26891558Srgrimes		return;
26901558Srgrimes	}
26911558Srgrimes	/*
26921558Srgrimes	 * Explicit credential specified as a colon separated list:
26931558Srgrimes	 *	uid:gid:gid:...
26941558Srgrimes	 */
26951558Srgrimes	if (pw != NULL)
26961558Srgrimes		cr->cr_uid = pw->pw_uid;
26971558Srgrimes	else if (isdigit(*name) || *name == '-')
26981558Srgrimes		cr->cr_uid = atoi(name);
26991558Srgrimes	else {
270037663Scharnier		syslog(LOG_ERR, "unknown user: %s", name);
27011558Srgrimes		return;
27021558Srgrimes	}
27031558Srgrimes	cr->cr_ngroups = 0;
2704194498Sbrooks	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
27051558Srgrimes		name = strsep(&names, ":");
27061558Srgrimes		if (isdigit(*name) || *name == '-') {
27071558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
27081558Srgrimes		} else {
27091558Srgrimes			if ((gr = getgrnam(name)) == NULL) {
271037663Scharnier				syslog(LOG_ERR, "unknown group: %s", name);
27111558Srgrimes				continue;
27121558Srgrimes			}
27131558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
27141558Srgrimes		}
27151558Srgrimes	}
2716194498Sbrooks	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
271737663Scharnier		syslog(LOG_ERR, "too many groups");
27181558Srgrimes}
27191558Srgrimes
2720194880Sdfr#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
27211558Srgrimes/*
27221558Srgrimes * Routines that maintain the remote mounttab
27231558Srgrimes */
27241558Srgrimesvoid
27251558Srgrimesget_mountlist()
27261558Srgrimes{
27271558Srgrimes	struct mountlist *mlp, **mlpp;
272823681Speter	char *host, *dirp, *cp;
27291558Srgrimes	char str[STRSIZ];
27301558Srgrimes	FILE *mlfile;
27311558Srgrimes
27321558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
273353117Sbillf		if (errno == ENOENT)
273453117Sbillf			return;
273553117Sbillf		else {
273653117Sbillf			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
273753117Sbillf			return;
273853117Sbillf		}
27391558Srgrimes	}
27401558Srgrimes	mlpp = &mlhead;
27411558Srgrimes	while (fgets(str, STRSIZ, mlfile) != NULL) {
274223681Speter		cp = str;
274323681Speter		host = strsep(&cp, " \t\n");
274423681Speter		dirp = strsep(&cp, " \t\n");
274523681Speter		if (host == NULL || dirp == NULL)
27461558Srgrimes			continue;
27471558Srgrimes		mlp = (struct mountlist *)malloc(sizeof (*mlp));
274837663Scharnier		if (mlp == (struct mountlist *)NULL)
274937663Scharnier			out_of_mem();
2750194880Sdfr		strncpy(mlp->ml_host, host, MNTNAMLEN);
2751194880Sdfr		mlp->ml_host[MNTNAMLEN] = '\0';
2752194880Sdfr		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2753194880Sdfr		mlp->ml_dirp[MNTPATHLEN] = '\0';
27541558Srgrimes		mlp->ml_next = (struct mountlist *)NULL;
27551558Srgrimes		*mlpp = mlp;
27561558Srgrimes		mlpp = &mlp->ml_next;
27571558Srgrimes	}
27581558Srgrimes	fclose(mlfile);
27591558Srgrimes}
27601558Srgrimes
276175635Siedowsevoid
276275635Siedowsedel_mlist(char *hostp, char *dirp)
27631558Srgrimes{
27641558Srgrimes	struct mountlist *mlp, **mlpp;
27651558Srgrimes	struct mountlist *mlp2;
27661558Srgrimes	FILE *mlfile;
27671558Srgrimes	int fnd = 0;
27681558Srgrimes
27691558Srgrimes	mlpp = &mlhead;
27701558Srgrimes	mlp = mlhead;
27711558Srgrimes	while (mlp) {
27721558Srgrimes		if (!strcmp(mlp->ml_host, hostp) &&
27731558Srgrimes		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
27741558Srgrimes			fnd = 1;
27751558Srgrimes			mlp2 = mlp;
27761558Srgrimes			*mlpp = mlp = mlp->ml_next;
27771558Srgrimes			free((caddr_t)mlp2);
27781558Srgrimes		} else {
27791558Srgrimes			mlpp = &mlp->ml_next;
27801558Srgrimes			mlp = mlp->ml_next;
27811558Srgrimes		}
27821558Srgrimes	}
27831558Srgrimes	if (fnd) {
27841558Srgrimes		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
278537663Scharnier			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
27861558Srgrimes			return;
27871558Srgrimes		}
27881558Srgrimes		mlp = mlhead;
27891558Srgrimes		while (mlp) {
27901558Srgrimes			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
27911558Srgrimes			mlp = mlp->ml_next;
27921558Srgrimes		}
27931558Srgrimes		fclose(mlfile);
27941558Srgrimes	}
27951558Srgrimes}
27961558Srgrimes
27971558Srgrimesvoid
27981558Srgrimesadd_mlist(hostp, dirp)
27991558Srgrimes	char *hostp, *dirp;
28001558Srgrimes{
28011558Srgrimes	struct mountlist *mlp, **mlpp;
28021558Srgrimes	FILE *mlfile;
28031558Srgrimes
28041558Srgrimes	mlpp = &mlhead;
28051558Srgrimes	mlp = mlhead;
28061558Srgrimes	while (mlp) {
28071558Srgrimes		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
28081558Srgrimes			return;
28091558Srgrimes		mlpp = &mlp->ml_next;
28101558Srgrimes		mlp = mlp->ml_next;
28111558Srgrimes	}
28121558Srgrimes	mlp = (struct mountlist *)malloc(sizeof (*mlp));
281337663Scharnier	if (mlp == (struct mountlist *)NULL)
281437663Scharnier		out_of_mem();
2815194880Sdfr	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
2816194880Sdfr	mlp->ml_host[MNTNAMLEN] = '\0';
2817194880Sdfr	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2818194880Sdfr	mlp->ml_dirp[MNTPATHLEN] = '\0';
28191558Srgrimes	mlp->ml_next = (struct mountlist *)NULL;
28201558Srgrimes	*mlpp = mlp;
28211558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
282237663Scharnier		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
28231558Srgrimes		return;
28241558Srgrimes	}
28251558Srgrimes	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
28261558Srgrimes	fclose(mlfile);
28271558Srgrimes}
28281558Srgrimes
28291558Srgrimes/*
28301558Srgrimes * Free up a group list.
28311558Srgrimes */
28321558Srgrimesvoid
28331558Srgrimesfree_grp(grp)
28341558Srgrimes	struct grouplist *grp;
28351558Srgrimes{
28361558Srgrimes	if (grp->gr_type == GT_HOST) {
283774462Salfred		if (grp->gr_ptr.gt_addrinfo != NULL)
283874462Salfred			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
28391558Srgrimes	} else if (grp->gr_type == GT_NET) {
28401558Srgrimes		if (grp->gr_ptr.gt_net.nt_name)
28411558Srgrimes			free(grp->gr_ptr.gt_net.nt_name);
28421558Srgrimes	}
28431558Srgrimes	free((caddr_t)grp);
28441558Srgrimes}
28451558Srgrimes
28461558Srgrimes#ifdef DEBUG
28471558Srgrimesvoid
28481558SrgrimesSYSLOG(int pri, const char *fmt, ...)
28491558Srgrimes{
28501558Srgrimes	va_list ap;
28511558Srgrimes
28521558Srgrimes	va_start(ap, fmt);
28531558Srgrimes	vfprintf(stderr, fmt, ap);
28541558Srgrimes	va_end(ap);
28551558Srgrimes}
28561558Srgrimes#endif /* DEBUG */
28571558Srgrimes
28581558Srgrimes/*
28591558Srgrimes * Check options for consistency.
28601558Srgrimes */
28611558Srgrimesint
28621558Srgrimescheck_options(dp)
28631558Srgrimes	struct dirlist *dp;
28641558Srgrimes{
28651558Srgrimes
2866192934Srmacklem	if (v4root_phase == 0 && dp == NULL)
28671558Srgrimes	    return (1);
286883653Speter	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
286983653Speter	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
28701558Srgrimes	    return (1);
28711558Srgrimes	}
28721558Srgrimes	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
287375801Siedowse		syslog(LOG_ERR, "-mask requires -network");
287475801Siedowse		return (1);
28751558Srgrimes	}
287675801Siedowse	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
287775801Siedowse		syslog(LOG_ERR, "-network requires mask specification");
287875801Siedowse		return (1);
287975801Siedowse	}
288075801Siedowse	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
288175801Siedowse		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
288275801Siedowse		return (1);
288375801Siedowse	}
28841558Srgrimes	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
288545927Salex	    syslog(LOG_ERR, "-alldirs has multiple directories");
28861558Srgrimes	    return (1);
28871558Srgrimes	}
2888192934Srmacklem	if (v4root_phase > 0 &&
2889192934Srmacklem	    (opt_flags &
2890192934Srmacklem	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
2891192934Srmacklem	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
2892192934Srmacklem	    return (1);
2893192934Srmacklem	}
28941558Srgrimes	return (0);
28951558Srgrimes}
28961558Srgrimes
28971558Srgrimes/*
28981558Srgrimes * Check an absolute directory path for any symbolic links. Return true
28991558Srgrimes */
29001558Srgrimesint
29011558Srgrimescheck_dirpath(dirp)
29021558Srgrimes	char *dirp;
29031558Srgrimes{
29041558Srgrimes	char *cp;
29051558Srgrimes	int ret = 1;
29061558Srgrimes	struct stat sb;
29071558Srgrimes
29081558Srgrimes	cp = dirp + 1;
29091558Srgrimes	while (*cp && ret) {
29101558Srgrimes		if (*cp == '/') {
29111558Srgrimes			*cp = '\0';
29129336Sdfr			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
29131558Srgrimes				ret = 0;
29141558Srgrimes			*cp = '/';
29151558Srgrimes		}
29161558Srgrimes		cp++;
29171558Srgrimes	}
29189336Sdfr	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
29191558Srgrimes		ret = 0;
29201558Srgrimes	return (ret);
29211558Srgrimes}
29229336Sdfr
292375801Siedowse/*
292475801Siedowse * Make a netmask according to the specified prefix length. The ss_family
292575801Siedowse * and other non-address fields must be initialised before calling this.
292675801Siedowse */
292775801Siedowseint
292875801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen)
292974462Salfred{
293075801Siedowse	u_char *p;
293175801Siedowse	int bits, i, len;
293274462Salfred
293375801Siedowse	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
293475801Siedowse		return (-1);
2935103949Smike	if (bitlen > len * CHAR_BIT)
293675801Siedowse		return (-1);
293774462Salfred
293875801Siedowse	for (i = 0; i < len; i++) {
2939103949Smike		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
294075801Siedowse		*p++ = (1 << bits) - 1;
294175801Siedowse		bitlen -= bits;
294274462Salfred	}
294375801Siedowse	return 0;
294474462Salfred}
294574462Salfred
294675801Siedowse/*
294775801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask
294875801Siedowse * is acceptable (i.e. of the form 1...10....0).
294975801Siedowse */
295075801Siedowseint
295175801Siedowsecheckmask(struct sockaddr *sa)
295274462Salfred{
295375801Siedowse	u_char *mask;
295475801Siedowse	int i, len;
295574462Salfred
295675801Siedowse	if ((mask = sa_rawaddr(sa, &len)) == NULL)
295775801Siedowse		return (-1);
295875801Siedowse
295975801Siedowse	for (i = 0; i < len; i++)
296075801Siedowse		if (mask[i] != 0xff)
296175801Siedowse			break;
296275801Siedowse	if (i < len) {
296375801Siedowse		if (~mask[i] & (u_char)(~mask[i] + 1))
296475801Siedowse			return (-1);
296575801Siedowse		i++;
296674462Salfred	}
296775801Siedowse	for (; i < len; i++)
296875801Siedowse		if (mask[i] != 0)
296975801Siedowse			return (-1);
297075801Siedowse	return (0);
297174462Salfred}
297274462Salfred
297375801Siedowse/*
297475801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if
297575801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'.
297675801Siedowse * If samask is NULL, perform a full comparision.
297775801Siedowse */
297875801Siedowseint
297975801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
298074462Salfred{
298175801Siedowse	unsigned char *p1, *p2, *mask;
298275801Siedowse	int len, i;
298374462Salfred
298475801Siedowse	if (sa1->sa_family != sa2->sa_family ||
298575801Siedowse	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
298675801Siedowse	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
298775801Siedowse		return (1);
298875801Siedowse
298975801Siedowse	switch (sa1->sa_family) {
299074462Salfred	case AF_INET6:
299175801Siedowse		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
299275801Siedowse		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
299375801Siedowse			return (1);
299474462Salfred		break;
299574462Salfred	}
299674462Salfred
299775801Siedowse	/* Simple binary comparison if no mask specified. */
299875801Siedowse	if (samask == NULL)
299975801Siedowse		return (memcmp(p1, p2, len));
300074462Salfred
300175801Siedowse	/* Set up the mask, and do a mask-based comparison. */
300275801Siedowse	if (sa1->sa_family != samask->sa_family ||
300375801Siedowse	    (mask = sa_rawaddr(samask, NULL)) == NULL)
300475801Siedowse		return (1);
300574462Salfred
300675801Siedowse	for (i = 0; i < len; i++)
300775801Siedowse		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
300875801Siedowse			return (1);
300975801Siedowse	return (0);
301074462Salfred}
301174462Salfred
301275801Siedowse/*
301375801Siedowse * Return a pointer to the part of the sockaddr that contains the
301475801Siedowse * raw address, and set *nbytes to its length in bytes. Returns
301575801Siedowse * NULL if the address family is unknown.
301675801Siedowse */
301775801Siedowsevoid *
301875801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) {
301975801Siedowse	void *p;
302074462Salfred	int len;
302174462Salfred
302275801Siedowse	switch (sa->sa_family) {
302374462Salfred	case AF_INET:
302475801Siedowse		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
302575801Siedowse		p = &((struct sockaddr_in *)sa)->sin_addr;
302674462Salfred		break;
302774462Salfred	case AF_INET6:
302875801Siedowse		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
302975801Siedowse		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
303074462Salfred		break;
303174462Salfred	default:
303275801Siedowse		p = NULL;
303375801Siedowse		len = 0;
303474462Salfred	}
303574462Salfred
303675801Siedowse	if (nbytes != NULL)
303775801Siedowse		*nbytes = len;
303875801Siedowse	return (p);
303974462Salfred}
304074462Salfred
304175754Siedowsevoid
304275754Siedowsehuphandler(int sig)
304375754Siedowse{
304475754Siedowse	got_sighup = 1;
304575754Siedowse}
304675754Siedowse
304774462Salfredvoid terminate(sig)
304874462Salfredint sig;
304974462Salfred{
3050149433Spjd	pidfile_remove(pfh);
3051194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3052194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
305374462Salfred	exit (0);
305474462Salfred}
3055192934Srmacklem
3056