mountd.c revision 216587
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 216587 2010-12-20 09:28:28Z charnier $");
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 *);
199216587Scharnierint	parsesec(char *, struct exportlist *);
200100117Salfredint	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
20175801Siedowsevoid	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
20275801Siedowseint	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
20375801Siedowse    struct sockaddr *samask);
20492882Simpint	scan_tree(struct dirlist *, struct sockaddr *);
20592882Simpstatic void usage(void);
20692882Simpint	xdr_dir(XDR *, char *);
20792882Simpint	xdr_explist(XDR *, caddr_t);
208100117Salfredint	xdr_explist_brief(XDR *, caddr_t);
209216587Scharnierint	xdr_explist_common(XDR *, caddr_t, int);
21092882Simpint	xdr_fhs(XDR *, caddr_t);
21192882Simpint	xdr_mlist(XDR *, caddr_t);
21292882Simpvoid	terminate(int);
2131558Srgrimes
2141558Srgrimesstruct exportlist *exphead;
2151558Srgrimesstruct mountlist *mlhead;
2161558Srgrimesstruct grouplist *grphead;
217166440Spjdchar *exnames_default[2] = { _PATH_EXPORTS, NULL };
218166440Spjdchar **exnames;
219172827Smatteochar **hosts = NULL;
22072650Sgreenstruct xucred def_anon = {
22191354Sdd	XUCRED_VERSION,
22272650Sgreen	(uid_t)-2,
2231558Srgrimes	1,
22472650Sgreen	{ (gid_t)-2 },
22572650Sgreen	NULL
2261558Srgrimes};
22725087Sdfrint force_v2 = 0;
2289336Sdfrint resvport_only = 1;
229172827Smatteoint nhosts = 0;
2309336Sdfrint dir_only = 1;
231121767Speterint dolog = 0;
23275754Siedowseint got_sighup = 0;
233172827Smatteoint xcreated = 0;
23474462Salfred
235172827Smatteochar *svcport_str = NULL;
236172827Smatteo
2371558Srgrimesint opt_flags;
23874462Salfredstatic int have_v6 = 1;
23974462Salfred
240192934Srmacklemint v4root_phase = 0;
241192934Srmacklemchar v4root_dirpath[PATH_MAX + 1];
242192934Srmacklemint run_v4server = 0;
243192934Srmacklemint has_publicfh = 0;
244192934Srmacklem
245149433Spjdstruct pidfh *pfh = NULL;
24675801Siedowse/* Bits for opt_flags above */
2471558Srgrimes#define	OP_MAPROOT	0x01
2481558Srgrimes#define	OP_MAPALL	0x02
24983653Speter/* 0x4 free */
2501558Srgrimes#define	OP_MASK		0x08
2511558Srgrimes#define	OP_NET		0x10
2521558Srgrimes#define	OP_ALLDIRS	0x40
25375801Siedowse#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
254100336Sjoerg#define	OP_QUIET	0x100
25574462Salfred#define OP_MASKLEN	0x200
256184588Sdfr#define OP_SEC		0x400
2571558Srgrimes
2581558Srgrimes#ifdef DEBUG
2591558Srgrimesint debug = 1;
26092882Simpvoid	SYSLOG(int, const char *, ...) __printflike(2, 3);
2611558Srgrimes#define syslog SYSLOG
2621558Srgrimes#else
2631558Srgrimesint debug = 0;
2641558Srgrimes#endif
2651558Srgrimes
2661558Srgrimes/*
2671558Srgrimes * Mountd server for NFS mount protocol as described in:
2681558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A
2691558Srgrimes * The optional arguments are the exports file name
2701558Srgrimes * default: _PATH_EXPORTS
2711558Srgrimes * and "-n" to allow nonroot mount.
2721558Srgrimes */
2731558Srgrimesint
274216587Scharniermain(int argc, 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
732216587Scharnierusage(void)
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
744216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp)
7451558Srgrimes{
7461558Srgrimes	struct exportlist *ep;
7471558Srgrimes	struct dirlist *dp;
7489336Sdfr	struct fhreturn fhr;
7491558Srgrimes	struct stat stb;
7501558Srgrimes	struct statfs fsb;
75174462Salfred	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
75274462Salfred	int lookup_failed = 1;
75374462Salfred	struct sockaddr *saddr;
7549336Sdfr	u_short sport;
755194880Sdfr	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
75628911Sguido	int bad = 0, defset, hostset;
7579336Sdfr	sigset_t sighup_mask;
7581558Srgrimes
7599336Sdfr	sigemptyset(&sighup_mask);
7609336Sdfr	sigaddset(&sighup_mask, SIGHUP);
76174462Salfred	saddr = svc_getrpccaller(transp)->buf;
76274462Salfred	switch (saddr->sa_family) {
76374462Salfred	case AF_INET6:
76475635Siedowse		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
76574462Salfred		break;
76674462Salfred	case AF_INET:
76775635Siedowse		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
76874462Salfred		break;
76974462Salfred	default:
77074462Salfred		syslog(LOG_ERR, "request from unknown address family");
77174462Salfred		return;
77274462Salfred	}
77374462Salfred	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
77474462Salfred	    NULL, 0, 0);
77574462Salfred	getnameinfo(saddr, saddr->sa_len, numerichost,
77674462Salfred	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
7771558Srgrimes	switch (rqstp->rq_proc) {
7781558Srgrimes	case NULLPROC:
779121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
78037663Scharnier			syslog(LOG_ERR, "can't send reply");
7811558Srgrimes		return;
782194880Sdfr	case MOUNTPROC_MNT:
7839336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
78431656Sguido			syslog(LOG_NOTICE,
78531656Sguido			    "mount request from %s from unprivileged port",
78674462Salfred			    numerichost);
7871558Srgrimes			svcerr_weakauth(transp);
7881558Srgrimes			return;
7891558Srgrimes		}
790121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
79131656Sguido			syslog(LOG_NOTICE, "undecodable mount request from %s",
79274462Salfred			    numerichost);
7931558Srgrimes			svcerr_decode(transp);
7941558Srgrimes			return;
7951558Srgrimes		}
7961558Srgrimes
7971558Srgrimes		/*
7981558Srgrimes		 * Get the real pathname and make sure it is a directory
7999336Sdfr		 * or a regular file if the -r option was specified
8009336Sdfr		 * and it exists.
8011558Srgrimes		 */
80251968Salfred		if (realpath(rpcpath, dirpath) == NULL ||
8031558Srgrimes		    stat(dirpath, &stb) < 0 ||
8049336Sdfr		    (!S_ISDIR(stb.st_mode) &&
80574462Salfred		    (dir_only || !S_ISREG(stb.st_mode))) ||
8061558Srgrimes		    statfs(dirpath, &fsb) < 0) {
8071558Srgrimes			chdir("/");	/* Just in case realpath doesn't */
80831656Sguido			syslog(LOG_NOTICE,
80937663Scharnier			    "mount request from %s for non existent path %s",
81074462Salfred			    numerichost, dirpath);
8111558Srgrimes			if (debug)
81237663Scharnier				warnx("stat failed on %s", dirpath);
81328911Sguido			bad = ENOENT;	/* We will send error reply later */
8141558Srgrimes		}
8151558Srgrimes
8161558Srgrimes		/* Check in the exports list */
8179336Sdfr		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
8181558Srgrimes		ep = ex_search(&fsb.f_fsid);
8199336Sdfr		hostset = defset = 0;
8209336Sdfr		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
8211558Srgrimes		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
82274462Salfred		      chk_host(dp, saddr, &defset, &hostset)) ||
82374462Salfred		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
82474462Salfred		     scan_tree(ep->ex_dirl, saddr) == 0))) {
82528911Sguido			if (bad) {
826121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
82728911Sguido				    (caddr_t)&bad))
82837663Scharnier					syslog(LOG_ERR, "can't send reply");
82928911Sguido				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
83028911Sguido				return;
83128911Sguido			}
8329336Sdfr			if (hostset & DP_HOSTSET)
8339336Sdfr				fhr.fhr_flag = hostset;
8349336Sdfr			else
8359336Sdfr				fhr.fhr_flag = defset;
8369336Sdfr			fhr.fhr_vers = rqstp->rq_vers;
8371558Srgrimes			/* Get the file handle */
83823681Speter			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
8399336Sdfr			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
8401558Srgrimes				bad = errno;
84137663Scharnier				syslog(LOG_ERR, "can't get fh for %s", dirpath);
842121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
8431558Srgrimes				    (caddr_t)&bad))
84437663Scharnier					syslog(LOG_ERR, "can't send reply");
8459336Sdfr				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
8461558Srgrimes				return;
8471558Srgrimes			}
848184588Sdfr			fhr.fhr_numsecflavors = ep->ex_numsecflavors;
849184588Sdfr			fhr.fhr_secflavors = ep->ex_secflavors;
850121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
851121556Speter			    (caddr_t)&fhr))
85237663Scharnier				syslog(LOG_ERR, "can't send reply");
85374462Salfred			if (!lookup_failed)
85474462Salfred				add_mlist(host, dirpath);
8551558Srgrimes			else
85674462Salfred				add_mlist(numerichost, dirpath);
8571558Srgrimes			if (debug)
85837663Scharnier				warnx("mount successful");
859121767Speter			if (dolog)
86031656Sguido				syslog(LOG_NOTICE,
86131656Sguido				    "mount request succeeded from %s for %s",
86274462Salfred				    numerichost, dirpath);
86331656Sguido		} else {
8641558Srgrimes			bad = EACCES;
86531656Sguido			syslog(LOG_NOTICE,
86631656Sguido			    "mount request denied from %s for %s",
86774462Salfred			    numerichost, dirpath);
86831656Sguido		}
86928911Sguido
870121556Speter		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
871121556Speter		    (caddr_t)&bad))
87237663Scharnier			syslog(LOG_ERR, "can't send reply");
8739336Sdfr		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
8741558Srgrimes		return;
875194880Sdfr	case MOUNTPROC_DUMP:
876121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
87737663Scharnier			syslog(LOG_ERR, "can't send reply");
878121767Speter		else if (dolog)
87931656Sguido			syslog(LOG_NOTICE,
88031656Sguido			    "dump request succeeded from %s",
88174462Salfred			    numerichost);
8821558Srgrimes		return;
883194880Sdfr	case MOUNTPROC_UMNT:
8849336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
88531656Sguido			syslog(LOG_NOTICE,
88631656Sguido			    "umount request from %s from unprivileged port",
88774462Salfred			    numerichost);
8881558Srgrimes			svcerr_weakauth(transp);
8891558Srgrimes			return;
8901558Srgrimes		}
891121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
89231656Sguido			syslog(LOG_NOTICE, "undecodable umount request from %s",
89374462Salfred			    numerichost);
8941558Srgrimes			svcerr_decode(transp);
8951558Srgrimes			return;
8961558Srgrimes		}
89751968Salfred		if (realpath(rpcpath, dirpath) == NULL) {
89851968Salfred			syslog(LOG_NOTICE, "umount request from %s "
89951968Salfred			    "for non existent path %s",
90074462Salfred			    numerichost, dirpath);
90151968Salfred		}
902121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
90337663Scharnier			syslog(LOG_ERR, "can't send reply");
90474462Salfred		if (!lookup_failed)
90575635Siedowse			del_mlist(host, dirpath);
90675635Siedowse		del_mlist(numerichost, dirpath);
907121767Speter		if (dolog)
90831656Sguido			syslog(LOG_NOTICE,
90931656Sguido			    "umount request succeeded from %s for %s",
91074462Salfred			    numerichost, dirpath);
9111558Srgrimes		return;
912194880Sdfr	case MOUNTPROC_UMNTALL:
9139336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
91431656Sguido			syslog(LOG_NOTICE,
91531656Sguido			    "umountall request from %s from unprivileged port",
91674462Salfred			    numerichost);
9171558Srgrimes			svcerr_weakauth(transp);
9181558Srgrimes			return;
9191558Srgrimes		}
920121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
92137663Scharnier			syslog(LOG_ERR, "can't send reply");
92274462Salfred		if (!lookup_failed)
92375635Siedowse			del_mlist(host, NULL);
92475635Siedowse		del_mlist(numerichost, NULL);
925121767Speter		if (dolog)
92631656Sguido			syslog(LOG_NOTICE,
92731656Sguido			    "umountall request succeeded from %s",
92874462Salfred			    numerichost);
9291558Srgrimes		return;
930194880Sdfr	case MOUNTPROC_EXPORT:
931121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
932121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
933121556Speter			    (caddr_t)NULL))
934100117Salfred				syslog(LOG_ERR, "can't send reply");
935121767Speter		if (dolog)
93631656Sguido			syslog(LOG_NOTICE,
93731656Sguido			    "export request succeeded from %s",
93874462Salfred			    numerichost);
9391558Srgrimes		return;
9401558Srgrimes	default:
9411558Srgrimes		svcerr_noproc(transp);
9421558Srgrimes		return;
9431558Srgrimes	}
9441558Srgrimes}
9451558Srgrimes
9461558Srgrimes/*
9471558Srgrimes * Xdr conversion for a dirpath string
9481558Srgrimes */
9491558Srgrimesint
950216587Scharnierxdr_dir(XDR *xdrsp, char *dirp)
9511558Srgrimes{
952194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
9531558Srgrimes}
9541558Srgrimes
9551558Srgrimes/*
9569336Sdfr * Xdr routine to generate file handle reply
9571558Srgrimes */
9581558Srgrimesint
959216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp)
9601558Srgrimes{
96192806Sobrien	struct fhreturn *fhrp = (struct fhreturn *)cp;
9629336Sdfr	u_long ok = 0, len, auth;
963184588Sdfr	int i;
9641558Srgrimes
9651558Srgrimes	if (!xdr_long(xdrsp, &ok))
9661558Srgrimes		return (0);
9679336Sdfr	switch (fhrp->fhr_vers) {
9689336Sdfr	case 1:
9699336Sdfr		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
9709336Sdfr	case 3:
9719336Sdfr		len = NFSX_V3FH;
9729336Sdfr		if (!xdr_long(xdrsp, &len))
9739336Sdfr			return (0);
9749336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
9759336Sdfr			return (0);
976184588Sdfr		if (fhrp->fhr_numsecflavors) {
977184588Sdfr			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
978184588Sdfr				return (0);
979184588Sdfr			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
980184588Sdfr				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
981184588Sdfr					return (0);
982184588Sdfr			return (1);
983184588Sdfr		} else {
984184588Sdfr			auth = AUTH_SYS;
985184588Sdfr			len = 1;
986184588Sdfr			if (!xdr_long(xdrsp, &len))
987184588Sdfr				return (0);
988184588Sdfr			return (xdr_long(xdrsp, &auth));
989184588Sdfr		}
9909336Sdfr	};
9919336Sdfr	return (0);
9921558Srgrimes}
9931558Srgrimes
9941558Srgrimesint
995216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused)
9961558Srgrimes{
9971558Srgrimes	struct mountlist *mlp;
9981558Srgrimes	int true = 1;
9991558Srgrimes	int false = 0;
10001558Srgrimes	char *strp;
10011558Srgrimes
10021558Srgrimes	mlp = mlhead;
10031558Srgrimes	while (mlp) {
10041558Srgrimes		if (!xdr_bool(xdrsp, &true))
10051558Srgrimes			return (0);
10061558Srgrimes		strp = &mlp->ml_host[0];
1007194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
10081558Srgrimes			return (0);
10091558Srgrimes		strp = &mlp->ml_dirp[0];
1010194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
10111558Srgrimes			return (0);
10121558Srgrimes		mlp = mlp->ml_next;
10131558Srgrimes	}
10141558Srgrimes	if (!xdr_bool(xdrsp, &false))
10151558Srgrimes		return (0);
10161558Srgrimes	return (1);
10171558Srgrimes}
10181558Srgrimes
10191558Srgrimes/*
10201558Srgrimes * Xdr conversion for export list
10211558Srgrimes */
10221558Srgrimesint
1023216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
10241558Srgrimes{
10251558Srgrimes	struct exportlist *ep;
10261558Srgrimes	int false = 0;
10279336Sdfr	int putdef;
10289336Sdfr	sigset_t sighup_mask;
10291558Srgrimes
10309336Sdfr	sigemptyset(&sighup_mask);
10319336Sdfr	sigaddset(&sighup_mask, SIGHUP);
10329336Sdfr	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
10331558Srgrimes	ep = exphead;
10341558Srgrimes	while (ep) {
10351558Srgrimes		putdef = 0;
1036100117Salfred		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1037100117Salfred			       &putdef, brief))
10381558Srgrimes			goto errout;
10391558Srgrimes		if (ep->ex_defdir && putdef == 0 &&
10401558Srgrimes			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1041100117Salfred			&putdef, brief))
10421558Srgrimes			goto errout;
10431558Srgrimes		ep = ep->ex_next;
10441558Srgrimes	}
10459336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
10461558Srgrimes	if (!xdr_bool(xdrsp, &false))
10471558Srgrimes		return (0);
10481558Srgrimes	return (1);
10491558Srgrimeserrout:
10509336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
10511558Srgrimes	return (0);
10521558Srgrimes}
10531558Srgrimes
10541558Srgrimes/*
10551558Srgrimes * Called from xdr_explist() to traverse the tree and export the
10561558Srgrimes * directory paths.
10571558Srgrimes */
10581558Srgrimesint
1059216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1060216587Scharnier	int brief)
10611558Srgrimes{
10621558Srgrimes	struct grouplist *grp;
10631558Srgrimes	struct hostlist *hp;
10641558Srgrimes	int true = 1;
10651558Srgrimes	int false = 0;
10661558Srgrimes	int gotalldir = 0;
10671558Srgrimes	char *strp;
10681558Srgrimes
10691558Srgrimes	if (dp) {
1070100117Salfred		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
10711558Srgrimes			return (1);
10721558Srgrimes		if (!xdr_bool(xdrsp, &true))
10731558Srgrimes			return (1);
10741558Srgrimes		strp = dp->dp_dirp;
1075194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
10761558Srgrimes			return (1);
10771558Srgrimes		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
10781558Srgrimes			gotalldir = 1;
10791558Srgrimes			*putdefp = 1;
10801558Srgrimes		}
1081100117Salfred		if (brief) {
1082100117Salfred			if (!xdr_bool(xdrsp, &true))
1083100117Salfred				return (1);
1084100117Salfred			strp = "(...)";
1085194880Sdfr			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1086100117Salfred				return (1);
1087100117Salfred		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
10881558Srgrimes		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
10891558Srgrimes			hp = dp->dp_hosts;
10901558Srgrimes			while (hp) {
10911558Srgrimes				grp = hp->ht_grp;
10921558Srgrimes				if (grp->gr_type == GT_HOST) {
10931558Srgrimes					if (!xdr_bool(xdrsp, &true))
10941558Srgrimes						return (1);
109574462Salfred					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
10968871Srgrimes					if (!xdr_string(xdrsp, &strp,
1097194880Sdfr					    MNTNAMLEN))
10981558Srgrimes						return (1);
10991558Srgrimes				} else if (grp->gr_type == GT_NET) {
11001558Srgrimes					if (!xdr_bool(xdrsp, &true))
11011558Srgrimes						return (1);
11021558Srgrimes					strp = grp->gr_ptr.gt_net.nt_name;
11038871Srgrimes					if (!xdr_string(xdrsp, &strp,
1104194880Sdfr					    MNTNAMLEN))
11051558Srgrimes						return (1);
11061558Srgrimes				}
11071558Srgrimes				hp = hp->ht_next;
11081558Srgrimes				if (gotalldir && hp == (struct hostlist *)NULL) {
11091558Srgrimes					hp = adp->dp_hosts;
11101558Srgrimes					gotalldir = 0;
11111558Srgrimes				}
11121558Srgrimes			}
11131558Srgrimes		}
11141558Srgrimes		if (!xdr_bool(xdrsp, &false))
11151558Srgrimes			return (1);
1116100117Salfred		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
11171558Srgrimes			return (1);
11181558Srgrimes	}
11191558Srgrimes	return (0);
11201558Srgrimes}
11211558Srgrimes
1122100117Salfredint
1123216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp)
1124100117Salfred{
1125100117Salfred
1126100117Salfred	return xdr_explist_common(xdrsp, cp, 0);
1127100117Salfred}
1128100117Salfred
1129100117Salfredint
1130216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp)
1131100117Salfred{
1132100117Salfred
1133100117Salfred	return xdr_explist_common(xdrsp, cp, 1);
1134100117Salfred}
1135100117Salfred
113696622Siedowsechar *line;
113796622Siedowseint linesize;
11381558SrgrimesFILE *exp_file;
11391558Srgrimes
11401558Srgrimes/*
1141166440Spjd * Get the export list from one, currently open file
11421558Srgrimes */
1143166440Spjdstatic void
1144216587Scharnierget_exportlist_one(void)
11451558Srgrimes{
11461558Srgrimes	struct exportlist *ep, *ep2;
11471558Srgrimes	struct grouplist *grp, *tgrp;
11481558Srgrimes	struct exportlist **epp;
11491558Srgrimes	struct dirlist *dirhead;
1150166440Spjd	struct statfs fsb;
115172650Sgreen	struct xucred anon;
11521558Srgrimes	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1153166440Spjd	int len, has_host, exflags, got_nondir, dirplen, netgrp;
11541558Srgrimes
1155192934Srmacklem	v4root_phase = 0;
11561558Srgrimes	dirhead = (struct dirlist *)NULL;
11571558Srgrimes	while (get_line()) {
11581558Srgrimes		if (debug)
115937663Scharnier			warnx("got line %s", line);
11601558Srgrimes		cp = line;
11611558Srgrimes		nextfield(&cp, &endcp);
11621558Srgrimes		if (*cp == '#')
11631558Srgrimes			goto nextline;
11641558Srgrimes
11651558Srgrimes		/*
11661558Srgrimes		 * Set defaults.
11671558Srgrimes		 */
11681558Srgrimes		has_host = FALSE;
11691558Srgrimes		anon = def_anon;
11701558Srgrimes		exflags = MNT_EXPORTED;
11711558Srgrimes		got_nondir = 0;
11721558Srgrimes		opt_flags = 0;
11731558Srgrimes		ep = (struct exportlist *)NULL;
1174192934Srmacklem		dirp = NULL;
11751558Srgrimes
11761558Srgrimes		/*
1177192934Srmacklem		 * Handle the V4 root dir.
1178192934Srmacklem		 */
1179192934Srmacklem		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1180192934Srmacklem			/*
1181192934Srmacklem			 * V4: just indicates that it is the v4 root point,
1182192934Srmacklem			 * so skip over that and set v4root_phase.
1183192934Srmacklem			 */
1184192934Srmacklem			if (v4root_phase > 0) {
1185192934Srmacklem				syslog(LOG_ERR, "V4:duplicate line, ignored");
1186192934Srmacklem				goto nextline;
1187192934Srmacklem			}
1188192934Srmacklem			v4root_phase = 1;
1189192934Srmacklem			cp += 3;
1190192934Srmacklem			nextfield(&cp, &endcp);
1191192934Srmacklem		}
1192192934Srmacklem
1193192934Srmacklem		/*
11941558Srgrimes		 * Create new exports list entry
11951558Srgrimes		 */
11961558Srgrimes		len = endcp-cp;
11971558Srgrimes		tgrp = grp = get_grp();
11981558Srgrimes		while (len > 0) {
1199194880Sdfr			if (len > MNTNAMLEN) {
12001558Srgrimes			    getexp_err(ep, tgrp);
12011558Srgrimes			    goto nextline;
12021558Srgrimes			}
12031558Srgrimes			if (*cp == '-') {
12041558Srgrimes			    if (ep == (struct exportlist *)NULL) {
12051558Srgrimes				getexp_err(ep, tgrp);
12061558Srgrimes				goto nextline;
12071558Srgrimes			    }
12081558Srgrimes			    if (debug)
120937663Scharnier				warnx("doing opt %s", cp);
12101558Srgrimes			    got_nondir = 1;
12111558Srgrimes			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
12121558Srgrimes				&exflags, &anon)) {
12131558Srgrimes				getexp_err(ep, tgrp);
12141558Srgrimes				goto nextline;
12151558Srgrimes			    }
12161558Srgrimes			} else if (*cp == '/') {
12171558Srgrimes			    savedc = *endcp;
12181558Srgrimes			    *endcp = '\0';
1219192934Srmacklem			    if (v4root_phase > 1) {
1220192934Srmacklem				    if (dirp != NULL) {
1221192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
1222192934Srmacklem					getexp_err(ep, tgrp);
1223192934Srmacklem					goto nextline;
1224192934Srmacklem				    }
1225192934Srmacklem			    }
12261558Srgrimes			    if (check_dirpath(cp) &&
12271558Srgrimes				statfs(cp, &fsb) >= 0) {
12281558Srgrimes				if (got_nondir) {
122937663Scharnier				    syslog(LOG_ERR, "dirs must be first");
12301558Srgrimes				    getexp_err(ep, tgrp);
12311558Srgrimes				    goto nextline;
12321558Srgrimes				}
1233192934Srmacklem				if (v4root_phase == 1) {
1234192934Srmacklem				    if (dirp != NULL) {
1235192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
12361558Srgrimes					getexp_err(ep, tgrp);
12371558Srgrimes					goto nextline;
12381558Srgrimes				    }
1239192934Srmacklem				    if (strlen(v4root_dirpath) == 0) {
1240192934Srmacklem					strlcpy(v4root_dirpath, cp,
1241192934Srmacklem					    sizeof (v4root_dirpath));
1242192934Srmacklem				    } else if (strcmp(v4root_dirpath, cp)
1243192934Srmacklem					!= 0) {
1244192934Srmacklem					syslog(LOG_ERR,
1245192934Srmacklem					    "different V4 dirpath %s", cp);
1246192934Srmacklem					getexp_err(ep, tgrp);
1247192934Srmacklem					goto nextline;
1248192934Srmacklem				    }
1249192934Srmacklem				    dirp = cp;
1250192934Srmacklem				    v4root_phase = 2;
1251192934Srmacklem				    got_nondir = 1;
1252192934Srmacklem				    ep = get_exp();
12531558Srgrimes				} else {
1254192934Srmacklem				    if (ep) {
1255192934Srmacklem					if (ep->ex_fs.val[0] !=
1256192934Srmacklem					    fsb.f_fsid.val[0] ||
1257192934Srmacklem					    ep->ex_fs.val[1] !=
1258192934Srmacklem					    fsb.f_fsid.val[1]) {
1259192934Srmacklem						getexp_err(ep, tgrp);
1260192934Srmacklem						goto nextline;
1261192934Srmacklem					}
1262192934Srmacklem				    } else {
1263192934Srmacklem					/*
1264192934Srmacklem					 * See if this directory is already
1265192934Srmacklem					 * in the list.
1266192934Srmacklem					 */
1267192934Srmacklem					ep = ex_search(&fsb.f_fsid);
1268192934Srmacklem					if (ep == (struct exportlist *)NULL) {
1269192934Srmacklem					    ep = get_exp();
1270192934Srmacklem					    ep->ex_fs = fsb.f_fsid;
1271192934Srmacklem					    ep->ex_fsdir = (char *)malloc
1272192934Srmacklem					        (strlen(fsb.f_mntonname) + 1);
1273192934Srmacklem					    if (ep->ex_fsdir)
1274192934Srmacklem						strcpy(ep->ex_fsdir,
1275192934Srmacklem						    fsb.f_mntonname);
1276192934Srmacklem					    else
1277192934Srmacklem						out_of_mem();
1278192934Srmacklem					    if (debug)
1279192934Srmacklem						warnx(
1280192934Srmacklem						  "making new ep fs=0x%x,0x%x",
1281192934Srmacklem						  fsb.f_fsid.val[0],
1282192934Srmacklem						  fsb.f_fsid.val[1]);
1283192934Srmacklem					} else if (debug)
1284192934Srmacklem					    warnx("found ep fs=0x%x,0x%x",
1285192934Srmacklem						fsb.f_fsid.val[0],
1286192934Srmacklem						fsb.f_fsid.val[1]);
1287192934Srmacklem				    }
1288192934Srmacklem
12891558Srgrimes				    /*
1290192934Srmacklem				     * Add dirpath to export mount point.
12911558Srgrimes				     */
1292192934Srmacklem				    dirp = add_expdir(&dirhead, cp, len);
1293192934Srmacklem				    dirplen = len;
12941558Srgrimes				}
12951558Srgrimes			    } else {
12961558Srgrimes				getexp_err(ep, tgrp);
12971558Srgrimes				goto nextline;
12981558Srgrimes			    }
12991558Srgrimes			    *endcp = savedc;
13001558Srgrimes			} else {
13011558Srgrimes			    savedc = *endcp;
13021558Srgrimes			    *endcp = '\0';
13031558Srgrimes			    got_nondir = 1;
13041558Srgrimes			    if (ep == (struct exportlist *)NULL) {
13051558Srgrimes				getexp_err(ep, tgrp);
13061558Srgrimes				goto nextline;
13071558Srgrimes			    }
13081558Srgrimes
13091558Srgrimes			    /*
13101558Srgrimes			     * Get the host or netgroup.
13111558Srgrimes			     */
13121558Srgrimes			    setnetgrent(cp);
13131558Srgrimes			    netgrp = getnetgrent(&hst, &usr, &dom);
13141558Srgrimes			    do {
13151558Srgrimes				if (has_host) {
13161558Srgrimes				    grp->gr_next = get_grp();
13171558Srgrimes				    grp = grp->gr_next;
13181558Srgrimes				}
13191558Srgrimes				if (netgrp) {
132037003Sjoerg				    if (hst == 0) {
132137663Scharnier					syslog(LOG_ERR,
132237663Scharnier				"null hostname in netgroup %s, skipping", cp);
132337004Sjoerg					grp->gr_type = GT_IGNORE;
132437003Sjoerg				    } else if (get_host(hst, grp, tgrp)) {
132537663Scharnier					syslog(LOG_ERR,
132637663Scharnier			"bad host %s in netgroup %s, skipping", hst, cp);
132729317Sjlemon					grp->gr_type = GT_IGNORE;
13281558Srgrimes				    }
13297401Swpaul				} else if (get_host(cp, grp, tgrp)) {
133037663Scharnier				    syslog(LOG_ERR, "bad host %s, skipping", cp);
133129317Sjlemon				    grp->gr_type = GT_IGNORE;
13321558Srgrimes				}
13331558Srgrimes				has_host = TRUE;
13341558Srgrimes			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
13351558Srgrimes			    endnetgrent();
13361558Srgrimes			    *endcp = savedc;
13371558Srgrimes			}
13381558Srgrimes			cp = endcp;
13391558Srgrimes			nextfield(&cp, &endcp);
13401558Srgrimes			len = endcp - cp;
13411558Srgrimes		}
13421558Srgrimes		if (check_options(dirhead)) {
13431558Srgrimes			getexp_err(ep, tgrp);
13441558Srgrimes			goto nextline;
13451558Srgrimes		}
13461558Srgrimes		if (!has_host) {
134775641Siedowse			grp->gr_type = GT_DEFAULT;
13481558Srgrimes			if (debug)
134937663Scharnier				warnx("adding a default entry");
13501558Srgrimes
13511558Srgrimes		/*
13521558Srgrimes		 * Don't allow a network export coincide with a list of
13531558Srgrimes		 * host(s) on the same line.
13541558Srgrimes		 */
13551558Srgrimes		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
135675801Siedowse			syslog(LOG_ERR, "network/host conflict");
13571558Srgrimes			getexp_err(ep, tgrp);
13581558Srgrimes			goto nextline;
135929317Sjlemon
136074462Salfred		/*
136174462Salfred		 * If an export list was specified on this line, make sure
136229317Sjlemon		 * that we have at least one valid entry, otherwise skip it.
136329317Sjlemon		 */
136429317Sjlemon		} else {
136529317Sjlemon			grp = tgrp;
136674462Salfred			while (grp && grp->gr_type == GT_IGNORE)
136729317Sjlemon				grp = grp->gr_next;
136829317Sjlemon			if (! grp) {
136929317Sjlemon			    getexp_err(ep, tgrp);
137029317Sjlemon			    goto nextline;
137129317Sjlemon			}
13721558Srgrimes		}
13731558Srgrimes
1374192934Srmacklem		if (v4root_phase == 1) {
1375192934Srmacklem			syslog(LOG_ERR, "V4:root, no dirp, ignored");
1376192934Srmacklem			getexp_err(ep, tgrp);
1377192934Srmacklem			goto nextline;
1378192934Srmacklem		}
1379192934Srmacklem
13801558Srgrimes		/*
13811558Srgrimes		 * Loop through hosts, pushing the exports into the kernel.
13821558Srgrimes		 * After loop, tgrp points to the start of the list and
13831558Srgrimes		 * grp points to the last entry in the list.
13841558Srgrimes		 */
13851558Srgrimes		grp = tgrp;
13861558Srgrimes		do {
138775635Siedowse			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
138875635Siedowse			    &fsb)) {
138975635Siedowse				getexp_err(ep, tgrp);
139075635Siedowse				goto nextline;
139175635Siedowse			}
13921558Srgrimes		} while (grp->gr_next && (grp = grp->gr_next));
13931558Srgrimes
13941558Srgrimes		/*
1395192934Srmacklem		 * For V4: don't enter in mount lists.
1396192934Srmacklem		 */
1397194773Srmacklem		if (v4root_phase > 0 && v4root_phase <= 2) {
1398194773Srmacklem			/*
1399194773Srmacklem			 * Since these structures aren't used by mountd,
1400194773Srmacklem			 * free them up now.
1401194773Srmacklem			 */
1402194773Srmacklem			if (ep != NULL)
1403194773Srmacklem				free_exp(ep);
1404194773Srmacklem			while (tgrp != NULL) {
1405194773Srmacklem				grp = tgrp;
1406194773Srmacklem				tgrp = tgrp->gr_next;
1407194773Srmacklem				free_grp(grp);
1408194773Srmacklem			}
1409192934Srmacklem			goto nextline;
1410194773Srmacklem		}
1411192934Srmacklem
1412192934Srmacklem		/*
14131558Srgrimes		 * Success. Update the data structures.
14141558Srgrimes		 */
14151558Srgrimes		if (has_host) {
14169336Sdfr			hang_dirp(dirhead, tgrp, ep, opt_flags);
14171558Srgrimes			grp->gr_next = grphead;
14181558Srgrimes			grphead = tgrp;
14191558Srgrimes		} else {
14201558Srgrimes			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
14219336Sdfr				opt_flags);
14221558Srgrimes			free_grp(grp);
14231558Srgrimes		}
14241558Srgrimes		dirhead = (struct dirlist *)NULL;
14251558Srgrimes		if ((ep->ex_flag & EX_LINKED) == 0) {
14261558Srgrimes			ep2 = exphead;
14271558Srgrimes			epp = &exphead;
14281558Srgrimes
14291558Srgrimes			/*
14301558Srgrimes			 * Insert in the list in alphabetical order.
14311558Srgrimes			 */
14321558Srgrimes			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
14331558Srgrimes				epp = &ep2->ex_next;
14341558Srgrimes				ep2 = ep2->ex_next;
14351558Srgrimes			}
14361558Srgrimes			if (ep2)
14371558Srgrimes				ep->ex_next = ep2;
14381558Srgrimes			*epp = ep;
14391558Srgrimes			ep->ex_flag |= EX_LINKED;
14401558Srgrimes		}
14411558Srgrimesnextline:
1442192934Srmacklem		v4root_phase = 0;
14431558Srgrimes		if (dirhead) {
14441558Srgrimes			free_dir(dirhead);
14451558Srgrimes			dirhead = (struct dirlist *)NULL;
14461558Srgrimes		}
14471558Srgrimes	}
14481558Srgrimes}
14491558Srgrimes
14501558Srgrimes/*
1451166440Spjd * Get the export list from all specified files
1452166440Spjd */
1453166440Spjdvoid
1454216587Scharnierget_exportlist(void)
1455166440Spjd{
1456166440Spjd	struct exportlist *ep, *ep2;
1457166440Spjd	struct grouplist *grp, *tgrp;
1458166440Spjd	struct export_args export;
1459166440Spjd	struct iovec *iov;
1460166440Spjd	struct statfs *fsp, *mntbufp;
1461166440Spjd	struct xvfsconf vfc;
1462166440Spjd	char *dirp;
1463166440Spjd	char errmsg[255];
1464166440Spjd	int dirplen, num, i;
1465166440Spjd	int iovlen;
1466168684Spjd	int done;
1467192934Srmacklem	struct nfsex_args eargs;
1468166440Spjd
1469192934Srmacklem	v4root_dirpath[0] = '\0';
1470166440Spjd	bzero(&export, sizeof(export));
1471166440Spjd	export.ex_flags = MNT_DELEXPORT;
1472166440Spjd	dirp = NULL;
1473166440Spjd	dirplen = 0;
1474166440Spjd	iov = NULL;
1475166440Spjd	iovlen = 0;
1476166440Spjd	bzero(errmsg, sizeof(errmsg));
1477166440Spjd
1478166440Spjd	/*
1479166440Spjd	 * First, get rid of the old list
1480166440Spjd	 */
1481166440Spjd	ep = exphead;
1482166440Spjd	while (ep) {
1483166440Spjd		ep2 = ep;
1484166440Spjd		ep = ep->ex_next;
1485166440Spjd		free_exp(ep2);
1486166440Spjd	}
1487166440Spjd	exphead = (struct exportlist *)NULL;
1488166440Spjd
1489166440Spjd	grp = grphead;
1490166440Spjd	while (grp) {
1491166440Spjd		tgrp = grp;
1492166440Spjd		grp = grp->gr_next;
1493166440Spjd		free_grp(tgrp);
1494166440Spjd	}
1495166440Spjd	grphead = (struct grouplist *)NULL;
1496166440Spjd
1497166440Spjd	/*
1498192934Srmacklem	 * and the old V4 root dir.
1499192934Srmacklem	 */
1500192934Srmacklem	bzero(&eargs, sizeof (eargs));
1501192934Srmacklem	eargs.export.ex_flags = MNT_DELEXPORT;
1502192934Srmacklem	if (run_v4server > 0 &&
1503192934Srmacklem	    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1504192934Srmacklem	    errno != ENOENT)
1505192934Srmacklem		syslog(LOG_ERR, "Can't delete exports for V4:");
1506192934Srmacklem
1507192934Srmacklem	/*
1508192934Srmacklem	 * and clear flag that notes if a public fh has been exported.
1509192934Srmacklem	 */
1510192934Srmacklem	has_publicfh = 0;
1511192934Srmacklem
1512192934Srmacklem	/*
1513166440Spjd	 * And delete exports that are in the kernel for all local
1514166440Spjd	 * filesystems.
1515166440Spjd	 * XXX: Should know how to handle all local exportable filesystems.
1516166440Spjd	 */
1517166440Spjd	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1518166440Spjd
1519166440Spjd	if (num > 0) {
1520166440Spjd		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1521166440Spjd		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1522166440Spjd		build_iovec(&iov, &iovlen, "from", NULL, 0);
1523166440Spjd		build_iovec(&iov, &iovlen, "update", NULL, 0);
1524166440Spjd		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1525166440Spjd		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1526166440Spjd	}
1527166440Spjd
1528166440Spjd	for (i = 0; i < num; i++) {
1529166440Spjd		fsp = &mntbufp[i];
1530166440Spjd		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1531166440Spjd			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1532166440Spjd			    fsp->f_fstypename);
1533166440Spjd			continue;
1534166440Spjd		}
1535166440Spjd
1536166440Spjd		/*
1537166440Spjd		 * Do not delete export for network filesystem by
1538166440Spjd		 * passing "export" arg to nmount().
1539166440Spjd		 * It only makes sense to do this for local filesystems.
1540166440Spjd		 */
1541166440Spjd		if (vfc.vfc_flags & VFCF_NETWORK)
1542166440Spjd			continue;
1543166440Spjd
1544166440Spjd		iov[1].iov_base = fsp->f_fstypename;
1545166440Spjd		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1546166440Spjd		iov[3].iov_base = fsp->f_mntonname;
1547166440Spjd		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1548166440Spjd		iov[5].iov_base = fsp->f_mntfromname;
1549166440Spjd		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1550166440Spjd
1551166440Spjd		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1552166440Spjd		    errno != ENOENT && errno != ENOTSUP) {
1553166440Spjd			syslog(LOG_ERR,
1554166440Spjd			    "can't delete exports for %s: %m %s",
1555166440Spjd			    fsp->f_mntonname, errmsg);
1556166440Spjd		}
1557166440Spjd	}
1558166440Spjd
1559166440Spjd	if (iov != NULL) {
1560166440Spjd		/* Free strings allocated by strdup() in getmntopts.c */
1561166440Spjd		free(iov[0].iov_base); /* fstype */
1562166440Spjd		free(iov[2].iov_base); /* fspath */
1563166440Spjd		free(iov[4].iov_base); /* from */
1564166440Spjd		free(iov[6].iov_base); /* update */
1565166440Spjd		free(iov[8].iov_base); /* export */
1566166440Spjd		free(iov[10].iov_base); /* errmsg */
1567166440Spjd
1568166440Spjd		/* free iov, allocated by realloc() */
1569166440Spjd		free(iov);
1570166440Spjd		iovlen = 0;
1571166440Spjd	}
1572166440Spjd
1573166440Spjd	/*
1574166440Spjd	 * Read in the exports file and build the list, calling
1575166440Spjd	 * nmount() as we go along to push the export rules into the kernel.
1576166440Spjd	 */
1577168684Spjd	done = 0;
1578166440Spjd	for (i = 0; exnames[i] != NULL; i++) {
1579166440Spjd		if (debug)
1580166440Spjd			warnx("reading exports from %s", exnames[i]);
1581166440Spjd		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1582168684Spjd			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1583168684Spjd			continue;
1584166440Spjd		}
1585166440Spjd		get_exportlist_one();
1586166440Spjd		fclose(exp_file);
1587168684Spjd		done++;
1588166440Spjd	}
1589168684Spjd	if (done == 0) {
1590168684Spjd		syslog(LOG_ERR, "can't open any exports file");
1591168684Spjd		exit(2);
1592168684Spjd	}
1593192934Srmacklem
1594192934Srmacklem	/*
1595192934Srmacklem	 * If there was no public fh, clear any previous one set.
1596192934Srmacklem	 */
1597192934Srmacklem	if (run_v4server > 0 && has_publicfh == 0)
1598192934Srmacklem		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1599166440Spjd}
1600166440Spjd
1601166440Spjd/*
16021558Srgrimes * Allocate an export list element
16031558Srgrimes */
16041558Srgrimesstruct exportlist *
1605216587Scharnierget_exp(void)
16061558Srgrimes{
16071558Srgrimes	struct exportlist *ep;
16081558Srgrimes
16091558Srgrimes	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
16101558Srgrimes	if (ep == (struct exportlist *)NULL)
16111558Srgrimes		out_of_mem();
161223681Speter	memset(ep, 0, sizeof(struct exportlist));
16131558Srgrimes	return (ep);
16141558Srgrimes}
16151558Srgrimes
16161558Srgrimes/*
16171558Srgrimes * Allocate a group list element
16181558Srgrimes */
16191558Srgrimesstruct grouplist *
1620216587Scharnierget_grp(void)
16211558Srgrimes{
16221558Srgrimes	struct grouplist *gp;
16231558Srgrimes
16241558Srgrimes	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
16251558Srgrimes	if (gp == (struct grouplist *)NULL)
16261558Srgrimes		out_of_mem();
162723681Speter	memset(gp, 0, sizeof(struct grouplist));
16281558Srgrimes	return (gp);
16291558Srgrimes}
16301558Srgrimes
16311558Srgrimes/*
16321558Srgrimes * Clean up upon an error in get_exportlist().
16331558Srgrimes */
16341558Srgrimesvoid
1635216587Scharniergetexp_err(struct exportlist *ep, struct grouplist *grp)
16361558Srgrimes{
16371558Srgrimes	struct grouplist *tgrp;
16381558Srgrimes
1639100336Sjoerg	if (!(opt_flags & OP_QUIET))
1640100336Sjoerg		syslog(LOG_ERR, "bad exports list line %s", line);
16411558Srgrimes	if (ep && (ep->ex_flag & EX_LINKED) == 0)
16421558Srgrimes		free_exp(ep);
16431558Srgrimes	while (grp) {
16441558Srgrimes		tgrp = grp;
16451558Srgrimes		grp = grp->gr_next;
16461558Srgrimes		free_grp(tgrp);
16471558Srgrimes	}
16481558Srgrimes}
16491558Srgrimes
16501558Srgrimes/*
16511558Srgrimes * Search the export list for a matching fs.
16521558Srgrimes */
16531558Srgrimesstruct exportlist *
1654216587Scharnierex_search(fsid_t *fsid)
16551558Srgrimes{
16561558Srgrimes	struct exportlist *ep;
16571558Srgrimes
16581558Srgrimes	ep = exphead;
16591558Srgrimes	while (ep) {
16601558Srgrimes		if (ep->ex_fs.val[0] == fsid->val[0] &&
16611558Srgrimes		    ep->ex_fs.val[1] == fsid->val[1])
16621558Srgrimes			return (ep);
16631558Srgrimes		ep = ep->ex_next;
16641558Srgrimes	}
16651558Srgrimes	return (ep);
16661558Srgrimes}
16671558Srgrimes
16681558Srgrimes/*
16691558Srgrimes * Add a directory path to the list.
16701558Srgrimes */
16711558Srgrimeschar *
1672216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len)
16731558Srgrimes{
16741558Srgrimes	struct dirlist *dp;
16751558Srgrimes
16761558Srgrimes	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
167737663Scharnier	if (dp == (struct dirlist *)NULL)
167837663Scharnier		out_of_mem();
16791558Srgrimes	dp->dp_left = *dpp;
16801558Srgrimes	dp->dp_right = (struct dirlist *)NULL;
16811558Srgrimes	dp->dp_flag = 0;
16821558Srgrimes	dp->dp_hosts = (struct hostlist *)NULL;
16831558Srgrimes	strcpy(dp->dp_dirp, cp);
16841558Srgrimes	*dpp = dp;
16851558Srgrimes	return (dp->dp_dirp);
16861558Srgrimes}
16871558Srgrimes
16881558Srgrimes/*
16891558Srgrimes * Hang the dir list element off the dirpath binary tree as required
16901558Srgrimes * and update the entry for host.
16911558Srgrimes */
16921558Srgrimesvoid
1693216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1694216587Scharnier	int flags)
16951558Srgrimes{
16961558Srgrimes	struct hostlist *hp;
16971558Srgrimes	struct dirlist *dp2;
16981558Srgrimes
16999336Sdfr	if (flags & OP_ALLDIRS) {
17001558Srgrimes		if (ep->ex_defdir)
17011558Srgrimes			free((caddr_t)dp);
17021558Srgrimes		else
17031558Srgrimes			ep->ex_defdir = dp;
17049336Sdfr		if (grp == (struct grouplist *)NULL) {
17051558Srgrimes			ep->ex_defdir->dp_flag |= DP_DEFSET;
17069336Sdfr		} else while (grp) {
17071558Srgrimes			hp = get_ht();
17081558Srgrimes			hp->ht_grp = grp;
17091558Srgrimes			hp->ht_next = ep->ex_defdir->dp_hosts;
17101558Srgrimes			ep->ex_defdir->dp_hosts = hp;
17111558Srgrimes			grp = grp->gr_next;
17121558Srgrimes		}
17131558Srgrimes	} else {
17141558Srgrimes
17151558Srgrimes		/*
171637663Scharnier		 * Loop through the directories adding them to the tree.
17171558Srgrimes		 */
17181558Srgrimes		while (dp) {
17191558Srgrimes			dp2 = dp->dp_left;
17209336Sdfr			add_dlist(&ep->ex_dirl, dp, grp, flags);
17211558Srgrimes			dp = dp2;
17221558Srgrimes		}
17231558Srgrimes	}
17241558Srgrimes}
17251558Srgrimes
17261558Srgrimes/*
17271558Srgrimes * Traverse the binary tree either updating a node that is already there
17281558Srgrimes * for the new directory or adding the new node.
17291558Srgrimes */
17301558Srgrimesvoid
1731216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
1732216587Scharnier	int flags)
17331558Srgrimes{
17341558Srgrimes	struct dirlist *dp;
17351558Srgrimes	struct hostlist *hp;
17361558Srgrimes	int cmp;
17371558Srgrimes
17381558Srgrimes	dp = *dpp;
17391558Srgrimes	if (dp) {
17401558Srgrimes		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
17411558Srgrimes		if (cmp > 0) {
17429336Sdfr			add_dlist(&dp->dp_left, newdp, grp, flags);
17431558Srgrimes			return;
17441558Srgrimes		} else if (cmp < 0) {
17459336Sdfr			add_dlist(&dp->dp_right, newdp, grp, flags);
17461558Srgrimes			return;
17471558Srgrimes		} else
17481558Srgrimes			free((caddr_t)newdp);
17491558Srgrimes	} else {
17501558Srgrimes		dp = newdp;
17511558Srgrimes		dp->dp_left = (struct dirlist *)NULL;
17521558Srgrimes		*dpp = dp;
17531558Srgrimes	}
17541558Srgrimes	if (grp) {
17551558Srgrimes
17561558Srgrimes		/*
17571558Srgrimes		 * Hang all of the host(s) off of the directory point.
17581558Srgrimes		 */
17591558Srgrimes		do {
17601558Srgrimes			hp = get_ht();
17611558Srgrimes			hp->ht_grp = grp;
17621558Srgrimes			hp->ht_next = dp->dp_hosts;
17631558Srgrimes			dp->dp_hosts = hp;
17641558Srgrimes			grp = grp->gr_next;
17651558Srgrimes		} while (grp);
17669336Sdfr	} else {
17671558Srgrimes		dp->dp_flag |= DP_DEFSET;
17689336Sdfr	}
17691558Srgrimes}
17701558Srgrimes
17711558Srgrimes/*
17721558Srgrimes * Search for a dirpath on the export point.
17731558Srgrimes */
17741558Srgrimesstruct dirlist *
1775216587Scharnierdirp_search(struct dirlist *dp, char *dirp)
17761558Srgrimes{
17771558Srgrimes	int cmp;
17781558Srgrimes
17791558Srgrimes	if (dp) {
178074462Salfred		cmp = strcmp(dp->dp_dirp, dirp);
17811558Srgrimes		if (cmp > 0)
178274462Salfred			return (dirp_search(dp->dp_left, dirp));
17831558Srgrimes		else if (cmp < 0)
178474462Salfred			return (dirp_search(dp->dp_right, dirp));
17851558Srgrimes		else
17861558Srgrimes			return (dp);
17871558Srgrimes	}
17881558Srgrimes	return (dp);
17891558Srgrimes}
17901558Srgrimes
17911558Srgrimes/*
17921558Srgrimes * Scan for a host match in a directory tree.
17931558Srgrimes */
17941558Srgrimesint
1795216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
1796216587Scharnier	int *hostsetp)
17971558Srgrimes{
17981558Srgrimes	struct hostlist *hp;
17991558Srgrimes	struct grouplist *grp;
180074462Salfred	struct addrinfo *ai;
18011558Srgrimes
18021558Srgrimes	if (dp) {
18031558Srgrimes		if (dp->dp_flag & DP_DEFSET)
18049336Sdfr			*defsetp = dp->dp_flag;
18051558Srgrimes		hp = dp->dp_hosts;
18061558Srgrimes		while (hp) {
18071558Srgrimes			grp = hp->ht_grp;
18081558Srgrimes			switch (grp->gr_type) {
18091558Srgrimes			case GT_HOST:
181074462Salfred				ai = grp->gr_ptr.gt_addrinfo;
181174462Salfred				for (; ai; ai = ai->ai_next) {
181275801Siedowse					if (!sacmp(ai->ai_addr, saddr, NULL)) {
181374462Salfred						*hostsetp =
181474462Salfred						    (hp->ht_flag | DP_HOSTSET);
181574462Salfred						return (1);
181674462Salfred					}
18179336Sdfr				}
181875801Siedowse				break;
18191558Srgrimes			case GT_NET:
182075801Siedowse				if (!sacmp(saddr, (struct sockaddr *)
182175801Siedowse				    &grp->gr_ptr.gt_net.nt_net,
182275801Siedowse				    (struct sockaddr *)
182375801Siedowse				    &grp->gr_ptr.gt_net.nt_mask)) {
182474462Salfred					*hostsetp = (hp->ht_flag | DP_HOSTSET);
182574462Salfred					return (1);
182674462Salfred				}
182775801Siedowse				break;
182875801Siedowse			}
18291558Srgrimes			hp = hp->ht_next;
18301558Srgrimes		}
18311558Srgrimes	}
18321558Srgrimes	return (0);
18331558Srgrimes}
18341558Srgrimes
18351558Srgrimes/*
18361558Srgrimes * Scan tree for a host that matches the address.
18371558Srgrimes */
18381558Srgrimesint
1839216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr)
18401558Srgrimes{
18419336Sdfr	int defset, hostset;
18421558Srgrimes
18431558Srgrimes	if (dp) {
18441558Srgrimes		if (scan_tree(dp->dp_left, saddr))
18451558Srgrimes			return (1);
18469336Sdfr		if (chk_host(dp, saddr, &defset, &hostset))
18471558Srgrimes			return (1);
18481558Srgrimes		if (scan_tree(dp->dp_right, saddr))
18491558Srgrimes			return (1);
18501558Srgrimes	}
18511558Srgrimes	return (0);
18521558Srgrimes}
18531558Srgrimes
18541558Srgrimes/*
18551558Srgrimes * Traverse the dirlist tree and free it up.
18561558Srgrimes */
18571558Srgrimesvoid
1858216587Scharnierfree_dir(struct dirlist *dp)
18591558Srgrimes{
18601558Srgrimes
18611558Srgrimes	if (dp) {
18621558Srgrimes		free_dir(dp->dp_left);
18631558Srgrimes		free_dir(dp->dp_right);
18641558Srgrimes		free_host(dp->dp_hosts);
18651558Srgrimes		free((caddr_t)dp);
18661558Srgrimes	}
18671558Srgrimes}
18681558Srgrimes
18691558Srgrimes/*
1870184588Sdfr * Parse a colon separated list of security flavors
1871184588Sdfr */
1872184588Sdfrint
1873216587Scharnierparsesec(char *seclist, struct exportlist *ep)
1874184588Sdfr{
1875184588Sdfr	char *cp, savedc;
1876184588Sdfr	int flavor;
1877184588Sdfr
1878184588Sdfr	ep->ex_numsecflavors = 0;
1879184588Sdfr	for (;;) {
1880184588Sdfr		cp = strchr(seclist, ':');
1881184588Sdfr		if (cp) {
1882184588Sdfr			savedc = *cp;
1883184588Sdfr			*cp = '\0';
1884184588Sdfr		}
1885184588Sdfr
1886184588Sdfr		if (!strcmp(seclist, "sys"))
1887184588Sdfr			flavor = AUTH_SYS;
1888184588Sdfr		else if (!strcmp(seclist, "krb5"))
1889184588Sdfr			flavor = RPCSEC_GSS_KRB5;
1890184588Sdfr		else if (!strcmp(seclist, "krb5i"))
1891184588Sdfr			flavor = RPCSEC_GSS_KRB5I;
1892184588Sdfr		else if (!strcmp(seclist, "krb5p"))
1893184588Sdfr			flavor = RPCSEC_GSS_KRB5P;
1894184588Sdfr		else {
1895184588Sdfr			if (cp)
1896184588Sdfr				*cp = savedc;
1897184588Sdfr			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
1898184588Sdfr			return (1);
1899184588Sdfr		}
1900184588Sdfr		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
1901184588Sdfr			if (cp)
1902184588Sdfr				*cp = savedc;
1903184588Sdfr			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
1904184588Sdfr			return (1);
1905184588Sdfr		}
1906184588Sdfr		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
1907184588Sdfr		ep->ex_numsecflavors++;
1908184588Sdfr		if (cp) {
1909184588Sdfr			*cp = savedc;
1910184588Sdfr			seclist = cp + 1;
1911184588Sdfr		} else {
1912184588Sdfr			break;
1913184588Sdfr		}
1914184588Sdfr	}
1915184588Sdfr	return (0);
1916184588Sdfr}
1917184588Sdfr
1918184588Sdfr/*
19191558Srgrimes * Parse the option string and update fields.
19201558Srgrimes * Option arguments may either be -<option>=<value> or
19211558Srgrimes * -<option> <value>
19221558Srgrimes */
19231558Srgrimesint
1924216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
1925216587Scharnier	int *has_hostp, int *exflagsp, struct xucred *cr)
19261558Srgrimes{
19271558Srgrimes	char *cpoptarg, *cpoptend;
19281558Srgrimes	char *cp, *endcp, *cpopt, savedc, savedc2;
19291558Srgrimes	int allflag, usedarg;
19301558Srgrimes
193151968Salfred	savedc2 = '\0';
19321558Srgrimes	cpopt = *cpp;
19331558Srgrimes	cpopt++;
19341558Srgrimes	cp = *endcpp;
19351558Srgrimes	savedc = *cp;
19361558Srgrimes	*cp = '\0';
19371558Srgrimes	while (cpopt && *cpopt) {
19381558Srgrimes		allflag = 1;
19391558Srgrimes		usedarg = -2;
194037663Scharnier		if ((cpoptend = strchr(cpopt, ','))) {
19411558Srgrimes			*cpoptend++ = '\0';
194237663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
19431558Srgrimes				*cpoptarg++ = '\0';
19441558Srgrimes		} else {
194537663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
19461558Srgrimes				*cpoptarg++ = '\0';
19471558Srgrimes			else {
19481558Srgrimes				*cp = savedc;
19491558Srgrimes				nextfield(&cp, &endcp);
19501558Srgrimes				**endcpp = '\0';
19511558Srgrimes				if (endcp > cp && *cp != '-') {
19521558Srgrimes					cpoptarg = cp;
19531558Srgrimes					savedc2 = *endcp;
19541558Srgrimes					*endcp = '\0';
19551558Srgrimes					usedarg = 0;
19561558Srgrimes				}
19571558Srgrimes			}
19581558Srgrimes		}
19591558Srgrimes		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
19601558Srgrimes			*exflagsp |= MNT_EXRDONLY;
19611558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
19621558Srgrimes		    !(allflag = strcmp(cpopt, "mapall")) ||
19631558Srgrimes		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
19641558Srgrimes			usedarg++;
19651558Srgrimes			parsecred(cpoptarg, cr);
19661558Srgrimes			if (allflag == 0) {
19671558Srgrimes				*exflagsp |= MNT_EXPORTANON;
19681558Srgrimes				opt_flags |= OP_MAPALL;
19691558Srgrimes			} else
19701558Srgrimes				opt_flags |= OP_MAPROOT;
19711558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
197275801Siedowse		    !strcmp(cpopt, "m"))) {
19731558Srgrimes			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
197437663Scharnier				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
19751558Srgrimes				return (1);
19761558Srgrimes			}
19771558Srgrimes			usedarg++;
19781558Srgrimes			opt_flags |= OP_MASK;
19791558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
19801558Srgrimes			!strcmp(cpopt, "n"))) {
198174462Salfred			if (strchr(cpoptarg, '/') != NULL) {
198274462Salfred				if (debug)
198374462Salfred					fprintf(stderr, "setting OP_MASKLEN\n");
198474462Salfred				opt_flags |= OP_MASKLEN;
198574462Salfred			}
19861558Srgrimes			if (grp->gr_type != GT_NULL) {
198737663Scharnier				syslog(LOG_ERR, "network/host conflict");
19881558Srgrimes				return (1);
19891558Srgrimes			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
199037663Scharnier				syslog(LOG_ERR, "bad net: %s", cpoptarg);
19911558Srgrimes				return (1);
19921558Srgrimes			}
19931558Srgrimes			grp->gr_type = GT_NET;
19941558Srgrimes			*has_hostp = 1;
19951558Srgrimes			usedarg++;
19961558Srgrimes			opt_flags |= OP_NET;
19971558Srgrimes		} else if (!strcmp(cpopt, "alldirs")) {
19981558Srgrimes			opt_flags |= OP_ALLDIRS;
199927447Sdfr		} else if (!strcmp(cpopt, "public")) {
200027447Sdfr			*exflagsp |= MNT_EXPUBLIC;
200127447Sdfr		} else if (!strcmp(cpopt, "webnfs")) {
200227447Sdfr			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
200327447Sdfr			opt_flags |= OP_MAPALL;
200427447Sdfr		} else if (cpoptarg && !strcmp(cpopt, "index")) {
200527447Sdfr			ep->ex_indexfile = strdup(cpoptarg);
2006100336Sjoerg		} else if (!strcmp(cpopt, "quiet")) {
2007100336Sjoerg			opt_flags |= OP_QUIET;
2008184588Sdfr		} else if (!strcmp(cpopt, "sec")) {
2009184588Sdfr			if (parsesec(cpoptarg, ep))
2010184588Sdfr				return (1);
2011184588Sdfr			opt_flags |= OP_SEC;
2012184588Sdfr			usedarg++;
20131558Srgrimes		} else {
201437663Scharnier			syslog(LOG_ERR, "bad opt %s", cpopt);
20151558Srgrimes			return (1);
20161558Srgrimes		}
20171558Srgrimes		if (usedarg >= 0) {
20181558Srgrimes			*endcp = savedc2;
20191558Srgrimes			**endcpp = savedc;
20201558Srgrimes			if (usedarg > 0) {
20211558Srgrimes				*cpp = cp;
20221558Srgrimes				*endcpp = endcp;
20231558Srgrimes			}
20241558Srgrimes			return (0);
20251558Srgrimes		}
20261558Srgrimes		cpopt = cpoptend;
20271558Srgrimes	}
20281558Srgrimes	**endcpp = savedc;
20291558Srgrimes	return (0);
20301558Srgrimes}
20311558Srgrimes
20321558Srgrimes/*
20331558Srgrimes * Translate a character string to the corresponding list of network
20341558Srgrimes * addresses for a hostname.
20351558Srgrimes */
20361558Srgrimesint
2037216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
20381558Srgrimes{
20397401Swpaul	struct grouplist *checkgrp;
204075635Siedowse	struct addrinfo *ai, *tai, hints;
204174462Salfred	int ecode;
204274462Salfred	char host[NI_MAXHOST];
20431558Srgrimes
204474462Salfred	if (grp->gr_type != GT_NULL) {
204574462Salfred		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
20461558Srgrimes		return (1);
20471558Srgrimes	}
204874462Salfred	memset(&hints, 0, sizeof hints);
204974462Salfred	hints.ai_flags = AI_CANONNAME;
205074462Salfred	hints.ai_protocol = IPPROTO_UDP;
205174462Salfred	ecode = getaddrinfo(cp, NULL, &hints, &ai);
205274462Salfred	if (ecode != 0) {
205375635Siedowse		syslog(LOG_ERR,"can't get address info for host %s", cp);
205474462Salfred		return 1;
205574462Salfred	}
205674462Salfred	grp->gr_ptr.gt_addrinfo = ai;
205774462Salfred	while (ai != NULL) {
205874462Salfred		if (ai->ai_canonname == NULL) {
205974462Salfred			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2060146187Sume			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
206174462Salfred				strlcpy(host, "?", sizeof(host));
206274462Salfred			ai->ai_canonname = strdup(host);
206374462Salfred			ai->ai_flags |= AI_CANONNAME;
206475641Siedowse		}
206574462Salfred		if (debug)
206675635Siedowse			fprintf(stderr, "got host %s\n", ai->ai_canonname);
206775635Siedowse		/*
206875635Siedowse		 * Sanity check: make sure we don't already have an entry
206975635Siedowse		 * for this host in the grouplist.
207075635Siedowse		 */
207175635Siedowse		for (checkgrp = tgrp; checkgrp != NULL;
207275635Siedowse		    checkgrp = checkgrp->gr_next) {
207375635Siedowse			if (checkgrp->gr_type != GT_HOST)
207475635Siedowse				continue;
207575635Siedowse			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
207675635Siedowse			    tai = tai->ai_next) {
207775801Siedowse				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
207875635Siedowse					continue;
207975635Siedowse				if (debug)
208075635Siedowse					fprintf(stderr,
208175635Siedowse					    "ignoring duplicate host %s\n",
208275635Siedowse					    ai->ai_canonname);
208375635Siedowse				grp->gr_type = GT_IGNORE;
208475635Siedowse				return (0);
208575635Siedowse			}
208675635Siedowse		}
208774462Salfred		ai = ai->ai_next;
20881558Srgrimes	}
208975635Siedowse	grp->gr_type = GT_HOST;
20901558Srgrimes	return (0);
20911558Srgrimes}
20921558Srgrimes
20931558Srgrimes/*
20941558Srgrimes * Free up an exports list component
20951558Srgrimes */
20961558Srgrimesvoid
2097216587Scharnierfree_exp(struct exportlist *ep)
20981558Srgrimes{
20991558Srgrimes
21001558Srgrimes	if (ep->ex_defdir) {
21011558Srgrimes		free_host(ep->ex_defdir->dp_hosts);
21021558Srgrimes		free((caddr_t)ep->ex_defdir);
21031558Srgrimes	}
21041558Srgrimes	if (ep->ex_fsdir)
21051558Srgrimes		free(ep->ex_fsdir);
210627447Sdfr	if (ep->ex_indexfile)
210727447Sdfr		free(ep->ex_indexfile);
21081558Srgrimes	free_dir(ep->ex_dirl);
21091558Srgrimes	free((caddr_t)ep);
21101558Srgrimes}
21111558Srgrimes
21121558Srgrimes/*
21131558Srgrimes * Free hosts.
21141558Srgrimes */
21151558Srgrimesvoid
2116216587Scharnierfree_host(struct hostlist *hp)
21171558Srgrimes{
21181558Srgrimes	struct hostlist *hp2;
21191558Srgrimes
21201558Srgrimes	while (hp) {
21211558Srgrimes		hp2 = hp;
21221558Srgrimes		hp = hp->ht_next;
21231558Srgrimes		free((caddr_t)hp2);
21241558Srgrimes	}
21251558Srgrimes}
21261558Srgrimes
21271558Srgrimesstruct hostlist *
2128216587Scharnierget_ht(void)
21291558Srgrimes{
21301558Srgrimes	struct hostlist *hp;
21311558Srgrimes
21321558Srgrimes	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
21331558Srgrimes	if (hp == (struct hostlist *)NULL)
21341558Srgrimes		out_of_mem();
21351558Srgrimes	hp->ht_next = (struct hostlist *)NULL;
21369336Sdfr	hp->ht_flag = 0;
21371558Srgrimes	return (hp);
21381558Srgrimes}
21391558Srgrimes
21401558Srgrimes/*
21411558Srgrimes * Out of memory, fatal
21421558Srgrimes */
21431558Srgrimesvoid
2144216587Scharnierout_of_mem(void)
21451558Srgrimes{
21461558Srgrimes
214737663Scharnier	syslog(LOG_ERR, "out of memory");
21481558Srgrimes	exit(2);
21491558Srgrimes}
21501558Srgrimes
21511558Srgrimes/*
2152158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into
21531558Srgrimes * the kernel.
21541558Srgrimes */
21551558Srgrimesint
2156158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2157158857Srodrigc    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
21581558Srgrimes{
215975841Siedowse	struct statfs fsb1;
216074462Salfred	struct addrinfo *ai;
2161192934Srmacklem	struct export_args ea, *eap;
2162158857Srodrigc	char errmsg[255];
2163158857Srodrigc	char *cp;
21641558Srgrimes	int done;
2165158857Srodrigc	char savedc;
2166158857Srodrigc	struct iovec *iov;
2167184588Sdfr	int i, iovlen;
2168158857Srodrigc	int ret;
2169192934Srmacklem	struct nfsex_args nfsea;
21701558Srgrimes
2171192934Srmacklem	if (run_v4server > 0)
2172192934Srmacklem		eap = &nfsea.export;
2173192934Srmacklem	else
2174192934Srmacklem		eap = &ea;
2175192934Srmacklem
2176158857Srodrigc	cp = NULL;
2177158857Srodrigc	savedc = '\0';
2178158857Srodrigc	iov = NULL;
2179158857Srodrigc	iovlen = 0;
2180158857Srodrigc	ret = 0;
218175801Siedowse
2182192934Srmacklem	bzero(eap, sizeof (struct export_args));
2183158857Srodrigc	bzero(errmsg, sizeof(errmsg));
2184192934Srmacklem	eap->ex_flags = exflags;
2185192934Srmacklem	eap->ex_anon = *anoncrp;
2186192934Srmacklem	eap->ex_indexfile = ep->ex_indexfile;
218775641Siedowse	if (grp->gr_type == GT_HOST)
218874462Salfred		ai = grp->gr_ptr.gt_addrinfo;
218975641Siedowse	else
219075641Siedowse		ai = NULL;
2191192934Srmacklem	eap->ex_numsecflavors = ep->ex_numsecflavors;
2192192934Srmacklem	for (i = 0; i < eap->ex_numsecflavors; i++)
2193192934Srmacklem		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2194192934Srmacklem	if (eap->ex_numsecflavors == 0) {
2195192934Srmacklem		eap->ex_numsecflavors = 1;
2196192934Srmacklem		eap->ex_secflavors[0] = AUTH_SYS;
2197184588Sdfr	}
21981558Srgrimes	done = FALSE;
2199158857Srodrigc
2200192934Srmacklem	if (v4root_phase == 0) {
2201192934Srmacklem		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2202192934Srmacklem		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2203192934Srmacklem		build_iovec(&iov, &iovlen, "from", NULL, 0);
2204192934Srmacklem		build_iovec(&iov, &iovlen, "update", NULL, 0);
2205192934Srmacklem		build_iovec(&iov, &iovlen, "export", eap,
2206192934Srmacklem		    sizeof (struct export_args));
2207192934Srmacklem		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2208192934Srmacklem	}
2209158857Srodrigc
22101558Srgrimes	while (!done) {
22111558Srgrimes		switch (grp->gr_type) {
22121558Srgrimes		case GT_HOST:
221375641Siedowse			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
221474462Salfred				goto skip;
2215192934Srmacklem			eap->ex_addr = ai->ai_addr;
2216192934Srmacklem			eap->ex_addrlen = ai->ai_addrlen;
2217192934Srmacklem			eap->ex_masklen = 0;
22181558Srgrimes			break;
22191558Srgrimes		case GT_NET:
222075801Siedowse			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
222174462Salfred			    have_v6 == 0)
222274462Salfred				goto skip;
2223192934Srmacklem			eap->ex_addr =
222475801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2225192934Srmacklem			eap->ex_addrlen =
2226158857Srodrigc			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2227192934Srmacklem			eap->ex_mask =
222875801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2229192934Srmacklem			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
22301558Srgrimes			break;
223175641Siedowse		case GT_DEFAULT:
2232192934Srmacklem			eap->ex_addr = NULL;
2233192934Srmacklem			eap->ex_addrlen = 0;
2234192934Srmacklem			eap->ex_mask = NULL;
2235192934Srmacklem			eap->ex_masklen = 0;
223675641Siedowse			break;
22377401Swpaul		case GT_IGNORE:
2238158857Srodrigc			ret = 0;
2239158857Srodrigc			goto error_exit;
22407401Swpaul			break;
22411558Srgrimes		default:
224237663Scharnier			syslog(LOG_ERR, "bad grouptype");
22431558Srgrimes			if (cp)
22441558Srgrimes				*cp = savedc;
2245158857Srodrigc			ret = 1;
2246158857Srodrigc			goto error_exit;
22471558Srgrimes		};
22481558Srgrimes
22491558Srgrimes		/*
2250192934Srmacklem		 * For V4:, use the nfssvc() syscall, instead of mount().
22511558Srgrimes		 */
2252192934Srmacklem		if (v4root_phase == 2) {
2253192934Srmacklem			nfsea.fspec = v4root_dirpath;
2254192934Srmacklem			if (run_v4server > 0 &&
2255192934Srmacklem			    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2256192934Srmacklem				syslog(LOG_ERR, "Exporting V4: failed");
2257192934Srmacklem				return (2);
2258158857Srodrigc			}
2259192934Srmacklem		} else {
2260192934Srmacklem			/*
2261192934Srmacklem			 * XXX:
2262192934Srmacklem			 * Maybe I should just use the fsb->f_mntonname path
2263192934Srmacklem			 * instead of looping back up the dirp to the mount
2264192934Srmacklem			 * point??
2265192934Srmacklem			 * Also, needs to know how to export all types of local
2266192934Srmacklem			 * exportable filesystems and not just "ufs".
2267192934Srmacklem			 */
2268192934Srmacklem			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2269192934Srmacklem			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2270192934Srmacklem			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2271192934Srmacklem			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2272192934Srmacklem			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2273192934Srmacklem			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2274192934Srmacklem
2275192934Srmacklem			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2276192934Srmacklem				if (cp)
2277192934Srmacklem					*cp-- = savedc;
2278192934Srmacklem				else
2279192934Srmacklem					cp = dirp + dirplen - 1;
2280192934Srmacklem				if (opt_flags & OP_QUIET) {
2281192934Srmacklem					ret = 1;
2282192934Srmacklem					goto error_exit;
2283192934Srmacklem				}
2284192934Srmacklem				if (errno == EPERM) {
2285192934Srmacklem					if (debug)
2286192934Srmacklem						warnx("can't change attributes for %s",
2287192934Srmacklem						    dirp);
2288192934Srmacklem					syslog(LOG_ERR,
2289192934Srmacklem					   "can't change attributes for %s",
229075635Siedowse					    dirp);
2291192934Srmacklem					ret = 1;
2292192934Srmacklem					goto error_exit;
2293192934Srmacklem				}
2294192934Srmacklem				if (opt_flags & OP_ALLDIRS) {
2295192934Srmacklem					if (errno == EINVAL)
2296192934Srmacklem						syslog(LOG_ERR,
2297100336Sjoerg		"-alldirs requested but %s is not a filesystem mountpoint",
2298192934Srmacklem						    dirp);
2299192934Srmacklem					else
2300192934Srmacklem						syslog(LOG_ERR,
2301192934Srmacklem						    "could not remount %s: %m",
2302192934Srmacklem						    dirp);
2303192934Srmacklem					ret = 1;
2304192934Srmacklem					goto error_exit;
2305192934Srmacklem				}
2306192934Srmacklem				/* back up over the last component */
2307192934Srmacklem				while (*cp == '/' && cp > dirp)
2308192934Srmacklem					cp--;
2309192934Srmacklem				while (*(cp - 1) != '/' && cp > dirp)
2310192934Srmacklem					cp--;
2311192934Srmacklem				if (cp == dirp) {
2312192934Srmacklem					if (debug)
2313192934Srmacklem						warnx("mnt unsucc");
2314192934Srmacklem					syslog(LOG_ERR, "can't export %s %s",
2315192934Srmacklem					    dirp, errmsg);
2316192934Srmacklem					ret = 1;
2317192934Srmacklem					goto error_exit;
2318192934Srmacklem				}
2319192934Srmacklem				savedc = *cp;
2320192934Srmacklem				*cp = '\0';
2321192934Srmacklem				/*
2322192934Srmacklem				 * Check that we're still on the same
2323192934Srmacklem				 * filesystem.
2324192934Srmacklem				 */
2325192934Srmacklem				if (statfs(dirp, &fsb1) != 0 ||
2326192934Srmacklem				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2327192934Srmacklem				    sizeof (fsb1.f_fsid)) != 0) {
2328192934Srmacklem					*cp = savedc;
2329100336Sjoerg					syslog(LOG_ERR,
2330192934Srmacklem					    "can't export %s %s", dirp,
2331192934Srmacklem					    errmsg);
2332192934Srmacklem					ret = 1;
2333192934Srmacklem					goto error_exit;
2334192934Srmacklem				}
23351558Srgrimes			}
23361558Srgrimes		}
2337192934Srmacklem
2338192934Srmacklem		/*
2339192934Srmacklem		 * For the experimental server:
2340192934Srmacklem		 * If this is the public directory, get the file handle
2341192934Srmacklem		 * and load it into the kernel via the nfssvc() syscall.
2342192934Srmacklem		 */
2343192934Srmacklem		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
2344192934Srmacklem			fhandle_t fh;
2345192934Srmacklem			char *public_name;
2346192934Srmacklem
2347192934Srmacklem			if (eap->ex_indexfile != NULL)
2348192934Srmacklem				public_name = eap->ex_indexfile;
2349192934Srmacklem			else
2350192934Srmacklem				public_name = dirp;
2351192934Srmacklem			if (getfh(public_name, &fh) < 0)
2352192934Srmacklem				syslog(LOG_ERR,
2353192934Srmacklem				    "Can't get public fh for %s", public_name);
2354192934Srmacklem			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2355192934Srmacklem				syslog(LOG_ERR,
2356192934Srmacklem				    "Can't set public fh for %s", public_name);
2357192934Srmacklem			else
2358192934Srmacklem				has_publicfh = 1;
2359192934Srmacklem		}
236074462Salfredskip:
236175641Siedowse		if (ai != NULL)
236274462Salfred			ai = ai->ai_next;
236375641Siedowse		if (ai == NULL)
23641558Srgrimes			done = TRUE;
23651558Srgrimes	}
23661558Srgrimes	if (cp)
23671558Srgrimes		*cp = savedc;
2368158857Srodrigcerror_exit:
2369158857Srodrigc	/* free strings allocated by strdup() in getmntopts.c */
2370158857Srodrigc	if (iov != NULL) {
2371158857Srodrigc		free(iov[0].iov_base); /* fstype */
2372158857Srodrigc		free(iov[2].iov_base); /* fspath */
2373158857Srodrigc		free(iov[4].iov_base); /* from */
2374158857Srodrigc		free(iov[6].iov_base); /* update */
2375158857Srodrigc		free(iov[8].iov_base); /* export */
2376158857Srodrigc		free(iov[10].iov_base); /* errmsg */
2377158857Srodrigc
2378158857Srodrigc		/* free iov, allocated by realloc() */
2379158857Srodrigc		free(iov);
2380158857Srodrigc	}
2381158857Srodrigc	return (ret);
23821558Srgrimes}
23831558Srgrimes
23841558Srgrimes/*
23851558Srgrimes * Translate a net address.
238675801Siedowse *
238775801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
23881558Srgrimes */
23891558Srgrimesint
2390216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg)
23911558Srgrimes{
239275861Siedowse	struct netent *np = NULL;
239374462Salfred	char *name, *p, *prefp;
239475801Siedowse	struct sockaddr_in sin;
239575861Siedowse	struct sockaddr *sa = NULL;
239674462Salfred	struct addrinfo hints, *ai = NULL;
239774462Salfred	char netname[NI_MAXHOST];
239874462Salfred	long preflen;
23991558Srgrimes
240075635Siedowse	p = prefp = NULL;
240174462Salfred	if ((opt_flags & OP_MASKLEN) && !maskflg) {
240274462Salfred		p = strchr(cp, '/');
240374462Salfred		*p = '\0';
240474462Salfred		prefp = p + 1;
240574462Salfred	}
240674462Salfred
240775861Siedowse	/*
240875861Siedowse	 * Check for a numeric address first. We wish to avoid
240975861Siedowse	 * possible DNS lookups in getnetbyname().
241075861Siedowse	 */
241175861Siedowse	if (isxdigit(*cp) || *cp == ':') {
241274462Salfred		memset(&hints, 0, sizeof hints);
241375801Siedowse		/* Ensure the mask and the network have the same family. */
241475801Siedowse		if (maskflg && (opt_flags & OP_NET))
241575801Siedowse			hints.ai_family = net->nt_net.ss_family;
241675801Siedowse		else if (!maskflg && (opt_flags & OP_HAVEMASK))
241775801Siedowse			hints.ai_family = net->nt_mask.ss_family;
241875801Siedowse		else
241975801Siedowse			hints.ai_family = AF_UNSPEC;
242074462Salfred		hints.ai_flags = AI_NUMERICHOST;
242175861Siedowse		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
242275861Siedowse			sa = ai->ai_addr;
242375861Siedowse		if (sa != NULL && ai->ai_family == AF_INET) {
242474462Salfred			/*
242575801Siedowse			 * The address in `cp' is really a network address, so
242675801Siedowse			 * use inet_network() to re-interpret this correctly.
242775801Siedowse			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
242874462Salfred			 */
242975801Siedowse			bzero(&sin, sizeof sin);
243074462Salfred			sin.sin_family = AF_INET;
243174462Salfred			sin.sin_len = sizeof sin;
243275801Siedowse			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
243374462Salfred			if (debug)
243475801Siedowse				fprintf(stderr, "get_net: v4 addr %s\n",
243575801Siedowse				    inet_ntoa(sin.sin_addr));
243674462Salfred			sa = (struct sockaddr *)&sin;
243775861Siedowse		}
243875861Siedowse	}
243975861Siedowse	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
244075861Siedowse		bzero(&sin, sizeof sin);
244175861Siedowse		sin.sin_family = AF_INET;
244275861Siedowse		sin.sin_len = sizeof sin;
244375861Siedowse		sin.sin_addr = inet_makeaddr(np->n_net, 0);
244475861Siedowse		sa = (struct sockaddr *)&sin;
244575861Siedowse	}
244675861Siedowse	if (sa == NULL)
244774462Salfred		goto fail;
244825318Spst
244975801Siedowse	if (maskflg) {
245075801Siedowse		/* The specified sockaddr is a mask. */
245175801Siedowse		if (checkmask(sa) != 0)
245275801Siedowse			goto fail;
245375801Siedowse		bcopy(sa, &net->nt_mask, sa->sa_len);
245475801Siedowse		opt_flags |= OP_HAVEMASK;
245575801Siedowse	} else {
245675801Siedowse		/* The specified sockaddr is a network address. */
245775801Siedowse		bcopy(sa, &net->nt_net, sa->sa_len);
245874462Salfred
245975801Siedowse		/* Get a network name for the export list. */
246075801Siedowse		if (np) {
246175801Siedowse			name = np->n_name;
246275801Siedowse		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2463146187Sume		   NULL, 0, NI_NUMERICHOST) == 0) {
246475801Siedowse			name = netname;
246575801Siedowse		} else {
246675801Siedowse			goto fail;
246775801Siedowse		}
246875801Siedowse		if ((net->nt_name = strdup(name)) == NULL)
246975801Siedowse			out_of_mem();
247075801Siedowse
247175801Siedowse		/*
247275801Siedowse		 * Extract a mask from either a "/<masklen>" suffix, or
247375801Siedowse		 * from the class of an IPv4 address.
247475801Siedowse		 */
247574462Salfred		if (opt_flags & OP_MASKLEN) {
247674462Salfred			preflen = strtol(prefp, NULL, 10);
247775801Siedowse			if (preflen < 0L || preflen == LONG_MAX)
247874462Salfred				goto fail;
247975801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
248075801Siedowse			if (makemask(&net->nt_mask, (int)preflen) != 0)
248175801Siedowse				goto fail;
248275801Siedowse			opt_flags |= OP_HAVEMASK;
248374462Salfred			*p = '/';
248475801Siedowse		} else if (sa->sa_family == AF_INET &&
248575801Siedowse		    (opt_flags & OP_MASK) == 0) {
248675801Siedowse			in_addr_t addr;
248774462Salfred
248875801Siedowse			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
248975801Siedowse			if (IN_CLASSA(addr))
249075801Siedowse				preflen = 8;
249175801Siedowse			else if (IN_CLASSB(addr))
249275801Siedowse				preflen = 16;
249375801Siedowse			else if (IN_CLASSC(addr))
249475801Siedowse				preflen = 24;
249575801Siedowse			else if (IN_CLASSD(addr))
249675801Siedowse				preflen = 28;
249775801Siedowse			else
249875801Siedowse				preflen = 32;	/* XXX */
249975801Siedowse
250075801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
250175801Siedowse			makemask(&net->nt_mask, (int)preflen);
250275801Siedowse			opt_flags |= OP_HAVEMASK;
250374462Salfred		}
250474462Salfred	}
250574462Salfred
250674462Salfred	if (ai)
250774462Salfred		freeaddrinfo(ai);
250874462Salfred	return 0;
250974462Salfred
251074462Salfredfail:
251174462Salfred	if (ai)
251274462Salfred		freeaddrinfo(ai);
251374462Salfred	return 1;
25141558Srgrimes}
25151558Srgrimes
25161558Srgrimes/*
25171558Srgrimes * Parse out the next white space separated field
25181558Srgrimes */
25191558Srgrimesvoid
2520216587Scharniernextfield(char **cp, char **endcp)
25211558Srgrimes{
25221558Srgrimes	char *p;
25231558Srgrimes
25241558Srgrimes	p = *cp;
25251558Srgrimes	while (*p == ' ' || *p == '\t')
25261558Srgrimes		p++;
25271558Srgrimes	if (*p == '\n' || *p == '\0')
25281558Srgrimes		*cp = *endcp = p;
25291558Srgrimes	else {
25301558Srgrimes		*cp = p++;
25311558Srgrimes		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
25321558Srgrimes			p++;
25331558Srgrimes		*endcp = p;
25341558Srgrimes	}
25351558Srgrimes}
25361558Srgrimes
25371558Srgrimes/*
25381558Srgrimes * Get an exports file line. Skip over blank lines and handle line
25391558Srgrimes * continuations.
25401558Srgrimes */
25411558Srgrimesint
2542216587Scharnierget_line(void)
25431558Srgrimes{
25441558Srgrimes	char *p, *cp;
254596622Siedowse	size_t len;
25461558Srgrimes	int totlen, cont_line;
25471558Srgrimes
25481558Srgrimes	/*
25491558Srgrimes	 * Loop around ignoring blank lines and getting all continuation lines.
25501558Srgrimes	 */
25511558Srgrimes	p = line;
25521558Srgrimes	totlen = 0;
25531558Srgrimes	do {
255496622Siedowse		if ((p = fgetln(exp_file, &len)) == NULL)
25551558Srgrimes			return (0);
25561558Srgrimes		cp = p + len - 1;
25571558Srgrimes		cont_line = 0;
25581558Srgrimes		while (cp >= p &&
25591558Srgrimes		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
25601558Srgrimes			if (*cp == '\\')
25611558Srgrimes				cont_line = 1;
25621558Srgrimes			cp--;
25631558Srgrimes			len--;
25641558Srgrimes		}
256579117Sdd		if (cont_line) {
256679117Sdd			*++cp = ' ';
256779117Sdd			len++;
256879117Sdd		}
256996622Siedowse		if (linesize < len + totlen + 1) {
257096622Siedowse			linesize = len + totlen + 1;
257196622Siedowse			line = realloc(line, linesize);
257296622Siedowse			if (line == NULL)
257396622Siedowse				out_of_mem();
25741558Srgrimes		}
257596622Siedowse		memcpy(line + totlen, p, len);
257696622Siedowse		totlen += len;
257796622Siedowse		line[totlen] = '\0';
25781558Srgrimes	} while (totlen == 0 || cont_line);
25791558Srgrimes	return (1);
25801558Srgrimes}
25811558Srgrimes
25821558Srgrimes/*
25831558Srgrimes * Parse a description of a credential.
25841558Srgrimes */
25851558Srgrimesvoid
2586216587Scharnierparsecred(char *namelist, struct xucred *cr)
25871558Srgrimes{
25881558Srgrimes	char *name;
25891558Srgrimes	int cnt;
25901558Srgrimes	char *names;
25911558Srgrimes	struct passwd *pw;
25921558Srgrimes	struct group *gr;
2593194498Sbrooks	gid_t groups[XU_NGROUPS + 1];
2594136051Sstefanf	int ngroups;
25951558Srgrimes
259691354Sdd	cr->cr_version = XUCRED_VERSION;
25971558Srgrimes	/*
259837663Scharnier	 * Set up the unprivileged user.
25991558Srgrimes	 */
26001558Srgrimes	cr->cr_uid = -2;
26011558Srgrimes	cr->cr_groups[0] = -2;
26021558Srgrimes	cr->cr_ngroups = 1;
26031558Srgrimes	/*
26041558Srgrimes	 * Get the user's password table entry.
26051558Srgrimes	 */
26061558Srgrimes	names = strsep(&namelist, " \t\n");
26071558Srgrimes	name = strsep(&names, ":");
26081558Srgrimes	if (isdigit(*name) || *name == '-')
26091558Srgrimes		pw = getpwuid(atoi(name));
26101558Srgrimes	else
26111558Srgrimes		pw = getpwnam(name);
26121558Srgrimes	/*
26131558Srgrimes	 * Credentials specified as those of a user.
26141558Srgrimes	 */
26151558Srgrimes	if (names == NULL) {
26161558Srgrimes		if (pw == NULL) {
261737663Scharnier			syslog(LOG_ERR, "unknown user: %s", name);
26181558Srgrimes			return;
26191558Srgrimes		}
26201558Srgrimes		cr->cr_uid = pw->pw_uid;
2621194498Sbrooks		ngroups = XU_NGROUPS + 1;
26221558Srgrimes		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
262337663Scharnier			syslog(LOG_ERR, "too many groups");
26241558Srgrimes		/*
2625136051Sstefanf		 * Compress out duplicate.
26261558Srgrimes		 */
26271558Srgrimes		cr->cr_ngroups = ngroups - 1;
26281558Srgrimes		cr->cr_groups[0] = groups[0];
26291558Srgrimes		for (cnt = 2; cnt < ngroups; cnt++)
26301558Srgrimes			cr->cr_groups[cnt - 1] = groups[cnt];
26311558Srgrimes		return;
26321558Srgrimes	}
26331558Srgrimes	/*
26341558Srgrimes	 * Explicit credential specified as a colon separated list:
26351558Srgrimes	 *	uid:gid:gid:...
26361558Srgrimes	 */
26371558Srgrimes	if (pw != NULL)
26381558Srgrimes		cr->cr_uid = pw->pw_uid;
26391558Srgrimes	else if (isdigit(*name) || *name == '-')
26401558Srgrimes		cr->cr_uid = atoi(name);
26411558Srgrimes	else {
264237663Scharnier		syslog(LOG_ERR, "unknown user: %s", name);
26431558Srgrimes		return;
26441558Srgrimes	}
26451558Srgrimes	cr->cr_ngroups = 0;
2646194498Sbrooks	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
26471558Srgrimes		name = strsep(&names, ":");
26481558Srgrimes		if (isdigit(*name) || *name == '-') {
26491558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
26501558Srgrimes		} else {
26511558Srgrimes			if ((gr = getgrnam(name)) == NULL) {
265237663Scharnier				syslog(LOG_ERR, "unknown group: %s", name);
26531558Srgrimes				continue;
26541558Srgrimes			}
26551558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
26561558Srgrimes		}
26571558Srgrimes	}
2658194498Sbrooks	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
265937663Scharnier		syslog(LOG_ERR, "too many groups");
26601558Srgrimes}
26611558Srgrimes
2662194880Sdfr#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
26631558Srgrimes/*
26641558Srgrimes * Routines that maintain the remote mounttab
26651558Srgrimes */
26661558Srgrimesvoid
2667216587Scharnierget_mountlist(void)
26681558Srgrimes{
26691558Srgrimes	struct mountlist *mlp, **mlpp;
267023681Speter	char *host, *dirp, *cp;
26711558Srgrimes	char str[STRSIZ];
26721558Srgrimes	FILE *mlfile;
26731558Srgrimes
26741558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
267553117Sbillf		if (errno == ENOENT)
267653117Sbillf			return;
267753117Sbillf		else {
267853117Sbillf			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
267953117Sbillf			return;
268053117Sbillf		}
26811558Srgrimes	}
26821558Srgrimes	mlpp = &mlhead;
26831558Srgrimes	while (fgets(str, STRSIZ, mlfile) != NULL) {
268423681Speter		cp = str;
268523681Speter		host = strsep(&cp, " \t\n");
268623681Speter		dirp = strsep(&cp, " \t\n");
268723681Speter		if (host == NULL || dirp == NULL)
26881558Srgrimes			continue;
26891558Srgrimes		mlp = (struct mountlist *)malloc(sizeof (*mlp));
269037663Scharnier		if (mlp == (struct mountlist *)NULL)
269137663Scharnier			out_of_mem();
2692194880Sdfr		strncpy(mlp->ml_host, host, MNTNAMLEN);
2693194880Sdfr		mlp->ml_host[MNTNAMLEN] = '\0';
2694194880Sdfr		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2695194880Sdfr		mlp->ml_dirp[MNTPATHLEN] = '\0';
26961558Srgrimes		mlp->ml_next = (struct mountlist *)NULL;
26971558Srgrimes		*mlpp = mlp;
26981558Srgrimes		mlpp = &mlp->ml_next;
26991558Srgrimes	}
27001558Srgrimes	fclose(mlfile);
27011558Srgrimes}
27021558Srgrimes
270375635Siedowsevoid
270475635Siedowsedel_mlist(char *hostp, char *dirp)
27051558Srgrimes{
27061558Srgrimes	struct mountlist *mlp, **mlpp;
27071558Srgrimes	struct mountlist *mlp2;
27081558Srgrimes	FILE *mlfile;
27091558Srgrimes	int fnd = 0;
27101558Srgrimes
27111558Srgrimes	mlpp = &mlhead;
27121558Srgrimes	mlp = mlhead;
27131558Srgrimes	while (mlp) {
27141558Srgrimes		if (!strcmp(mlp->ml_host, hostp) &&
27151558Srgrimes		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
27161558Srgrimes			fnd = 1;
27171558Srgrimes			mlp2 = mlp;
27181558Srgrimes			*mlpp = mlp = mlp->ml_next;
27191558Srgrimes			free((caddr_t)mlp2);
27201558Srgrimes		} else {
27211558Srgrimes			mlpp = &mlp->ml_next;
27221558Srgrimes			mlp = mlp->ml_next;
27231558Srgrimes		}
27241558Srgrimes	}
27251558Srgrimes	if (fnd) {
27261558Srgrimes		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
272737663Scharnier			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
27281558Srgrimes			return;
27291558Srgrimes		}
27301558Srgrimes		mlp = mlhead;
27311558Srgrimes		while (mlp) {
27321558Srgrimes			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
27331558Srgrimes			mlp = mlp->ml_next;
27341558Srgrimes		}
27351558Srgrimes		fclose(mlfile);
27361558Srgrimes	}
27371558Srgrimes}
27381558Srgrimes
27391558Srgrimesvoid
2740216587Scharnieradd_mlist(char *hostp, char *dirp)
27411558Srgrimes{
27421558Srgrimes	struct mountlist *mlp, **mlpp;
27431558Srgrimes	FILE *mlfile;
27441558Srgrimes
27451558Srgrimes	mlpp = &mlhead;
27461558Srgrimes	mlp = mlhead;
27471558Srgrimes	while (mlp) {
27481558Srgrimes		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
27491558Srgrimes			return;
27501558Srgrimes		mlpp = &mlp->ml_next;
27511558Srgrimes		mlp = mlp->ml_next;
27521558Srgrimes	}
27531558Srgrimes	mlp = (struct mountlist *)malloc(sizeof (*mlp));
275437663Scharnier	if (mlp == (struct mountlist *)NULL)
275537663Scharnier		out_of_mem();
2756194880Sdfr	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
2757194880Sdfr	mlp->ml_host[MNTNAMLEN] = '\0';
2758194880Sdfr	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2759194880Sdfr	mlp->ml_dirp[MNTPATHLEN] = '\0';
27601558Srgrimes	mlp->ml_next = (struct mountlist *)NULL;
27611558Srgrimes	*mlpp = mlp;
27621558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
276337663Scharnier		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
27641558Srgrimes		return;
27651558Srgrimes	}
27661558Srgrimes	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
27671558Srgrimes	fclose(mlfile);
27681558Srgrimes}
27691558Srgrimes
27701558Srgrimes/*
27711558Srgrimes * Free up a group list.
27721558Srgrimes */
27731558Srgrimesvoid
2774216587Scharnierfree_grp(struct grouplist *grp)
27751558Srgrimes{
27761558Srgrimes	if (grp->gr_type == GT_HOST) {
277774462Salfred		if (grp->gr_ptr.gt_addrinfo != NULL)
277874462Salfred			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
27791558Srgrimes	} else if (grp->gr_type == GT_NET) {
27801558Srgrimes		if (grp->gr_ptr.gt_net.nt_name)
27811558Srgrimes			free(grp->gr_ptr.gt_net.nt_name);
27821558Srgrimes	}
27831558Srgrimes	free((caddr_t)grp);
27841558Srgrimes}
27851558Srgrimes
27861558Srgrimes#ifdef DEBUG
27871558Srgrimesvoid
27881558SrgrimesSYSLOG(int pri, const char *fmt, ...)
27891558Srgrimes{
27901558Srgrimes	va_list ap;
27911558Srgrimes
27921558Srgrimes	va_start(ap, fmt);
27931558Srgrimes	vfprintf(stderr, fmt, ap);
27941558Srgrimes	va_end(ap);
27951558Srgrimes}
27961558Srgrimes#endif /* DEBUG */
27971558Srgrimes
27981558Srgrimes/*
27991558Srgrimes * Check options for consistency.
28001558Srgrimes */
28011558Srgrimesint
2802216587Scharniercheck_options(struct dirlist *dp)
28031558Srgrimes{
28041558Srgrimes
2805192934Srmacklem	if (v4root_phase == 0 && dp == NULL)
28061558Srgrimes	    return (1);
280783653Speter	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
280883653Speter	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
28091558Srgrimes	    return (1);
28101558Srgrimes	}
28111558Srgrimes	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
281275801Siedowse		syslog(LOG_ERR, "-mask requires -network");
281375801Siedowse		return (1);
28141558Srgrimes	}
281575801Siedowse	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
281675801Siedowse		syslog(LOG_ERR, "-network requires mask specification");
281775801Siedowse		return (1);
281875801Siedowse	}
281975801Siedowse	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
282075801Siedowse		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
282175801Siedowse		return (1);
282275801Siedowse	}
2823192934Srmacklem	if (v4root_phase > 0 &&
2824192934Srmacklem	    (opt_flags &
2825192934Srmacklem	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
2826192934Srmacklem	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
2827192934Srmacklem	    return (1);
2828192934Srmacklem	}
2829207689Srmacklem	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2830207689Srmacklem	    syslog(LOG_ERR, "-alldirs has multiple directories");
2831207689Srmacklem	    return (1);
2832207689Srmacklem	}
28331558Srgrimes	return (0);
28341558Srgrimes}
28351558Srgrimes
28361558Srgrimes/*
28371558Srgrimes * Check an absolute directory path for any symbolic links. Return true
28381558Srgrimes */
28391558Srgrimesint
2840216587Scharniercheck_dirpath(char *dirp)
28411558Srgrimes{
28421558Srgrimes	char *cp;
28431558Srgrimes	int ret = 1;
28441558Srgrimes	struct stat sb;
28451558Srgrimes
28461558Srgrimes	cp = dirp + 1;
28471558Srgrimes	while (*cp && ret) {
28481558Srgrimes		if (*cp == '/') {
28491558Srgrimes			*cp = '\0';
28509336Sdfr			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
28511558Srgrimes				ret = 0;
28521558Srgrimes			*cp = '/';
28531558Srgrimes		}
28541558Srgrimes		cp++;
28551558Srgrimes	}
28569336Sdfr	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
28571558Srgrimes		ret = 0;
28581558Srgrimes	return (ret);
28591558Srgrimes}
28609336Sdfr
286175801Siedowse/*
286275801Siedowse * Make a netmask according to the specified prefix length. The ss_family
286375801Siedowse * and other non-address fields must be initialised before calling this.
286475801Siedowse */
286575801Siedowseint
286675801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen)
286774462Salfred{
286875801Siedowse	u_char *p;
286975801Siedowse	int bits, i, len;
287074462Salfred
287175801Siedowse	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
287275801Siedowse		return (-1);
2873103949Smike	if (bitlen > len * CHAR_BIT)
287475801Siedowse		return (-1);
287574462Salfred
287675801Siedowse	for (i = 0; i < len; i++) {
2877103949Smike		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
287875801Siedowse		*p++ = (1 << bits) - 1;
287975801Siedowse		bitlen -= bits;
288074462Salfred	}
288175801Siedowse	return 0;
288274462Salfred}
288374462Salfred
288475801Siedowse/*
288575801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask
288675801Siedowse * is acceptable (i.e. of the form 1...10....0).
288775801Siedowse */
288875801Siedowseint
288975801Siedowsecheckmask(struct sockaddr *sa)
289074462Salfred{
289175801Siedowse	u_char *mask;
289275801Siedowse	int i, len;
289374462Salfred
289475801Siedowse	if ((mask = sa_rawaddr(sa, &len)) == NULL)
289575801Siedowse		return (-1);
289675801Siedowse
289775801Siedowse	for (i = 0; i < len; i++)
289875801Siedowse		if (mask[i] != 0xff)
289975801Siedowse			break;
290075801Siedowse	if (i < len) {
290175801Siedowse		if (~mask[i] & (u_char)(~mask[i] + 1))
290275801Siedowse			return (-1);
290375801Siedowse		i++;
290474462Salfred	}
290575801Siedowse	for (; i < len; i++)
290675801Siedowse		if (mask[i] != 0)
290775801Siedowse			return (-1);
290875801Siedowse	return (0);
290974462Salfred}
291074462Salfred
291175801Siedowse/*
291275801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if
291375801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'.
291475801Siedowse * If samask is NULL, perform a full comparision.
291575801Siedowse */
291675801Siedowseint
291775801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
291874462Salfred{
291975801Siedowse	unsigned char *p1, *p2, *mask;
292075801Siedowse	int len, i;
292174462Salfred
292275801Siedowse	if (sa1->sa_family != sa2->sa_family ||
292375801Siedowse	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
292475801Siedowse	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
292575801Siedowse		return (1);
292675801Siedowse
292775801Siedowse	switch (sa1->sa_family) {
292874462Salfred	case AF_INET6:
292975801Siedowse		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
293075801Siedowse		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
293175801Siedowse			return (1);
293274462Salfred		break;
293374462Salfred	}
293474462Salfred
293575801Siedowse	/* Simple binary comparison if no mask specified. */
293675801Siedowse	if (samask == NULL)
293775801Siedowse		return (memcmp(p1, p2, len));
293874462Salfred
293975801Siedowse	/* Set up the mask, and do a mask-based comparison. */
294075801Siedowse	if (sa1->sa_family != samask->sa_family ||
294175801Siedowse	    (mask = sa_rawaddr(samask, NULL)) == NULL)
294275801Siedowse		return (1);
294374462Salfred
294475801Siedowse	for (i = 0; i < len; i++)
294575801Siedowse		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
294675801Siedowse			return (1);
294775801Siedowse	return (0);
294874462Salfred}
294974462Salfred
295075801Siedowse/*
295175801Siedowse * Return a pointer to the part of the sockaddr that contains the
295275801Siedowse * raw address, and set *nbytes to its length in bytes. Returns
295375801Siedowse * NULL if the address family is unknown.
295475801Siedowse */
295575801Siedowsevoid *
295675801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) {
295775801Siedowse	void *p;
295874462Salfred	int len;
295974462Salfred
296075801Siedowse	switch (sa->sa_family) {
296174462Salfred	case AF_INET:
296275801Siedowse		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
296375801Siedowse		p = &((struct sockaddr_in *)sa)->sin_addr;
296474462Salfred		break;
296574462Salfred	case AF_INET6:
296675801Siedowse		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
296775801Siedowse		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
296874462Salfred		break;
296974462Salfred	default:
297075801Siedowse		p = NULL;
297175801Siedowse		len = 0;
297274462Salfred	}
297374462Salfred
297475801Siedowse	if (nbytes != NULL)
297575801Siedowse		*nbytes = len;
297675801Siedowse	return (p);
297774462Salfred}
297874462Salfred
297975754Siedowsevoid
2980216587Scharnierhuphandler(int sig __unused)
298175754Siedowse{
298275754Siedowse	got_sighup = 1;
298375754Siedowse}
298475754Siedowse
2985216587Scharniervoid terminate(int sig __unused)
298674462Salfred{
2987149433Spjd	pidfile_remove(pfh);
2988194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
2989194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
299074462Salfred	exit (0);
299174462Salfred}
2992192934Srmacklem
2993