1/*
2 * Copyright (c) 1999-2011 Apple Inc.  All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * Copyright (c) 1989, 1993
25 *	The Regents of the University of California.  All rights reserved.
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
29 * are met:
30 * 1. Redistributions of source code must retain the above copyright
31 *    notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 *    notice, this list of conditions and the following disclaimer in the
34 *    documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 *    must display the following acknowledgement:
37 *	This product includes software developed by the University of
38 *	California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 *    may be used to endorse or promote products derived from this software
41 *    without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53 * SUCH DAMAGE.
54 */
55
56#include <sys/param.h>
57#include <sys/file.h>
58#include <sys/ioctl.h>
59#include <sys/malloc.h>
60#include <sys/mount.h>
61#include <sys/socket.h>
62#include <sys/sockio.h>
63#include <sys/stat.h>
64#include <sys/syslog.h>
65#include <sys/sysctl.h>
66#include <sys/ucred.h>
67#include <sys/wait.h>
68#include <sys/queue.h>
69#include <sys/types.h>
70
71#include <oncrpc/rpc.h>
72#include <oncrpc/pmap_clnt.h>
73#include <oncrpc/pmap_prot.h>
74#include <nfs/rpcv2.h>
75#include <nfs/nfsproto.h>
76#include <nfs/nfs.h>
77
78#include <net/if.h>
79#include <net/if_types.h>
80#include <net/if_dl.h>
81#include <net/route.h>
82#include <netinet/in.h>
83#include <arpa/inet.h>
84
85int bindresvport_sa(int sd, struct sockaddr *sa);
86
87#include <ctype.h>
88#include <err.h>
89#include <errno.h>
90#include <grp.h>
91#include <netdb.h>
92#include <pwd.h>
93#include <signal.h>
94#include <stdio.h>
95#include <stdlib.h>
96#include <string.h>
97#include <unistd.h>
98#include <pthread.h>
99
100#include <CoreFoundation/CoreFoundation.h>
101#include <DiskArbitration/DiskArbitration.h>
102
103#ifdef __LP64__
104typedef int		xdr_long_t;
105#else
106typedef long		xdr_long_t;
107#endif
108
109#include "pathnames.h"
110#include "common.h"
111
112/*
113 * Structure for maintaining list of export IDs for exported volumes
114 */
115struct expidlist {
116	LIST_ENTRY(expidlist)	xid_list;
117	char			xid_path[MAXPATHLEN];	/* exported sub-directory */
118	u_int32_t		xid_id;			/* export ID */
119};
120
121/*
122 * Structure for maintaining list of UUIDs for exported volumes
123 */
124struct uuidlist {
125	TAILQ_ENTRY(uuidlist)		ul_list;
126	char				ul_mntfromname[MAXPATHLEN];
127	char				ul_mntonname[MAXPATHLEN];
128	u_char				ul_uuid[16];	/* UUID used */
129	u_char				ul_dauuid[16];	/* DiskArb UUID */
130	char				ul_davalid;	/* DiskArb UUID valid */
131	char				ul_exported;	/* currently exported? */
132	u_int32_t			ul_fsid;	/* exported FS ID */
133	LIST_HEAD(expidhead,expidlist)	ul_exportids;	/* export ID list */
134};
135TAILQ_HEAD(,uuidlist) ulhead;
136#define UL_CHECK_MNTFROM	0x1
137#define UL_CHECK_MNTON		0x2
138#define UL_CHECK_ALL		0x3
139
140#define AOK	(void *)	// assert alignment is OK
141
142/*
143 * Default FSID is just a "hash" of UUID
144 */
145#define UUID2FSID(U) \
146	(*((u_int32_t*) AOK (U))     ^ *(((u_int32_t*) AOK (U))+1) ^ \
147	 *(((u_int32_t*) AOK (U))+2) ^ *(((u_int32_t*) AOK (U))+3))
148
149/*
150 * Structure for keeping the (outstanding) mount list
151 */
152struct mountlist {
153	struct mountlist	*ml_next;
154	char			*ml_host;	/* NFS client name or address */
155	char			*ml_dir;	/* mounted directory */
156};
157
158/*
159 * Structure used to hold a list of names
160 */
161struct namelist {
162	TAILQ_ENTRY(namelist)	nl_next;
163	char			*nl_name;
164};
165TAILQ_HEAD(namelisttqh, namelist);
166
167/*
168 * Structure used to hold a list of directories
169 */
170struct dirlist {
171	struct dirlist		*dl_next;
172	char			*dl_dir;
173};
174
175/*
176 * Structure used to hold an export error message.
177 */
178struct errlist {
179	LIST_ENTRY(errlist)	el_next;
180	uint32_t		el_linenum;
181	char			*el_msg;
182};
183
184/*
185 * Structures for keeping the export lists
186 */
187
188TAILQ_HEAD(expfstqh, expfs);
189TAILQ_HEAD(expdirtqh, expdir);
190TAILQ_HEAD(hosttqh, host);
191
192/*
193 * Structure to hold the exports for each exported file system.
194 */
195struct expfs {
196	TAILQ_ENTRY(expfs)	xf_next;
197	struct expdirtqh	xf_dirl;	/* list of exported directories */
198	int			xf_flag;	/* internal flags for this struct */
199	u_char			xf_uuid[16];	/* file system's UUID */
200	u_int32_t		xf_fsid;	/* exported FS ID */
201	char			*xf_fsdir;	/* mount point of this file system */
202};
203/* xf_flag bits */
204#define	XF_LINKED	0x1
205
206/*
207 * Structure to hold info about an exported directory
208 */
209struct expdir {
210	TAILQ_ENTRY(expdir)	xd_next;
211	struct hosttqh		xd_hosts;	/* List of hosts this dir exported to */
212	struct expdirtqh	xd_mountdirs;	/* list of mountable sub-directories */
213	int			xd_iflags;	/* internal flags for this structure */
214	int			xd_flags;	/* default export flags */
215	struct xucred		xd_cred;	/* default export mapped credential */
216	struct nfs_sec		xd_sec;		/* default security flavors */
217	int			xd_oflags;	/* old default export flags */
218	struct xucred		xd_ocred;	/* old default export mapped credential */
219	struct nfs_sec		xd_osec;	/* old default security flavors */
220	struct nfs_sec		xd_ssec;	/* security flavors for showmount */
221	char			*xd_dir;	/* pathname of exported directory */
222	struct expidlist 	*xd_xid;	/* corresponding export ID */
223};
224
225/*
226 * Structures for holding sets of exported-to hosts/nets/addresses
227 */
228
229/* holds a host address list and name */
230struct hostinfo {
231	char 		*h_name;	/* host name */
232	struct addrinfo *h_ailist;	/* address list */
233};
234
235/* holds a network/mask and name */
236struct netmsk {
237	char 		*nt_name;	/* network name */
238	sa_family_t	nt_family;	/* network family */
239	union {
240	    struct {
241		in_addr_t	net;	/* IPv4 network address */
242		in_addr_t	mask;	/* IPv4 network mask */
243	    } ipv4;
244	    struct {
245		struct in6_addr	net;	/* IPv6 network address */
246		struct in6_addr	mask;	/* IPv6 network mask */
247	    } ipv6;
248	} nt_u;
249};
250
251#define nt_net		nt_u.ipv4.net
252#define nt_mask		nt_u.ipv4.mask
253#define nt_net6		nt_u.ipv6.net
254#define nt_mask6	nt_u.ipv6.mask
255
256/* holds either a host or network */
257union grouptypes {
258	struct hostinfo gt_hostinfo;
259	struct netmsk	gt_net;
260	char *		gt_netgroup;
261};
262
263/* host/network list entry */
264struct grouplist {
265	struct grouplist *gr_cache;	/* linked list in cache */
266	struct grouplist *gr_next;	/* linked list in get_exportlist() */
267	int gr_refcnt;			/* #references on this group */
268	int16_t gr_type;		/* type of group */
269	int16_t gr_flags;		/* group flags */
270	union grouptypes gr_u;		/* the host/network */
271};
272/* Group types */
273#define	GT_NULL		0x0		/* not fully-initialized yet */
274#define	GT_NETGROUP	0x1		/* this is a netgroup */
275#define	GT_NET		0x2		/* this is a network */
276#define	GT_HOST		0x3		/* this is a single host address */
277/* Group flags */
278#define	GF_SHOW		0x1		/* show this entry in export list */
279
280/*
281 * host/network flags list entry
282 */
283struct host {
284	TAILQ_ENTRY(host) ht_next;
285	int		 ht_flags;	/* export options for these hosts */
286	struct xucred	 ht_cred;	/* mapped credential for these hosts */
287	struct grouplist *ht_grp;	/* host/network flags applies to */
288	struct nfs_sec	 ht_sec;	/* security flavors for these hosts */
289};
290
291struct fhreturn {
292	int		fhr_flags;
293	int		fhr_vers;
294	struct nfs_sec	fhr_sec;
295	fhandle_t	fhr_fh;
296};
297
298/* Global defs */
299int	add_name(struct namelisttqh *, char *);
300void	free_namelist(struct namelisttqh *);
301int	add_dir(struct dirlist **, char *);
302char *	add_expdir(struct expdir **, char *, int);
303int	add_grp(struct grouplist **, struct grouplist *);
304int	add_host(struct hosttqh *, struct host *);
305void	add_mlist(char *, char *);
306int	addrinfo_cmp(struct addrinfo *, struct addrinfo *);
307int	check_dirpath(char *);
308int	check_options(int);
309void	clear_export_error(uint32_t);
310int	cmp_secflavs(struct nfs_sec *, struct nfs_sec *);
311void	merge_secflavs(struct nfs_sec *, struct nfs_sec *);
312void	del_mlist(char *, char *);
313int	expdir_search(struct expfs *, char *, struct sockaddr *, int *, struct nfs_sec *);
314int	do_export(int, struct expfs *, struct expdir *, struct grouplist *, int,
315		struct xucred *, struct nfs_sec *);
316int	do_opt(char **, char **, struct grouplist *, int *,
317		int *, int *, struct xucred *, struct nfs_sec *, char *, u_char *);
318struct expfs *ex_search(u_char *);
319void	export_error(int, const char *, ...);
320void	export_error_cleanup(struct expfs *);
321struct host *find_group_address_match_in_host_list(struct hosttqh *, struct grouplist *);
322struct host *find_host(struct hosttqh *, struct sockaddr *);
323void	free_dirlist(struct dirlist *dl);
324void	free_expdir(struct expdir *);
325void	free_expfs(struct expfs *);
326void	free_grp(struct grouplist *);
327void	free_hosts(struct hosttqh *);
328void	free_host(struct host *);
329struct expdir *get_expdir(void);
330struct expfs *get_expfs(void);
331int	get_host_addresses(char *, struct grouplist *);
332struct host *get_host(void);
333int	get_export_entry(void);
334void	get_mountlist(void);
335int	get_net(char *, struct netmsk *, int);
336int	get_sec_flavors(char *flavorlist, struct nfs_sec *);
337struct grouplist *get_grp(struct grouplist *);
338const char *grp_addr(struct grouplist *);
339char *	grp_name(struct grouplist *);
340int	hang_options_setup(struct expdir *, int, struct xucred *, struct grouplist *, struct nfs_sec *, int *);
341void	hang_options_finalize(struct expdir *);
342void	hang_options_cleanup(struct expdir *);
343int	hang_options_mountdir(struct expdir *, char *, int, struct grouplist *, struct nfs_sec *);
344void	mntsrv(struct svc_req *, SVCXPRT *);
345void	nextfield(char **, char **);
346int	parsecred(char *, struct xucred *);
347int	put_exlist(struct expdir *, XDR *);
348int	subdir_check(char *, char *);
349int	xdr_dir(XDR *, char *);
350int	xdr_explist(XDR *, caddr_t);
351int	xdr_fhs(XDR *, caddr_t);
352int	xdr_mlist(XDR *, caddr_t);
353
354int	get_uuid_from_diskarb(const char *, u_char *);
355struct uuidlist * get_uuid_from_list(const struct statfs *, u_char *, const int);
356struct uuidlist * add_uuid_to_list(const struct statfs *, u_char *, u_char *);
357struct uuidlist * get_uuid(const struct statfs *, u_char *);
358struct uuidlist * find_uuid(u_char *);
359struct uuidlist * find_uuid_by_fsid(u_int32_t);
360void	uuidlist_clearexport(void);
361char *	uuidstring(u_char *, char *);
362void	uuidlist_save(void);
363void	uuidlist_restore(void);
364
365struct expidlist * find_export_id(struct uuidlist *, u_int32_t);
366struct expidlist * get_export_id(struct uuidlist *, char *);
367
368void dump_exports(void);
369void snprintf_cred(char *buf, int, struct xucred *cr);
370
371pthread_mutex_t export_mutex;		/* lock for mountd/export globals */
372struct expfstqh xfshead;		/* list of exported file systems */
373struct dirlist *xpaths;			/* list of exported paths */
374int xpaths_complete = 1;
375struct mountlist *mlhead;		/* remote mount list */
376struct grouplist *grpcache;		/* host/net group cache */
377struct xucred def_anon = {		/* default map credential: "nobody" */
378	XUCRED_VERSION,
379	(uid_t) -2,
380	1,
381	{ (gid_t) -2 },
382};
383
384LIST_HEAD(,errlist) xerrs;		/* list of export errors */
385int export_errors, hostnamecount, hostnamegoodcount, missingexportcount;
386SVCXPRT *udptransp, *tcptransp;
387SVCXPRT *udp6transp, *tcp6transp;
388int mounttcpsock, mountudpsock;
389int mounttcp6sock, mountudp6sock;
390
391/* export options */
392#define	OP_MAPROOT	0x00000001	/* map root credentials */
393#define	OP_MAPALL	0x00000002	/* map all credentials */
394#define	OP_SECFLAV	0x00000004	/* security flavor(s) specified */
395#define	OP_MASK		0x00000008	/* network mask specified */
396#define	OP_NET		0x00000010	/* network address specified */
397#define	OP_MANGLEDNAMES	0x00000020	/* tell the vfs to mangle names that are > 255 bytes */
398#define	OP_ALLDIRS	0x00000040	/* allow mounting subdirs */
399#define	OP_READONLY	0x00000080	/* export read-only */
400#define	OP_32BITCLIENTS	0x00000100	/* use 32-bit directory cookies */
401#define	OP_FSPATH	0x00000200	/* file system path specified */
402#define	OP_FSUUID	0x00000400	/* file system UUID specified */
403#define	OP_OFFLINE	0x00000800	/* export is offline */
404#define	OP_ONLINE	0x04000000	/* export is online */
405#define	OP_SHOW		0x08000000	/* show this entry in export list */
406#define	OP_MISSING	0x10000000	/* export is missing */
407#define	OP_DEFEXP	0x20000000	/* default export for everyone (else) */
408#define	OP_ADD		0x40000000	/* tag export for potential addition */
409#define	OP_DEL		0x80000000	/* tag export for potential deletion */
410#define	OP_EXOPTMASK	0x100009E3	/* export options mask */
411#define	OP_EXOPTS(X)	((X) & OP_EXOPTMASK)
412
413#define RECHECKEXPORTS_TIMEOUT			600
414#define RECHECKEXPORTS_DELAYED_STARTUP_TIMEOUT	120
415#define RECHECKEXPORTS_DELAYED_STARTUP_INTERVAL	5
416
417/*
418 * Mountd server for NFS mount protocol as described in:
419 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
420 * The optional arguments are the exports file name
421 * default: _PATH_EXPORTS
422 * and "-n" to allow nonroot mount.
423 */
424
425/*
426 * The incredibly complex mountd thread function
427 */
428void *
429mountd_thread(__unused void *arg)
430{
431	set_thread_sigmask();
432	svc_run();
433	log(LOG_ERR, "mountd died");
434	exit(1);
435}
436
437void
438mountd_init(void)
439{
440	int error;
441
442	TAILQ_INIT(&xfshead);
443	xpaths = NULL;
444	mlhead = NULL;
445	grpcache = NULL;
446	TAILQ_INIT(&ulhead);
447	LIST_INIT(&xerrs);
448
449	error = pthread_mutex_init(&export_mutex, NULL);
450	if (error) {
451		log(LOG_ERR, "export mutex init failed: %s (%d)", strerror(error), error);
452		exit(1);
453	}
454
455	uuidlist_restore();
456}
457
458void
459mountd(void)
460{
461	struct sockaddr_storage saddr;
462	struct sockaddr_in *sin = (struct sockaddr_in*)&saddr;
463	struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&saddr;
464	socklen_t socklen;
465	struct nfs_export_args nxa;
466	int error, on = 1, init_retry, svcregcnt;
467	pthread_t thd;
468	time_t init_start;
469
470	/* global initialization */
471	mountd_init();
472	check_for_mount_changes();
473
474	/*
475	 * mountd needs to start from a clean slate.
476	 */
477
478	/* clear the table of mounts at startup. */
479	unlink(_PATH_RMOUNTLIST);
480
481	/* Delete all exports that are in the kernel. */
482	bzero(&nxa, sizeof(nxa));
483	nxa.nxa_flags = NXA_DELETE_ALL;
484	error = nfssvc(NFSSVC_EXPORT, &nxa);
485	if (error && (errno != ENOENT))
486		log(LOG_ERR, "Can't delete all exports: %s (%d)", strerror(errno), errno);
487
488	/* set up the export and mount lists */
489
490	/*
491	 * Note that the recheckexports_until functionality will allow us to retry exports
492	 * for a while if we have problems resolving any host names.  However, these problems
493	 * may not affect all exports (e.g. default exports) and that could result in some
494	 * hosts getting the wrong access until their export options are set up properly.
495	 * Since this could result in some hosts receiving errors or erroneous processing,
496	 * we check if the first get_exportlist() check resulted in any successful host
497	 * name lookups.  If there were names and none of them were looked up successfully,
498	 * then we'll delay startup for a *short* while in an attempt to avoid any problems
499	 * if the problem clears up shortly.
500	 */
501	init_start = time(NULL);
502	init_retry = 0;
503	while (1) {
504		DEBUG(1, "Getting export list.");
505		get_exportlist();
506		if (!hostnamecount || hostnamegoodcount) {
507			if (init_retry)
508				log(LOG_WARNING, "host name resolution seems to be working now... continuing initialization");
509			break;
510		}
511		if (!init_retry) {
512			log(LOG_WARNING, "host name resolution seems to be having problems... delaying initialization");
513			init_retry = 1;
514		} else if (time(NULL) > (init_start + RECHECKEXPORTS_DELAYED_STARTUP_TIMEOUT)) {
515			log(LOG_WARNING, "giving up on host name resolution... continuing initialization");
516			break;
517		}
518		sleep(RECHECKEXPORTS_DELAYED_STARTUP_INTERVAL);
519	}
520
521	DEBUG(1, "Getting mount list.");
522	get_mountlist();
523	DEBUG(1, "Here we go.");
524
525	/* create mountd service sockets */
526	if (!config.udp && !config.tcp) {
527		log(LOG_WARNING, "No network transport(s) configured.  mountd thread not starting.");
528		return;
529	}
530
531	mountudpsock = mounttcpsock = -1;
532	mountudp6sock = mounttcp6sock = -1;
533
534	/* If we are serving UDP, set up the MOUNT/UDP socket. */
535	if (config.udp) {
536
537		/* IPv4 */
538		if ((mountudpsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
539			log(LOG_ERR, "can't create MOUNT/UDP IPv4 socket: %s (%d)", strerror(errno), errno);
540		if (mountudpsock >= 0) {
541			sin->sin_family = AF_INET;
542			sin->sin_addr.s_addr = INADDR_ANY;
543			sin->sin_port = htons(config.mount_port);
544			sin->sin_len = sizeof(*sin);
545			if (bindresvport_sa(mountudpsock, (struct sockaddr*)sin) < 0) {
546				/* socket may still be lingering from previous incarnation */
547				/* wait a few seconds and try again */
548				sleep(6);
549				if (bindresvport_sa(mountudpsock, (struct sockaddr*)sin) < 0) {
550					log(LOG_ERR, "can't bind MOUNT/UDP IPv4 addr: %s (%d)", strerror(errno), errno);
551					close(mountudpsock);
552					mountudpsock = -1;
553				}
554			}
555		}
556		if (mountudpsock >= 0) {
557			socklen = sizeof(*sin);
558			if (getsockname(mountudpsock, (struct sockaddr*)sin, &socklen)) {
559				log(LOG_ERR, "can't getsockname on MOUNT/UDP IPv4 socket: %s (%d)", strerror(errno), errno);
560				close(mountudpsock);
561				mountudpsock = -1;
562			} else {
563				mountudpport = ntohs(sin->sin_port);
564			}
565		}
566		if ((mountudpsock >= 0) && ((udptransp = svcudp_create(mountudpsock)) == NULL)) {
567			log(LOG_ERR, "Can't create MOUNT/UDP IPv4 service");
568			close(mountudpsock);
569			mountudpsock = -1;
570			mountudpport = 0;
571		}
572		if (mountudpsock >= 0) {
573			svcregcnt = 0;
574			if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, 0))
575				log(LOG_ERR, "Can't register IPv4 MOUNT/UDP v1 service");
576			else
577				svcregcnt++;
578			if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, 0))
579				log(LOG_ERR, "Can't register IPv4 MOUNT/UDP v3 service");
580			else
581				svcregcnt++;
582			if (!svcregcnt) {
583				svc_destroy(udptransp);
584				close(mountudpsock);
585				mountudpsock = -1;
586				mountudpport = 0;
587			}
588		}
589
590		/* IPv6 */
591		if ((mountudp6sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
592			log(LOG_ERR, "can't create MOUNT/UDP IPv6 socket: %s (%d)", strerror(errno), errno);
593		if (mountudp6sock >= 0) {
594			setsockopt(mountudp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
595			sin6->sin6_family = AF_INET6;
596			sin6->sin6_addr = in6addr_any;
597			sin6->sin6_port = htons(config.mount_port);
598			sin6->sin6_len = sizeof(*sin6);
599			if (bindresvport_sa(mountudp6sock, (struct sockaddr*)sin6) < 0) {
600				/* socket may still be lingering from previous incarnation */
601				/* wait a few seconds and try again */
602				sleep(6);
603				if (bindresvport_sa(mountudp6sock, (struct sockaddr*)sin6) < 0) {
604					log(LOG_ERR, "can't bind MOUNT/UDP IPv6 addr: %s (%d)", strerror(errno), errno);
605					close(mountudp6sock);
606					mountudp6sock = -1;
607				}
608			}
609		}
610		if (mountudp6sock >= 0) {
611			socklen = sizeof(*sin6);
612			if (getsockname(mountudp6sock, (struct sockaddr*)sin6, &socklen)) {
613				log(LOG_ERR, "can't getsockname on MOUNT/UDP IPv6 socket: %s (%d)", strerror(errno), errno);
614				close(mountudp6sock);
615				mountudp6sock = -1;
616			} else {
617				mountudp6port = ntohs(sin6->sin6_port);
618			}
619		}
620		if ((mountudp6sock >= 0) && ((udp6transp = svcudp_create(mountudp6sock)) == NULL)) {
621			log(LOG_ERR, "Can't create MOUNT/UDP IPv6 service");
622			close(mountudp6sock);
623			mountudp6sock = -1;
624			mountudp6port = 0;
625		}
626		if (mountudp6sock >= 0) {
627			svcregcnt = 0;
628			if (!svc_register(udp6transp, RPCPROG_MNT, 1, mntsrv, 0))
629				log(LOG_ERR, "Can't register IPv6 MOUNT/UDP v1 service");
630			else
631				svcregcnt++;
632			if (!svc_register(udp6transp, RPCPROG_MNT, 3, mntsrv, 0))
633				log(LOG_ERR, "Can't register IPv6 MOUNT/UDP v3 service");
634			else
635				svcregcnt++;
636			if (!svcregcnt) {
637				svc_destroy(udp6transp);
638				close(mountudp6sock);
639				mountudp6sock = -1;
640				mountudp6port = 0;
641			}
642		}
643
644	}
645
646	/* If we are serving TCP, set up the MOUNT/TCP socket. */
647	if (config.tcp) {
648
649		/* IPv4 */
650		if ((mounttcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
651			log(LOG_ERR, "can't create MOUNT/TCP IPv4 socket: %s (%d)", strerror(errno), errno);
652		if (mounttcpsock >= 0) {
653			if (setsockopt(mounttcpsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
654				log(LOG_WARNING, "setsockopt MOUNT/TCP IPv4 SO_REUSEADDR: %s (%d)", strerror(errno), errno);
655			sin->sin_family = AF_INET;
656			sin->sin_addr.s_addr = INADDR_ANY;
657			sin->sin_port = htons(config.mount_port);
658			sin->sin_len = sizeof(*sin);
659			if (bindresvport_sa(mounttcpsock, (struct sockaddr*)sin) < 0) {
660				log(LOG_ERR, "can't bind MOUNT/TCP IPv4 addr: %s (%d)", strerror(errno), errno);
661				close(mounttcpsock);
662				mounttcpsock = -1;
663			}
664		}
665		if ((mounttcpsock >= 0) && (listen(mounttcpsock, 128) < 0)) {
666			log(LOG_ERR, "MOUNT IPv4 listen failed: %s (%d)", strerror(errno), errno);
667			close(mounttcpsock);
668			mounttcpsock = -1;
669		}
670		if (mounttcpsock >= 0) {
671			socklen = sizeof(*sin);
672			if (getsockname(mounttcpsock, (struct sockaddr*)sin, &socklen)) {
673				log(LOG_ERR, "can't getsockname on MOUNT/TCP IPv4 socket: %s (%d)", strerror(errno), errno);
674				close(mounttcpsock);
675				mounttcpsock = -1;
676			} else {
677				mounttcpport = ntohs(sin->sin_port);
678			}
679		}
680		if ((mounttcpsock >= 0) && ((tcptransp = svctcp_create(mounttcpsock, 0, 0)) == NULL)) {
681			log(LOG_ERR, "Can't create MOUNT/TCP IPv4 service");
682			close(mounttcpsock);
683			mounttcpsock = -1;
684			mounttcpport = 0;
685		}
686		if (mounttcpsock >= 0) {
687			svcregcnt = 0;
688			if (!svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, 0))
689				log(LOG_ERR, "Can't register IPv4 MOUNT/TCP v1 service");
690			else
691				svcregcnt++;
692			if (!svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, 0))
693				log(LOG_ERR, "Can't register IPv4 MOUNT/TCP v3 service");
694			else
695				svcregcnt++;
696			if (!svcregcnt) {
697				svc_destroy(tcptransp);
698				close(mounttcpsock);
699				mounttcpsock = -1;
700				mounttcpport = 0;
701			}
702		}
703
704		/* IPv6 */
705		if ((mounttcp6sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
706			log(LOG_ERR, "can't create MOUNT/TCP IPv6 socket: %s (%d)", strerror(errno), errno);
707		if (mounttcp6sock >= 0) {
708			if (setsockopt(mounttcp6sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
709				log(LOG_WARNING, "setsockopt MOUNT/TCP IPv6 SO_REUSEADDR: %s (%d)", strerror(errno), errno);
710			setsockopt(mounttcp6sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
711			sin6->sin6_family = AF_INET6;
712			sin6->sin6_addr = in6addr_any;
713			sin6->sin6_port = htons(config.mount_port);
714			sin6->sin6_len = sizeof(*sin6);
715			if (bindresvport_sa(mounttcp6sock, (struct sockaddr*)sin6) < 0) {
716				log(LOG_ERR, "can't bind MOUNT/TCP IPv6 addr: %s (%d)", strerror(errno), errno);
717				close(mounttcp6sock);
718				mounttcp6sock = -1;
719			}
720		}
721		if ((mounttcp6sock >= 0) && (listen(mounttcp6sock, 128) < 0)) {
722			log(LOG_ERR, "MOUNT IPv6 listen failed: %s (%d)", strerror(errno), errno);
723			close(mounttcp6sock);
724			mounttcp6sock = -1;
725		}
726		if (mounttcp6sock >= 0) {
727			socklen = sizeof(*sin6);
728			if (getsockname(mounttcp6sock, (struct sockaddr*)sin6, &socklen)) {
729				log(LOG_ERR, "can't getsockname on MOUNT/TCP IPv6 socket: %s (%d)", strerror(errno), errno);
730				close(mounttcp6sock);
731				mounttcp6sock = -1;
732			} else {
733				mounttcp6port = ntohs(sin6->sin6_port);
734			}
735		}
736		if ((mounttcp6sock >= 0) && ((tcp6transp = svctcp_create(mounttcp6sock, 0, 0)) == NULL)) {
737			log(LOG_ERR, "Can't create MOUNT/TCP IPv6 service");
738			close(mounttcp6sock);
739			mounttcp6sock = -1;
740			mounttcp6port = 0;
741		}
742		if (mounttcp6sock >= 0) {
743			svcregcnt = 0;
744			if (!svc_register(tcp6transp, RPCPROG_MNT, 1, mntsrv, 0))
745				log(LOG_ERR, "Can't register IPv6 MOUNT/TCP v1 service");
746			else
747				svcregcnt++;
748			if (!svc_register(tcp6transp, RPCPROG_MNT, 3, mntsrv, 0))
749				log(LOG_ERR, "Can't register IPv6 MOUNT/TCP v3 service");
750			else
751				svcregcnt++;
752			if (!svcregcnt) {
753				svc_destroy(tcp6transp);
754				close(mounttcp6sock);
755				mounttcp6sock = -1;
756				mounttcp6port = 0;
757			}
758		}
759
760	}
761
762	if ((mountudp6sock < 0) && (mounttcp6sock < 0))
763		log(LOG_WARNING, "Can't create MOUNT IPv6 sockets");
764	if ((mountudpsock < 0) && (mounttcpsock < 0))
765		log(LOG_WARNING, "Can't create MOUNT IPv4 sockets");
766	if ((mountudp6sock < 0) && (mounttcp6sock < 0) &&
767	    (mountudpsock < 0) && (mounttcpsock < 0)) {
768		log(LOG_ERR, "Can't create any MOUNT sockets!");
769		exit(1);
770	}
771
772	/* launch mountd pthread */
773	error = pthread_create(&thd, &pattr, mountd_thread, NULL);
774	if (error) {
775		log(LOG_ERR, "mountd pthread_create: %s (%d)", strerror(error), error);
776		exit(1);
777	}
778}
779
780/*
781 * functions for locking/unlocking the exports list (and other export-related globals)
782 */
783void
784lock_exports(void)
785{
786	int error;
787
788	if (checkexports)
789		return;
790
791	error = pthread_mutex_lock(&export_mutex);
792	if (error)
793		log(LOG_ERR, "export mutex lock failed: %s (%d)", strerror(error), error);
794}
795void
796unlock_exports(void)
797{
798	int error;
799
800	if (checkexports)
801		return;
802
803	error = pthread_mutex_unlock(&export_mutex);
804	if (error)
805		log(LOG_ERR, "export mutex unlock failed: %s (%d)", strerror(error), error);
806}
807
808/*
809 * The mount rpc service
810 */
811void
812mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
813{
814	struct expfs *xf;
815	struct fhreturn fhr;
816	struct stat stb;
817	struct statfs fsb;
818	struct nfs_sec secflavs;
819	struct sockaddr *sa;
820	u_short sport;
821	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
822	char addrbuf[2*INET6_ADDRSTRLEN], hostbuf[NI_MAXHOST];
823	int bad = ENOENT, options;
824	u_char uuid[16];
825
826	sa = svc_getcaller_sa(transp);
827	if (sa->sa_family == AF_INET)
828		sport = ntohs(((struct sockaddr_in*) AOK sa)->sin_port);
829	else if (sa->sa_family == AF_INET6)
830		sport = ntohs(((struct sockaddr_in6*) AOK sa)->sin6_port);
831	else
832		sport = 0;
833
834	strlcpy(hostbuf, "unknown_host", sizeof(hostbuf));
835	strlcpy(addrbuf, "unknown_host", sizeof(addrbuf));
836
837	switch (rqstp->rq_proc) {
838	case NULLPROC:
839		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
840			log(LOG_ERR, "Can't send NULL MOUNT reply");
841		return;
842	case RPCMNT_MOUNT:
843		if ((sport >= IPPORT_RESERVED) && config.mount_require_resv_port) {
844			svcerr_weakauth(transp);
845			return;
846		}
847		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
848			svcerr_decode(transp);
849			return;
850		}
851
852		lock_exports();
853
854		/*
855		 * Get the real pathname and make sure it is a directory
856		 * or a regular file if the -r option was specified
857		 * and it exists.
858		 */
859		if (realpath(rpcpath, dirpath) == 0 ||
860		    stat(dirpath, &stb) < 0 ||
861		    (!S_ISDIR(stb.st_mode) &&
862		     (!config.mount_regular_files || !S_ISREG(stb.st_mode))) ||
863		    statfs(dirpath, &fsb) < 0) {
864			unlock_exports();
865			chdir("/");	/* Just in case realpath doesn't */
866			DEBUG(1, "stat failed on %s", dirpath);
867			if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
868				log(LOG_ERR, "Can't send reply for failed mount");
869			if (config.verbose) {
870				getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
871				log(LOG_NOTICE, "Mount failed: %s %s", addrbuf, dirpath);
872			}
873			return;
874		}
875
876		/* get UUID for volume */
877		if (!get_uuid_from_list(&fsb, uuid, UL_CHECK_ALL)) {
878			unlock_exports();
879			DEBUG(1, "no exported volume uuid for %s", dirpath);
880			if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
881				log(LOG_ERR, "Can't send reply for failed mount");
882			if (config.verbose) {
883				getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
884				log(LOG_NOTICE, "Mount failed: %s %s", addrbuf, dirpath);
885			}
886			return;
887		}
888
889		/* Check in the exports list */
890		xf = ex_search(uuid);
891		if (xf && expdir_search(xf, dirpath, sa, &options, &secflavs)) {
892			fhr.fhr_flags = options;
893			fhr.fhr_vers = rqstp->rq_vers;
894			bcopy(&secflavs, &fhr.fhr_sec, sizeof(struct nfs_sec));
895			/* Get the file handle (specifying max fh size based on protocol version */
896			memset(&fhr.fhr_fh, 0, sizeof(fhandle_t));
897			fhr.fhr_fh.fh_len = (fhr.fhr_vers < 3) ? NFSV2_MAX_FH_SIZE : NFSV3_MAX_FH_SIZE;
898			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
899				DEBUG(1, "Can't get fh for %s: %s (%d)", dirpath,
900					strerror(errno), errno);
901				bad = EACCES; /* path must not be exported */
902				if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
903					log(LOG_ERR, "Can't send reply for failed mount");
904				unlock_exports();
905				return;
906			}
907			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, (caddr_t)&fhr))
908				log(LOG_ERR, "Can't send mount reply");
909			if (!getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, 0))
910				add_mlist(hostbuf, dirpath);
911			log(LOG_INFO, "Mount successful: %s %s", hostbuf, dirpath);
912		} else {
913			bad = EACCES;
914			if (config.verbose) {
915				getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
916				log(LOG_NOTICE, "Mount failed: %s %s", addrbuf, dirpath);
917			}
918			if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t)&bad))
919				log(LOG_ERR, "Can't send reply for failed mount");
920		}
921		unlock_exports();
922		return;
923	case RPCMNT_DUMP:
924		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
925			log(LOG_ERR, "Can't send MOUNT dump reply");
926		if (config.verbose >= 3) {
927			getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
928			DEBUG(1, "dump: %s", addrbuf);
929		}
930		return;
931	case RPCMNT_UMOUNT:
932		if ((sport >= IPPORT_RESERVED) && config.mount_require_resv_port) {
933			svcerr_weakauth(transp);
934			return;
935		}
936		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, dirpath)) {
937			svcerr_decode(transp);
938			return;
939		}
940		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
941			log(LOG_ERR, "Can't send UMOUNT reply");
942		if (!getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NAMEREQD))
943			del_mlist(hostbuf, dirpath);
944		else
945			hostbuf[0] = '\0';
946		if (!getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST))
947			del_mlist(addrbuf, dirpath);
948		log(LOG_INFO, "umount: %s %s", hostbuf[0] ? hostbuf : addrbuf, dirpath);
949		return;
950	case RPCMNT_UMNTALL:
951		if ((sport >= IPPORT_RESERVED) && config.mount_require_resv_port) {
952			svcerr_weakauth(transp);
953			return;
954		}
955		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
956			log(LOG_ERR, "Can't send UMNTALL reply");
957		if (!getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, NI_NAMEREQD))
958			del_mlist(hostbuf, NULL);
959		else
960			hostbuf[0] = '\0';
961		if (!getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST))
962			del_mlist(addrbuf, (char *)NULL);
963		log(LOG_INFO, "umount all: %s", hostbuf[0] ? hostbuf : addrbuf);
964		return;
965	case RPCMNT_EXPORT:
966		lock_exports();
967		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
968			log(LOG_ERR, "Can't send EXPORT reply");
969		unlock_exports();
970		if (config.verbose >= 3) {
971			getnameinfo(sa, sa->sa_len, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
972			DEBUG(1, "export: %s", addrbuf);
973		}
974		return;
975	default:
976		svcerr_noproc(transp);
977		return;
978	}
979}
980
981/*
982 * Xdr conversion for a dirpath string
983 */
984int
985xdr_dir(XDR *xdrsp, char *dirp)
986{
987	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
988}
989
990/*
991 * Xdr routine to generate file handle reply
992 */
993int
994xdr_fhs(XDR *xdrsp, caddr_t cp)
995{
996	struct fhreturn *fhrp = (struct fhreturn *) AOK cp;
997	xdr_long_t ok = 0, len, auth;
998	int32_t i;
999
1000	if (!xdr_long(xdrsp, &ok))
1001		return (0);
1002	switch (fhrp->fhr_vers) {
1003	case 1:
1004		return (xdr_opaque(xdrsp, fhrp->fhr_fh.fh_data, NFSX_V2FH));
1005	case 3:
1006		len = fhrp->fhr_fh.fh_len;
1007		if (!xdr_long(xdrsp, &len))
1008			return (0);
1009		if (!xdr_opaque(xdrsp, fhrp->fhr_fh.fh_data, fhrp->fhr_fh.fh_len))
1010			return (0);
1011		/* security flavors */
1012		if (fhrp->fhr_sec.count == 0) {
1013			auth = RPCAUTH_SYS;
1014			len = 1;
1015			if (!xdr_long(xdrsp, &len))
1016				return (0);
1017			return (xdr_long(xdrsp, &auth));
1018		}
1019
1020		len = fhrp->fhr_sec.count;
1021		if (!xdr_long(xdrsp, &len))
1022			return (0);
1023		for (i = 0; i < fhrp->fhr_sec.count; i++) {
1024			auth = (xdr_long_t)fhrp->fhr_sec.flavors[i];
1025			if (!xdr_long(xdrsp, &auth))
1026				return (0);
1027		}
1028		return (TRUE);
1029	};
1030	return (0);
1031}
1032
1033int
1034xdr_mlist(XDR *xdrsp, __unused caddr_t cp)
1035{
1036	struct mountlist *mlp;
1037	int trueval = 1;
1038	int falseval = 0;
1039
1040	mlp = mlhead;
1041	while (mlp) {
1042		if (!xdr_bool(xdrsp, &trueval))
1043			return (0);
1044		if (!xdr_string(xdrsp, &mlp->ml_host, RPCMNT_NAMELEN))
1045			return (0);
1046		if (!xdr_string(xdrsp, &mlp->ml_dir, RPCMNT_PATHLEN))
1047			return (0);
1048		mlp = mlp->ml_next;
1049	}
1050	if (!xdr_bool(xdrsp, &falseval))
1051		return (0);
1052	return (1);
1053}
1054
1055/*
1056 * Xdr conversion for export list
1057 */
1058int
1059xdr_explist(XDR *xdrsp, __unused caddr_t cp)
1060{
1061	struct expfs *xf;
1062	struct expdir *xd;
1063	int falseval = 0;
1064
1065	TAILQ_FOREACH(xf, &xfshead, xf_next) {
1066		TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) {
1067			if (put_exlist(xd, xdrsp))
1068				goto errout;
1069		}
1070	}
1071	if (!xdr_bool(xdrsp, &falseval))
1072		return (0);
1073	return (1);
1074errout:
1075	return (0);
1076}
1077
1078/*
1079 * Called from xdr_explist() to output the mountable exported
1080 * directory paths.
1081 */
1082int
1083put_exlist(struct expdir *xd, XDR *xdrsp)
1084{
1085	struct expdir *mxd;
1086	struct grouplist *grp;
1087	struct host *hp;
1088	int trueval = 1;
1089	int falseval = 0;
1090	char *strp;
1091	char offline_all[] = "<offline>";
1092	char offline_some[] = "<offline*>";
1093	char everyone[] = "(Everyone)";
1094	char abuf[RPCMNT_NAMELEN+1];
1095	int offline = 0, auth = 0, i;
1096
1097	if (!xd)
1098		return (0);
1099
1100	if (!xdr_bool(xdrsp, &trueval))
1101		return (1);
1102	strp = xd->xd_dir;
1103	if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
1104		return (1);
1105	if (xd->xd_iflags & OP_OFFLINE) {
1106		/* report if export is offline for all or some* hosts */
1107		if (!xdr_bool(xdrsp, &trueval))
1108			return (1);
1109		strp = (xd->xd_iflags & OP_ONLINE) ? offline_some : offline_all;
1110		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
1111			return (1);
1112		offline = 1;
1113	}
1114	if ((xd->xd_ssec.count > 1) || (xd->xd_ssec.flavors[0] != RPCAUTH_SYS)) {
1115		/* report non-default auth flavors */
1116		if (!xdr_bool(xdrsp, &trueval))
1117			return (1);
1118		abuf[0] = '\0';
1119		strlcpy(abuf, "<", sizeof(abuf));
1120		for (i=0; i < xd->xd_ssec.count; i++) {
1121			if (xd->xd_ssec.flavors[i] == RPCAUTH_SYS)
1122				strlcat(abuf, "sys", sizeof(abuf));
1123			else if (xd->xd_ssec.flavors[i] == RPCAUTH_KRB5)
1124				strlcat(abuf, "krb5", sizeof(abuf));
1125			else if (xd->xd_ssec.flavors[i] == RPCAUTH_KRB5I)
1126				strlcat(abuf, "krb5i", sizeof(abuf));
1127			else if (xd->xd_ssec.flavors[i] == RPCAUTH_KRB5P)
1128				strlcat(abuf, "krb5p", sizeof(abuf));
1129			else
1130				continue;
1131			if (i < xd->xd_ssec.count-1)
1132				strlcat(abuf, ":", sizeof(abuf));
1133		}
1134		strlcat(abuf, ">", sizeof(abuf));
1135		strp = abuf;
1136		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
1137			return (1);
1138		auth = 1;
1139	}
1140	if (!(xd->xd_flags & OP_DEFEXP)) {
1141		TAILQ_FOREACH(hp, &xd->xd_hosts, ht_next) {
1142			if (!(hp->ht_flags & OP_SHOW))
1143				continue;
1144			grp = hp->ht_grp;
1145			switch (grp->gr_type) {
1146			case GT_HOST:
1147			case GT_NET:
1148			case GT_NETGROUP:
1149				if (!xdr_bool(xdrsp, &trueval))
1150					return (1);
1151				strp = grp_name(grp);
1152				if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
1153					return (1);
1154			}
1155		}
1156	} else if (offline || auth) {
1157		if (!xdr_bool(xdrsp, &trueval))
1158			return (1);
1159		strp = everyone;
1160		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
1161			return (1);
1162	}
1163	if (!xdr_bool(xdrsp, &falseval))
1164		return (1);
1165
1166	TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) {
1167		if (put_exlist(mxd, xdrsp))
1168			return (1);
1169	}
1170
1171	return (0);
1172}
1173
1174
1175/*
1176 * Clean up a pathname.  Removes quotes around quoted strings,
1177 * strips escaped characters, removes trailing slashes.
1178 */
1179char *
1180clean_pathname(char *line)
1181{
1182	int len, esc;
1183	char c, *p, *s;
1184
1185	if (line == NULL)
1186		return NULL;
1187	len = strlen(line);
1188	s = malloc(len + 1);
1189	if (s == NULL)
1190		return NULL;
1191
1192	len = 0;
1193	esc = 0;
1194	c = '\0';
1195
1196	p = line;
1197
1198	if (*p == '\'' || *p == '"') {
1199		c = *p;
1200		p++;
1201	}
1202
1203	for (;*p != '\0'; p++) {
1204		if (esc == 1) {
1205			s[len++] = *p;
1206			esc = 0;
1207		} else if (*p == c)
1208			break;
1209		else if (*p == '\\')
1210			esc = 1;
1211		else if (c == '\0' && (*p == ' ' || *p == '\t'))
1212			break;
1213		else s[len++] = *p;
1214	}
1215
1216	/* strip trailing slashes */
1217	for (; len > 1 && s[len-1] == '/'; len--)
1218		;
1219
1220	s[len] = '\0';
1221
1222	return (s);
1223}
1224
1225
1226/*
1227 * Query DiskArb for a volume's UUID
1228 */
1229int
1230get_uuid_from_diskarb(const char *path, u_char *uuid)
1231{
1232	DASessionRef session;
1233	DADiskRef disk;
1234	CFDictionaryRef dd;
1235	CFTypeRef val;
1236	CFUUIDBytes uuidbytes;
1237	int rv = 1;
1238
1239	session = NULL;
1240	disk = NULL;
1241	dd = NULL;
1242
1243	session = DASessionCreate(NULL);
1244	if (!session) {
1245		log(LOG_ERR, "can't create DiskArb session");
1246		rv = 0;
1247		goto out;
1248	}
1249	disk = DADiskCreateFromBSDName(NULL, session, path);
1250	if (!disk) {
1251		DEBUG(1, "DADiskCreateFromBSDName(%s) failed", path);
1252		rv = 0;
1253		goto out;
1254	}
1255	dd = DADiskCopyDescription(disk);
1256	if (!dd) {
1257		DEBUG(1, "DADiskCopyDescription(%s) failed", path);
1258		rv = 0;
1259		goto out;
1260	}
1261
1262	if (!CFDictionaryGetValueIfPresent(dd, (kDADiskDescriptionVolumeUUIDKey), &val)) {
1263		DEBUG(1, "unable to get UUID for volume %s", path);
1264		rv = 0;
1265		goto out;
1266	}
1267	uuidbytes = CFUUIDGetUUIDBytes(val);
1268	bcopy(&uuidbytes, uuid, sizeof(uuidbytes));
1269
1270out:
1271	if (session) CFRelease(session);
1272	if (disk) CFRelease(disk);
1273	if (dd) CFRelease(dd);
1274	return (rv);
1275}
1276
1277/*
1278 * find the UUID for this volume in the UUID list
1279 */
1280struct uuidlist *
1281get_uuid_from_list(const struct statfs *fsb, u_char *uuid, const int flags)
1282{
1283	struct uuidlist *ulp;
1284
1285	if (!(flags & UL_CHECK_ALL))
1286		return (NULL);
1287
1288	TAILQ_FOREACH(ulp, &ulhead, ul_list) {
1289		if ((flags & UL_CHECK_MNTFROM) &&
1290		    strcmp(fsb->f_mntfromname, ulp->ul_mntfromname))
1291			continue;
1292		if ((flags & UL_CHECK_MNTON) &&
1293		    strcmp(fsb->f_mntonname, ulp->ul_mntonname))
1294			continue;
1295		if (uuid)
1296			bcopy(&ulp->ul_uuid, uuid, sizeof(ulp->ul_uuid));
1297		break;
1298	}
1299	return (ulp);
1300}
1301
1302/*
1303 * find UUID list entry with the given UUID
1304 */
1305struct uuidlist *
1306find_uuid(u_char *uuid)
1307{
1308	struct uuidlist *ulp;
1309
1310	TAILQ_FOREACH(ulp, &ulhead, ul_list) {
1311		if (!bcmp(ulp->ul_uuid, uuid, sizeof(ulp->ul_uuid)))
1312			break;
1313	}
1314	return (ulp);
1315}
1316
1317/*
1318 * find UUID list entry with the given FSID
1319 */
1320struct uuidlist *
1321find_uuid_by_fsid(u_int32_t fsid)
1322{
1323	struct uuidlist *ulp;
1324
1325	TAILQ_FOREACH(ulp, &ulhead, ul_list) {
1326		if (ulp->ul_fsid == fsid)
1327			break;
1328	}
1329	return (ulp);
1330}
1331
1332/*
1333 * add a UUID to the UUID list
1334 */
1335struct uuidlist *
1336add_uuid_to_list(const struct statfs *fsb, u_char *dauuid, u_char *uuid)
1337{
1338	struct uuidlist *ulpnew;
1339	u_int32_t xfsid;
1340
1341	ulpnew = malloc(sizeof(struct uuidlist));
1342	if (!ulpnew) {
1343		log(LOG_ERR, "add_uuid_to_list: out of memory");
1344		return (NULL);
1345	}
1346	bzero(ulpnew, sizeof(*ulpnew));
1347	LIST_INIT(&ulpnew->ul_exportids);
1348	if (dauuid) {
1349		bcopy(dauuid, ulpnew->ul_dauuid, sizeof(ulpnew->ul_dauuid));
1350		ulpnew->ul_davalid = 1;
1351	}
1352	bcopy(uuid, ulpnew->ul_uuid, sizeof(ulpnew->ul_uuid));
1353	strlcpy(ulpnew->ul_mntfromname, fsb->f_mntfromname, sizeof(ulpnew->ul_mntfromname));
1354	strlcpy(ulpnew->ul_mntonname, fsb->f_mntonname, sizeof(ulpnew->ul_mntonname));
1355
1356	/* make sure exported FS ID is unique */
1357	xfsid = UUID2FSID(uuid);
1358	ulpnew->ul_fsid = xfsid;
1359	while (find_uuid_by_fsid(ulpnew->ul_fsid))
1360		if (++ulpnew->ul_fsid == xfsid) {
1361			/* exhausted exported FS ID values! */
1362			log(LOG_ERR, "exported FS ID values exhausted, can't add %s",
1363				ulpnew->ul_mntonname);
1364			free(ulpnew);
1365			return (NULL);
1366		}
1367
1368	TAILQ_INSERT_TAIL(&ulhead, ulpnew, ul_list);
1369	return (ulpnew);
1370}
1371
1372/*
1373 * get the UUID to use for this volume's file handles
1374 * and add it to the UUID list if it isn't there yet.
1375 */
1376struct uuidlist *
1377get_uuid(const struct statfs *fsb, u_char *uuid)
1378{
1379	CFUUIDRef cfuuid;
1380	CFUUIDBytes uuidbytes;
1381	struct uuidlist *ulp;
1382	u_char dauuid[16];
1383	int davalid, uuidchanged, reportuuid = 0;
1384	char buf[64], buf2[64];
1385
1386	/* get DiskArb's idea of the UUID (if any) */
1387	davalid = get_uuid_from_diskarb(fsb->f_mntfromname, dauuid);
1388
1389	if (davalid) {
1390		DEBUG(2, "get_uuid: %s %s DiskArb says: %s",
1391			fsb->f_mntfromname, fsb->f_mntonname,
1392			uuidstring(dauuid, buf));
1393	}
1394
1395	/* try to get UUID out of UUID list */
1396	if ((ulp = get_uuid_from_list(fsb, uuid, UL_CHECK_MNTON))) {
1397		DEBUG(2, "get_uuid: %s %s found: %s",
1398			fsb->f_mntfromname, fsb->f_mntonname,
1399			uuidstring(uuid, buf));
1400		/*
1401		 * Check against any DiskArb UUID.
1402		 * If diskarb UUID is different then drop the uuid entry.
1403		 */
1404		if (davalid) {
1405			if (!ulp->ul_davalid)
1406				uuidchanged = 1;
1407			else if (bcmp(ulp->ul_dauuid, dauuid, sizeof(dauuid)))
1408				uuidchanged = 1;
1409			else
1410				uuidchanged = 0;
1411		} else {
1412			if (ulp->ul_davalid) {
1413				/*
1414				 * We had a UUID before, but now we don't?
1415				 * Assume this is just a transient error,
1416				 * issue a warning, and stick with the old UUID.
1417				 */
1418				uuidstring(ulp->ul_dauuid, buf);
1419				log(LOG_WARNING, "lost UUID for %s, was %s, keeping old UUID",
1420					fsb->f_mntonname, buf);
1421				uuidchanged = 0;
1422			} else
1423				uuidchanged = 0;
1424		}
1425		if (uuidchanged) {
1426			uuidstring(ulp->ul_dauuid, buf);
1427			if (davalid)
1428				uuidstring(dauuid, buf2);
1429			else
1430				strlcpy(buf2, "------------------------------------", sizeof(buf2));
1431			if (ulp->ul_exported) {
1432				/*
1433				 * Woah!  We already have this file system exported with
1434				 * a different UUID (UUID changed while processing the
1435				 * exports list).  Ignore the UUID change for now so that
1436				 * all the exports for this file system will be registered
1437				 * using the same UUID/FSID.
1438				 *
1439				 * XXX Should we do something like set gothup=1 so that
1440				 * we will reregister all the exports (with the new UUID)?
1441				 * If so, what's to prevent an infinite loop if we always
1442				 * seem to be hitting this problem?
1443				 */
1444				log(LOG_WARNING, "ignoring UUID change for already exported file system %s, was %s now %s",
1445					fsb->f_mntonname, buf, buf2);
1446				uuidchanged = 0;
1447			}
1448		}
1449		if (uuidchanged) {
1450			log(LOG_WARNING, "UUID changed for %s, was %s now %s",
1451				fsb->f_mntonname, buf, buf2);
1452			bcopy(dauuid, uuid, sizeof(dauuid));
1453			/* remove old UUID from list */
1454			TAILQ_REMOVE(&ulhead, ulp, ul_list);
1455			free(ulp);
1456			ulp = NULL;
1457		} else {
1458			ulp->ul_exported = 1;
1459		}
1460	} else if (davalid) {
1461		/*
1462		 * The UUID wasn't in the list, but DiskArb has a UUID for it.
1463		 * (If the DiskArb UUID conflicts with something already in the
1464		 * list, we'll need to create a new UUID for it below.)
1465		 */
1466		bcopy(dauuid, uuid, sizeof(dauuid));
1467	} else {
1468		/*
1469		 * We need to create a UUID to use for this volume.
1470		 * This is because it wasn't already in the list, and
1471		 * either DiskArb didn't have a UUID for the volume or
1472		 * the UUID DiskArb has for the volume conflicts with
1473		 * a UUID for a volume already in the list.
1474		 */
1475		reportuuid = 1;
1476		cfuuid = CFUUIDCreate(NULL);
1477		uuidbytes = CFUUIDGetUUIDBytes(cfuuid);
1478		bcopy(&uuidbytes, uuid, sizeof(uuidbytes));
1479		CFRelease(cfuuid);
1480	}
1481
1482	if (!ulp) {
1483		/*
1484		 * Add the UUID to the list, but make sure it is unique first.
1485		 */
1486		while ((ulp = find_uuid(uuid))) {
1487			reportuuid = 1;
1488			uuidstring(uuid, buf);
1489			log(LOG_WARNING, "%s UUID conflict with %s, %s",
1490				fsb->f_mntonname, ulp->ul_mntonname, buf);
1491			cfuuid = CFUUIDCreate(NULL);
1492			uuidbytes = CFUUIDGetUUIDBytes(cfuuid);
1493			bcopy(&uuidbytes, uuid, sizeof(uuidbytes));
1494			CFRelease(cfuuid);
1495			/* double check that the UUID is unique */
1496		}
1497		ulp = add_uuid_to_list(fsb, (davalid ? dauuid : NULL), uuid);
1498		if (!ulp) {
1499			log(LOG_ERR, "error adding %s", fsb->f_mntonname);
1500		} else {
1501			ulp->ul_exported = 1;
1502		}
1503	} else if (!ulp->ul_mntfromname[0]) {
1504		/*
1505		 * If the volume didn't exist when mountd read the
1506		 * mountdexptab, it's possible this ulp doesn't
1507		 * have a copy of it's mntfromname.  So, we make
1508		 * sure to grab a copy here before the volume gets
1509		 * exported.
1510		 */
1511		strlcpy(ulp->ul_mntfromname, fsb->f_mntfromname, sizeof(ulp->ul_mntfromname));
1512	}
1513
1514	if (reportuuid)
1515		log(LOG_WARNING, "%s using UUID %s", fsb->f_mntonname, uuidstring(uuid, buf));
1516	else
1517		DEBUG(1, "%s using UUID %s", fsb->f_mntonname, uuidstring(uuid, buf));
1518
1519	return (ulp);
1520}
1521
1522/*
1523 * clear export flags on all UUID entries
1524 */
1525void
1526uuidlist_clearexport(void)
1527{
1528	struct uuidlist *ulp;
1529
1530	TAILQ_FOREACH(ulp, &ulhead, ul_list) {
1531		ulp->ul_exported = 0;
1532	}
1533}
1534
1535/* convert UUID bytes to UUID string */
1536#define HEXTOC(c) \
1537	((c) >= 'a' ? ((c) - ('a' - 10)) : \
1538	((c) >= 'A' ? ((c) - ('A' - 10)) : ((c) - '0')))
1539#define HEXSTRTOI(p) \
1540	((HEXTOC(p[0]) << 4) + HEXTOC(p[1]))
1541char *
1542uuidstring(u_char *uuid, char *string)
1543{
1544	snprintf(string, (16*2)+4+1, /* XXX silly, yes. But at least we're not using sprintf. [sigh] */
1545		"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
1546		uuid[0] & 0xff, uuid[1] & 0xff, uuid[2] & 0xff, uuid[3] & 0xff,
1547		uuid[4] & 0xff, uuid[5] & 0xff,
1548		uuid[6] & 0xff, uuid[7] & 0xff,
1549		uuid[8] & 0xff, uuid[9] & 0xff,
1550		uuid[10] & 0xff, uuid[11] & 0xff, uuid[12] & 0xff,
1551		uuid[13] & 0xff, uuid[14] & 0xff, uuid[15] & 0xff);
1552	return (string);
1553}
1554
1555/*
1556 * save the exported volume UUID list to the mountdexptab file
1557 *
1558 * We have the option of saving all UUIDs in the list, or just
1559 * saving the ones that are currently exported.  However, if we
1560 * have a volume exported, then removed from the export list, and
1561 * then added back to the export list, it may be expected that the
1562 * file handles/UUIDs will be the same.  But if we don't save what
1563 * the UUIDs were before, we risk the chance of using a different
1564 * UUID for the second export.  This can happen if the volume's
1565 * DiskArb UUID is not used for export (because DiskArb doesn't have
1566 * a UUID for it, or because there was a UUID conflict and we needed
1567 * to use a different UUID).
1568 */
1569void
1570uuidlist_save(void)
1571{
1572	FILE *ulfile;
1573	struct uuidlist *ulp;
1574	struct expidlist *xid;
1575	char buf[64], buf2[64];
1576
1577	if ((ulfile = fopen(_PATH_MOUNTEXPLIST, "w")) == NULL) {
1578		log(LOG_ERR, "Can't write %s: %s (%d)", _PATH_MOUNTEXPLIST,
1579			strerror(errno), errno);
1580		return;
1581	}
1582	TAILQ_FOREACH(ulp, &ulhead, ul_list) {
1583#ifdef SAVE_ONLY_EXPORTED_UUIDS
1584		if (!ulp->ul_exported)
1585			continue;
1586#endif
1587		if (ulp->ul_davalid)
1588			uuidstring(ulp->ul_dauuid, buf);
1589		else
1590			strlcpy(buf, "------------------------------------", sizeof(buf));
1591		uuidstring(ulp->ul_uuid, buf2);
1592		fprintf(ulfile, "%s %s 0x%08X %s\n", buf, buf2, ulp->ul_fsid, ulp->ul_mntonname);
1593		LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) {
1594			fprintf(ulfile, "XID 0x%08X %s\n", xid->xid_id,
1595				((xid->xid_path[0] == '\0') ? "." : xid->xid_path));
1596		}
1597	}
1598	fclose(ulfile);
1599}
1600
1601/*
1602 * read in the exported volume UUID list from the mountdexptab file
1603 */
1604void
1605uuidlist_restore(void)
1606{
1607	struct uuidlist *ulp;
1608	struct expidlist *xid;
1609	char *cp, str[2*MAXPATHLEN];
1610	FILE *ulfile;
1611	int i, slen, davalid, uuidchanged;
1612	uint32_t linenum;
1613	struct statfs fsb;
1614	u_char dauuid[16];
1615	char buf[64], buf2[64];
1616
1617	if ((ulfile = fopen(_PATH_MOUNTEXPLIST, "r")) == NULL) {
1618		if (errno != ENOENT)
1619			log(LOG_WARNING, "Can't open %s: %s (%d)", _PATH_MOUNTEXPLIST,
1620				strerror(errno), errno);
1621		else
1622			DEBUG(1, "Can't open %s, %s (%d)", _PATH_MOUNTEXPLIST,
1623				strerror(errno), errno);
1624		return;
1625	}
1626	ulp = NULL;
1627	linenum = 0;
1628	while (fgets(str, 2*MAXPATHLEN, ulfile) != NULL) {
1629		linenum++;
1630		slen = strlen(str);
1631		if (str[slen-1] == '\n')
1632			str[slen-1] = '\0';
1633		if (!strncmp(str, "XID ", 4)) {
1634			/* we have an export ID line for the current UUID */
1635			if (!ulp) {
1636				log(LOG_ERR, "ignoring orphaned export ID at line %d of %s",
1637					linenum, _PATH_MOUNTEXPLIST);
1638				continue;
1639			}
1640			/* parse XID and add to current UUID */
1641			xid = malloc(sizeof(*xid));
1642			if (xid == NULL) {
1643				log(LOG_ERR, "uuidlist_restore: Out of memory");
1644				exit(2);
1645			}
1646			cp = str + 4;
1647			slen -= 4;
1648			if (sscanf(cp, "%i", &xid->xid_id) != 1) {
1649				log(LOG_ERR, "invalid export ID at line %d of %s",
1650					linenum, _PATH_MOUNTEXPLIST);
1651				free(xid);
1652				continue;
1653			}
1654			while (*cp && (*cp != ' ')) {
1655				cp++;
1656				slen--;
1657			}
1658			cp++;
1659			slen--;
1660			if (slen >= (int)sizeof(xid->xid_path)) {
1661				log(LOG_ERR, "export ID path too long at line %d of %s",
1662					linenum, _PATH_MOUNTEXPLIST);
1663				free(xid);
1664				continue;
1665			}
1666			if ((cp[0] == '.') && (cp[1] == '\0'))
1667				xid->xid_path[0] = '\0';
1668			else
1669				strlcpy(xid->xid_path, cp, sizeof(xid->xid_path));
1670			LIST_INSERT_HEAD(&ulp->ul_exportids, xid, xid_list);
1671			continue;
1672		}
1673		ulp = malloc(sizeof(*ulp));
1674		if (ulp == NULL) {
1675			log(LOG_ERR, "uuidlist_restore: Out of memory");
1676			exit(2);
1677		}
1678		bzero(ulp, sizeof(*ulp));
1679		LIST_INIT(&ulp->ul_exportids);
1680		cp = str;
1681		if (*cp == '-') {
1682			/* DiskArb UUID not present */
1683			ulp->ul_davalid = 0;
1684			bzero(ulp->ul_dauuid, sizeof(ulp->ul_dauuid));
1685			while (*cp && (*cp != ' '))
1686				cp++;
1687		} else {
1688			ulp->ul_davalid = 1;
1689			for (i=0; i < (int)sizeof(ulp->ul_dauuid); i++, cp+=2) {
1690				if (*cp == '-')
1691					cp++;
1692				if (!isxdigit(*cp) || !isxdigit(*(cp+1))) {
1693					log(LOG_ERR, "invalid UUID at line %d of %s",
1694						linenum, _PATH_MOUNTEXPLIST);
1695					free(ulp);
1696					ulp = NULL;
1697					break;
1698				}
1699				ulp->ul_dauuid[i] = HEXSTRTOI(cp);
1700			}
1701		}
1702		if (ulp == NULL)
1703			continue;
1704		cp++;
1705		for (i=0; i < (int)sizeof(ulp->ul_uuid); i++, cp+=2) {
1706			if (*cp == '-')
1707				cp++;
1708			if (!isxdigit(*cp) || !isxdigit(*(cp+1))) {
1709				log(LOG_ERR, "invalid UUID at line %d of %s",
1710					linenum, _PATH_MOUNTEXPLIST);
1711				free(ulp);
1712				ulp = NULL;
1713				break;
1714			}
1715			ulp->ul_uuid[i] = HEXSTRTOI(cp);
1716		}
1717		if (ulp == NULL)
1718			continue;
1719		if (*cp != ' ') {
1720			log(LOG_ERR, "invalid entry at line %d of %s",
1721				linenum, _PATH_MOUNTEXPLIST);
1722			free(ulp);
1723			continue;
1724		}
1725		cp++;
1726		if (sscanf(cp, "%i", &ulp->ul_fsid) != 1) {
1727			log(LOG_ERR, "invalid entry at line %d of %s",
1728				linenum, _PATH_MOUNTEXPLIST);
1729			free(ulp);
1730			continue;
1731		}
1732		while (*cp && (*cp != ' '))
1733			cp++;
1734		if (*cp != ' ') {
1735			log(LOG_ERR, "invalid entry at line %d of %s",
1736				linenum, _PATH_MOUNTEXPLIST);
1737			free(ulp);
1738			continue;
1739		}
1740		cp++;
1741		strncpy(ulp->ul_mntonname, cp, MAXPATHLEN);
1742		ulp->ul_mntonname[MAXPATHLEN-1] = '\0';
1743
1744		/* verify the path exists and that it is a mount point */
1745		if (!check_dirpath(ulp->ul_mntonname) ||
1746		    (statfs(ulp->ul_mntonname, &fsb) < 0) ||
1747		    strcmp(ulp->ul_mntonname, fsb.f_mntonname)) {
1748			/* don't drop the UUID record if the volume isn't currently mounted! */
1749			/* If it's mounted/exported later, we want to use the same record. */
1750			DEBUG(1, "export entry for non-existent file system %s at line %d of %s",
1751				ulp->ul_mntonname, linenum, _PATH_MOUNTEXPLIST);
1752			ulp->ul_mntfromname[0] = '\0';
1753			TAILQ_INSERT_TAIL(&ulhead, ulp, ul_list);
1754			continue;
1755		}
1756
1757		/* grab the path's mntfromname */
1758		strncpy(ulp->ul_mntfromname, fsb.f_mntfromname, MAXPATHLEN);
1759		ulp->ul_mntfromname[MAXPATHLEN-1] = '\0';
1760
1761		/*
1762		 * Grab DiskArb's UUID for this volume (if any) and
1763		 * see if it has changed.
1764		 */
1765		davalid = get_uuid_from_diskarb(ulp->ul_mntfromname, dauuid);
1766		if (davalid) {
1767			if (!ulp->ul_davalid)
1768				uuidchanged = 1;
1769			else if (bcmp(ulp->ul_dauuid, dauuid, sizeof(dauuid)))
1770				uuidchanged = 1;
1771			else
1772				uuidchanged = 0;
1773		} else {
1774			if (ulp->ul_davalid) {
1775				/*
1776				 * We had a UUID before, but now we don't?
1777				 * Assume this is just a transient error,
1778				 * issue a warning, and stick with the old UUID.
1779				 */
1780				uuidstring(ulp->ul_dauuid, buf);
1781				log(LOG_WARNING, "lost UUID for %s, was %s, keeping old UUID",
1782					fsb.f_mntonname, buf);
1783				uuidchanged = 0;
1784			} else
1785				uuidchanged = 0;
1786		}
1787		if (uuidchanged) {
1788			/* The UUID changed, so we'll drop any entry */
1789			uuidstring(ulp->ul_dauuid, buf);
1790			if (davalid)
1791				uuidstring(dauuid, buf2);
1792			else
1793				strlcpy(buf2, "------------------------------------", sizeof(buf2));
1794			log(LOG_WARNING, "UUID changed for %s, was %s now %s",
1795				ulp->ul_mntonname, buf, buf2);
1796			free(ulp);
1797			continue;
1798		}
1799
1800		TAILQ_INSERT_TAIL(&ulhead, ulp, ul_list);
1801	}
1802	fclose(ulfile);
1803}
1804
1805struct expidlist *
1806find_export_id(struct uuidlist *ulp, u_int32_t id)
1807{
1808	struct expidlist *xid;
1809
1810	LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) {
1811		if (xid->xid_id == id)
1812			break;
1813	}
1814
1815	return (xid);
1816}
1817
1818struct expidlist *
1819get_export_id(struct uuidlist *ulp, char *path)
1820{
1821	struct expidlist *xid;
1822	u_int32_t maxid = 0;
1823
1824	LIST_FOREACH(xid, &ulp->ul_exportids, xid_list) {
1825		if (!strcmp(xid->xid_path, path))
1826			break;
1827		if (maxid < xid->xid_id)
1828			maxid = xid->xid_id;
1829	}
1830	if (xid)
1831		return (xid);
1832	/* add it */
1833	xid = malloc(sizeof(*xid));
1834	if (!xid) {
1835		log(LOG_ERR, "get_export_id: out of memory");
1836		return (NULL);
1837	}
1838	bzero(xid, sizeof(*xid));
1839	strlcpy(xid->xid_path, path, sizeof(xid->xid_path));
1840	xid->xid_id = maxid + 1;
1841	while (find_export_id(ulp, xid->xid_id)) {
1842		xid->xid_id++;
1843		if (xid->xid_id == maxid) {
1844			/* exhausted export id values! */
1845			log(LOG_ERR, "export ID values exhausted for %s",
1846				ulp->ul_mntonname);
1847			free(xid);
1848			return (NULL);
1849		}
1850	}
1851	LIST_INSERT_HEAD(&ulp->ul_exportids, xid, xid_list);
1852	return (xid);
1853}
1854
1855/*
1856 * find_exported_fs_by_path_and_uuid()
1857 *
1858 * Given a path and a uuid, find the exported file system that
1859 * best matches both.
1860 */
1861struct uuidlist *
1862find_exported_fs_by_path_and_uuid(char *fspath, u_char *fsuuid)
1863{
1864	struct uuidlist *ulp;
1865
1866	ulp = TAILQ_FIRST(&ulhead);
1867	while (ulp) {
1868		/* find next matching uuid */
1869		while (ulp && fsuuid) {
1870			if (ulp->ul_davalid && !bcmp(ulp->ul_dauuid, fsuuid, sizeof(ulp->ul_dauuid)))
1871				break;
1872			if (!bcmp(ulp->ul_uuid, fsuuid, sizeof(ulp->ul_uuid)))
1873				break;
1874			ulp = TAILQ_NEXT(ulp, ul_list);
1875		}
1876		if (!ulp)
1877			break;
1878		/* we're done if fspath ommitted or matches */
1879		if (!fspath || !strcmp(ulp->ul_mntonname, fspath))
1880			break;
1881		ulp = TAILQ_NEXT(ulp, ul_list);
1882	}
1883
1884	return (ulp);
1885}
1886
1887/*
1888 * find_exported_fs_by_dirlist()
1889 *
1890 * Given a list of directories, find the common parent directory and
1891 * the exported file system that directory should be located on.
1892 */
1893struct uuidlist *
1894find_exported_fs_by_dirlist(struct dirlist *dirhead)
1895{
1896	struct dirlist *dirl;
1897	char *path, *p;
1898	int cmp, bestlen, len;
1899	struct uuidlist *ulp, *bestulp;
1900
1901	if (!dirhead)
1902		return (NULL);
1903
1904	path = strdup(dirhead->dl_dir);
1905
1906	dirl = dirhead->dl_next;
1907	while (dirl) {
1908		cmp = subdir_check(path, dirl->dl_dir);
1909		if (cmp >= 0) {
1910			/* same or subdir, so skip */
1911			dirl = dirl->dl_next;
1912			continue;
1913		}
1914		p = strrchr(path, '/');
1915		if (p == path) {
1916			/* hit root */
1917			p[1] = '\0';
1918			break;
1919		}
1920		p[0] = '\0';
1921	}
1922	DEBUG(4, "find_exported_fs: %s", path);
1923
1924	/*
1925	 * Now search uuid list for best match.
1926	 * We're looking for the longest mntonname that this path matches.
1927	 */
1928	bestulp = NULL;
1929	bestlen = -1;
1930	TAILQ_FOREACH(ulp, &ulhead, ul_list) {
1931		if (subdir_check(ulp->ul_mntonname, path) < 0)
1932			continue;
1933		len = strlen(ulp->ul_mntonname);
1934		if (len > bestlen) {
1935			bestulp = ulp;
1936			bestlen = strlen(ulp->ul_mntonname);
1937		}
1938	}
1939
1940	free(path);
1941	DEBUG(4, "find_exported_fs: best exported fs: %s", bestulp ? bestulp->ul_mntonname : "<none>");
1942	return (bestulp);
1943}
1944
1945/*
1946 * subdir_check()
1947 *
1948 * Compares two pathname strings to see if one is a subdir of the other.
1949 * Returns:
1950 * 1   if second path is a subdir of the first path
1951 * 0   if the paths are the same
1952 * -1  if the paths have just a substring match
1953 * -2  if the paths do not match at all
1954 * -3  if second path could not be a subdir of the first path (due to length)
1955 */
1956int
1957subdir_check(char *s1, char *s2)
1958{
1959	int len1, len2, rv;
1960	len1 = strlen(s1);
1961	len2 = strlen(s2);
1962	if (len1 > len2)
1963		rv = -3;
1964	else if (strncmp(s1, s2, len1))
1965		rv = -2;
1966	else if (len1 == len2)
1967		rv = 0;
1968	else if ((s2[len1] == '/') || (len1 == 1))
1969		rv = 1;
1970	else
1971		rv = -1;
1972	DEBUG(4, "subdir_check: %s %s %d", s1, s2, rv);
1973	return rv;
1974}
1975
1976char *line = NULL;
1977uint32_t linesize = 0;
1978FILE *exp_file;
1979uint32_t linenum, entrylines;
1980
1981/*
1982 * Get the export list
1983 */
1984int
1985get_exportlist(void)
1986{
1987	struct expfs *xf, *xf2, *xf3;
1988	struct grouplist ngrp, *grp, *tgrp, tmpgrp;
1989	struct expdir *xd, *xd2, *xd3;
1990	struct expidlist *xid;
1991	struct dirlist *dirhead, *dirl, *dirl2;
1992	struct namelisttqh names, netgroups;
1993	struct namelist *nl;
1994	struct host *ht, *ht2, *ht3;
1995	struct hosttqh htfree;
1996	struct statfs fsb;
1997	struct xucred anon;
1998	struct nfs_sec secflavs;
1999	char *cp, *endcp, *name, *word, *hst, *usr, *dom, savedc, *savedcp, *subdir, *mntonname, *word2;
2000	int len, dlen, hostcount, badhostcount, exflags, got_nondir, netgrp, cmp, show;
2001	char fspath[MAXPATHLEN];
2002	u_char uuid[16], fsuuid[16];
2003	struct uuidlist *ulp, *bestulp;
2004	char buf[64], buf2[64];
2005	int error, opt_flags, need_export, saved_errors;
2006
2007	lock_exports();
2008
2009	/*
2010	 * First, tag all existing export/group structures for deletion
2011	 */
2012	TAILQ_FOREACH(xf, &xfshead, xf_next) {
2013		TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) {
2014			xd->xd_iflags &= ~(OP_ADD|OP_OFFLINE|OP_ONLINE);
2015			xd->xd_ssec.count = 0;
2016			xd->xd_oflags = xd->xd_flags;
2017			xd->xd_ocred = xd->xd_cred;
2018			xd->xd_osec = xd->xd_sec;
2019			xd->xd_flags |= OP_DEL;
2020			TAILQ_FOREACH(ht, &xd->xd_hosts, ht_next)
2021				ht->ht_flags |= OP_DEL;
2022			TAILQ_FOREACH(xd2, &xd->xd_mountdirs, xd_next) {
2023				xd2->xd_flags |= OP_DEL;
2024				TAILQ_FOREACH(ht, &xd2->xd_hosts, ht_next)
2025					ht->ht_flags |= OP_DEL;
2026			}
2027		}
2028	}
2029	uuidlist_clearexport();
2030
2031	if (xpaths) {
2032		free_dirlist(xpaths);
2033		xpaths = NULL;
2034	}
2035	xpaths_complete = 1;
2036
2037	TAILQ_INIT(&names);
2038	TAILQ_INIT(&netgroups);
2039	hostnamecount = hostnamegoodcount = 0;
2040	missingexportcount = 0;
2041	export_errors = 0;
2042	linenum = 0;
2043
2044	if ((exp_file = fopen(exportsfilepath, "r")) == NULL) {
2045		log(LOG_WARNING, "Can't open %s", exportsfilepath);
2046		export_errors = 1;
2047		goto exports_read;
2048	}
2049
2050	/*
2051	 * Read in the exports and build the list, calling nfssvc(NFSSVC_EXPORT)
2052	 * as we go along to push the NEW export rules into the kernel.
2053	 */
2054
2055	dirhead = NULL;
2056	while (get_export_entry()) {
2057		/*
2058		 * Create new exports list entry
2059		 */
2060		DEBUG(1, "---> Got line: %s", line);
2061
2062		/*
2063		 * Set defaults.
2064		 */
2065		saved_errors = export_errors;
2066		hostcount = badhostcount = 0;
2067		anon = def_anon;
2068		exflags = 0;
2069		opt_flags = 0;
2070		got_nondir = 0;
2071		xf = NULL;
2072		ulp = NULL;
2073		fspath[0] = '\0';
2074		bzero(fsuuid, sizeof(fsuuid));
2075
2076		/* init group list and net group */
2077		tgrp = NULL;
2078		bzero(&ngrp, sizeof(ngrp));
2079
2080		/* init default security flavor */
2081		bzero(&secflavs, sizeof(secflavs));
2082		secflavs.flavors[0] = RPCAUTH_SYS;
2083		secflavs.count = 1;
2084
2085		/*
2086		 * process all the fields in this line
2087		 * (we loop until nextfield finds nothing)
2088		 */
2089		cp = line;
2090		nextfield(&cp, &endcp);
2091		if (*cp == '#')
2092			goto nextline;
2093
2094		while (endcp > cp) {
2095			DEBUG(2, "got field: %.*s", endcp-cp, cp);
2096			if (*cp == '-') {
2097				/*
2098				 * looks like we have some options
2099				 */
2100				if (dirhead == NULL) {
2101					export_error(LOG_ERR, "got options with no exported directory: %s", cp);
2102					export_error_cleanup(xf);
2103					goto nextline;
2104				}
2105				DEBUG(3, "processing option: %.*s", endcp-cp, cp);
2106				got_nondir = 1;
2107				savedcp = cp;
2108				if (do_opt(&cp, &endcp, &ngrp, &hostcount, &opt_flags,
2109					   &exflags, &anon, &secflavs, fspath, fsuuid)) {
2110					export_error(LOG_ERR, "error processing options: %s", savedcp);
2111					export_error_cleanup(xf);
2112					goto nextline;
2113				}
2114			} else if ((*cp == '/') || ((*cp == '\'' || *cp == '"') && (*(cp+1) == '/'))) {
2115				/*
2116				 * looks like we have a pathname
2117				 */
2118				DEBUG(2, "processing pathname: %.*s", endcp-cp, cp);
2119				word = clean_pathname(cp);
2120				DEBUG(3, "   cleaned pathname: %s", word);
2121				if (word == NULL) {
2122					export_error(LOG_ERR, "error processing pathname (out of memory)");
2123					export_error_cleanup(xf);
2124					goto nextline;
2125				}
2126				if (got_nondir) {
2127					export_error(LOG_ERR, "Directories must be first: %s", word);
2128					export_error_cleanup(xf);
2129					free(word);
2130					goto nextline;
2131				}
2132				if (strlen(word) > RPCMNT_NAMELEN) {
2133					export_error(LOG_ERR, "pathname too long (%d > %d): %s",
2134					    strlen(word), RPCMNT_NAMELEN, word);
2135					export_error_cleanup(xf);
2136					free(word);
2137					goto nextline;
2138				}
2139				/* Add path to this global exported path list */
2140				word2 = strdup(word);
2141				if ((error = add_dir(&xpaths, word2)))
2142					free(word2);
2143				if (error == ENOMEM) {
2144					log(LOG_WARNING, "Can't allocate memory to add export path: %s", word);
2145					xpaths_complete = 0;
2146				}
2147				/* Add path to this line's directory list */
2148				error = add_dir(&dirhead, word);
2149				if (error == EEXIST) {
2150					export_error(LOG_WARNING, "duplicate directory: %s", word);
2151					free(word);
2152				} else if (error == ENOMEM) {
2153					export_error(LOG_ERR, "Can't allocate memory to add directory: %s", word);
2154					export_error_cleanup(xf);
2155					free(word);
2156					goto nextline;
2157				}
2158			} else {
2159				/*
2160				 * looks like we have a host/netgroup
2161				 */
2162				savedc = *endcp;
2163				*endcp = '\0';
2164				DEBUG(2, "got host/netgroup: %s", cp);
2165				got_nondir = 1;
2166				if (dirhead == NULL) {
2167					export_error(LOG_ERR, "got host/group with no directory?: %s", cp);
2168					export_error_cleanup(xf);
2169					goto nextline;
2170				}
2171
2172				/* add it to the name list */
2173				error = add_name(&names, cp);
2174				if (error == ENOMEM) {
2175					export_error(LOG_ERR, "Can't allocate memory to add host/group: %s", cp);
2176					export_error_cleanup(xf);
2177					goto nextline;
2178				}
2179				/* iterate name list until it's empty (first name gets GF_SHOW) */
2180				show = 1;
2181				while ((nl = TAILQ_FIRST(&names))) {
2182					TAILQ_REMOVE(&names, nl, nl_next);
2183					name = nl->nl_name;
2184					free(nl);
2185					/* check if name is a netgroup */
2186					setnetgrent(name);
2187					netgrp = getnetgrent(&hst, &usr, &dom);
2188					if (netgrp) {
2189						DEBUG(3, "got netgroup: %s", name);
2190						error = add_name(&netgroups, name);
2191						if (error == ENOMEM) {
2192							export_error(LOG_ERR, "Can't allocate memory to add host/group: %s", cp);
2193							export_error_cleanup(xf);
2194							endnetgrent();
2195							free(name);
2196							goto nextline;
2197						}
2198						if (show) {
2199							/* add an entry for the netgroup (w/GF_SHOW) */
2200							DEBUG(3, "add netgroup w/show: %s", name);
2201							show = 0;
2202							bzero(&tmpgrp, sizeof(tmpgrp));
2203							tmpgrp.gr_type = GT_NETGROUP;
2204							tmpgrp.gr_flags = GF_SHOW;
2205							tmpgrp.gr_u.gt_netgroup = strdup(name);
2206							if (!tmpgrp.gr_u.gt_netgroup || !(grp = get_grp(&tmpgrp))) {
2207								export_error(LOG_ERR, "Can't allocate memory to add netgroup - %s", name);
2208								export_error_cleanup(xf);
2209								endnetgrent();
2210								free(name);
2211								goto nextline;
2212							} else if (!add_grp(&tgrp, grp)) {
2213								DEBUG(3, "duplicate netgroup: %s", name);
2214								free_grp(grp);
2215							}
2216						}
2217						if (error == -1) {
2218							/* already in the netgroup list, skip it */
2219							DEBUG(3, "netgroup already in netgroup list: %s", name);
2220							endnetgrent();
2221							free(name);
2222							continue;
2223						}
2224						/* enumerate the netgroup, adding each name to the name list */
2225						do {
2226							DEBUG(3, "add netgroup member: %s", hst);
2227							error = add_name(&names, hst);
2228							if (error == ENOMEM) {
2229								export_error(LOG_ERR, "Can't allocate memory to add netgroup:host - %s", name, hst);
2230								export_error_cleanup(xf);
2231								endnetgrent();
2232								free(name);
2233								goto nextline;
2234							}
2235						} while (getnetgrent(&hst, &usr, &dom));
2236						endnetgrent();
2237						free(name);
2238						continue;
2239					}
2240					endnetgrent();
2241					/* not a netgroup, add a host/net entry */
2242					bzero(&tmpgrp, sizeof(tmpgrp));
2243					if (get_host_addresses(name, &tmpgrp)) {
2244						export_error(LOG_WARNING, "couldn't get address for host: %s", name);
2245						badhostcount++;
2246					} else {
2247						DEBUG(3, "got host: %s", name);
2248						if (show) {
2249							show = 0;
2250							tmpgrp.gr_flags |= GF_SHOW;
2251						}
2252						grp = get_grp(&tmpgrp);
2253						if (!grp) {
2254							export_error(LOG_ERR, "Can't allocate memory to add host - %s", name);
2255						} else if (!add_grp(&tgrp, grp)) {
2256							DEBUG(3, "duplicate host: %s", name);
2257							free_grp(grp);
2258						} else {
2259							hostcount++;
2260						}
2261					}
2262					free(name);
2263				}
2264
2265				*endcp = savedc;
2266			}
2267			cp = endcp;
2268			nextfield(&cp, &endcp);
2269		}
2270
2271		/*
2272		 * Done parsing through export entry fields.
2273		 */
2274
2275		/* check options and hosts */
2276		if (!(opt_flags & (OP_MAPROOT|OP_MAPALL))) {
2277			/* If no mapping option specified, map root by default */
2278			exflags |= NX_MAPROOT;
2279			opt_flags |= OP_MAPROOT;
2280		}
2281		if (check_options(opt_flags)) {
2282			export_error(LOG_ERR, "bad export options");
2283			export_error_cleanup(xf);
2284			goto nextline;
2285		}
2286		if (!hostcount) {
2287			if (badhostcount) {
2288				export_error(LOG_ERR, "no valid hosts found for export");
2289				export_error_cleanup(xf);
2290				goto nextline;
2291			}
2292			DEBUG(1, "default export");
2293		} else if (opt_flags & OP_NET) {
2294			if (tgrp) {
2295				/*
2296				 * Don't allow a network export coincide with a list of
2297				 * host(s) on the same line.
2298				 */
2299				export_error(LOG_ERR, "can't specify both network and hosts on same line");
2300				export_error_cleanup(xf);
2301				goto nextline;
2302			}
2303			ngrp.gr_flags |= GF_SHOW;
2304			tgrp = get_grp(&ngrp);
2305			if (!tgrp) {
2306				export_error(LOG_ERR, "Can't allocate memory to add network - %s",
2307					grp_name(&ngrp));
2308				export_error_cleanup(xf);
2309				goto nextline;
2310			}
2311			ngrp.gr_type = GT_NULL;
2312		}
2313
2314		/* check directory list */
2315		if (dirhead == NULL) { /* sanity check */
2316			export_error(LOG_ERR, "no directories for export entry?");
2317			export_error_cleanup(xf);
2318			goto nextline;
2319		}
2320
2321		mntonname = NULL;
2322		if (opt_flags & (OP_FSPATH|OP_FSUUID))
2323			bestulp = find_exported_fs_by_path_and_uuid(
2324					(opt_flags & OP_FSPATH) ? fspath : NULL,
2325					(opt_flags & OP_FSUUID) ? fsuuid : NULL);
2326		else
2327			bestulp = find_exported_fs_by_dirlist(dirhead);
2328
2329		dirl = dirhead;
2330		/* Look for an exported directory that passes check_dirpath() */
2331		while (dirl && !check_dirpath(dirl->dl_dir)) {
2332			export_error(LOG_WARNING, "path contains non-directory or non-existent components: %s", dirl->dl_dir);
2333			/* skip subdirectories */
2334			dirl2 = dirl->dl_next;
2335			while (dirl2 && (subdir_check(dirl->dl_dir, dirl2->dl_dir) == 1))
2336				dirl2 = dirl2->dl_next;
2337			dirl = dirl2;
2338		}
2339		if (!dirl) {
2340			if (!bestulp) {
2341				export_error(LOG_ERR, "no usable directories in export entry and no fallback");
2342				export_error_cleanup(xf);
2343				goto nextline;
2344			}
2345			export_error(LOG_WARNING, "no usable directories in export entry");
2346			goto prepare_offline_export;
2347		}
2348		if (statfs(dirl->dl_dir, &fsb) < 0) {
2349			export_error(LOG_ERR, "statfs failed (%s (%d)) for path: %s",
2350				strerror(errno), errno, dirl->dl_dir);
2351			export_error_cleanup(xf);
2352			goto nextline;
2353		}
2354		if ((opt_flags & OP_FSPATH) && strcmp(fsb.f_mntonname, fspath)) {
2355			/* fspath doesn't match export fs path? */
2356			if (!bestulp) {
2357				export_error(LOG_ERR, "file system path (%s) does not match fspath (%s) and no fallback",
2358					fsb.f_mntonname, fspath);
2359				export_error_cleanup(xf);
2360				goto nextline;
2361			}
2362			export_error(LOG_WARNING, "file system path (%s) does not match fspath (%s)",
2363				fsb.f_mntonname, fspath);
2364			goto prepare_offline_export;
2365		}
2366		if (bestulp && (subdir_check(fsb.f_mntonname, bestulp->ul_mntonname) > 0)) {
2367			export_error(LOG_WARNING, "Exported file system (%s) doesn't match best guess (%s).",
2368				fsb.f_mntonname, bestulp->ul_mntonname);
2369			if (!(opt_flags & (OP_FSUUID|OP_FSPATH)))
2370				export_error(LOG_WARNING, "Suggest using fspath=/path and/or fsuuid=uuid to disambiguate.");
2371			goto prepare_offline_export;
2372		}
2373		if (!(ulp = get_uuid(&fsb, uuid))) {
2374			export_error(LOG_ERR, "couldn't get UUID for volume: %s", fsb.f_mntonname);
2375			export_error_cleanup(xf);
2376			goto nextline;
2377		}
2378		if ((opt_flags & OP_FSUUID) && bcmp(uuid, fsuuid, sizeof(fsuuid))) {
2379			/* fsuuid doesn't match export fs uuid? */
2380			if (!bestulp) {
2381				export_error(LOG_ERR, "file system UUID (%s) does not match fsuuid (%s) and no fallback",
2382					uuidstring(uuid, buf), uuidstring(fsuuid, buf2));
2383				export_error_cleanup(xf);
2384				goto nextline;
2385			}
2386			export_error(LOG_WARNING, "file system UUID (%s) does not match fsuuid (%s)",
2387				uuidstring(uuid, buf), uuidstring(fsuuid, buf2));
2388			goto prepare_offline_export;
2389		}
2390
2391		if (bestulp && (opt_flags & OP_MISSING)) {
2392prepare_offline_export:
2393			missingexportcount++;
2394			export_error(LOG_WARNING, "using fallback (marked offline): %s", bestulp->ul_mntonname);
2395			exflags |= NX_OFFLINE;
2396			opt_flags |= OP_MISSING;
2397			ulp = bestulp;
2398			mntonname = ulp->ul_mntonname;
2399			bcopy(ulp->ul_uuid, uuid, sizeof(uuid));
2400		} else {
2401			mntonname = fsb.f_mntonname;
2402		}
2403
2404		/* See if this directory is already in the export list. */
2405		xf = ex_search(uuid);
2406		if (xf == NULL) {
2407			xf = get_expfs();
2408			if (xf)
2409				xf->xf_fsdir = strdup(mntonname);
2410			if (!xf || !xf->xf_fsdir) {
2411				export_error(LOG_ERR, "Can't allocate memory to export volume: %s",
2412					mntonname);
2413				export_error_cleanup(xf);
2414				goto nextline;
2415			}
2416			bcopy(uuid, xf->xf_uuid, sizeof(uuid));
2417			xf->xf_fsid = ulp->ul_fsid;
2418			DEBUG(2, "New expfs uuid=%s", uuidstring(uuid, buf));
2419		} else {
2420			DEBUG(2, "Found expfs uuid=%s", uuidstring(uuid, buf));
2421		}
2422
2423		/* verify the rest of the directories in the list are kosher */
2424		if (dirl)
2425			dirl = dirl->dl_next;
2426		for (; dirl; dirl = dirl->dl_next) {
2427			DEBUG(2, "dir: %s", dirl->dl_dir);
2428			if (!check_dirpath(dirl->dl_dir)) {
2429				export_error(LOG_WARNING, "path contains non-directory or non-existent components: %s", dirl->dl_dir);
2430				continue;
2431			}
2432			if (statfs(dirl->dl_dir, &fsb) < 0) {
2433				export_error(LOG_WARNING, "statfs failed (%s (%d)) for path: %s",
2434					strerror(errno), errno, dirl->dl_dir);
2435				continue;
2436			}
2437			if (strcmp(xf->xf_fsdir, fsb.f_mntonname)) {
2438				export_error(LOG_WARNING, "Volume mismatch for: %s\ndirectories must be on same volume", dirl->dl_dir);
2439				continue;
2440			}
2441		}
2442
2443		/*
2444		 * Done processing exports line fields.
2445		 */
2446
2447		/*
2448		 * Walk the dirlist.
2449		 * Verify the next dir is (or can be) an exported directory.
2450		 * Check subsequent dirs to see if they are mount subdirs of that dir.
2451		 */
2452		dirl = dirhead;
2453		while (dirl) {
2454			DEBUG(2, "dir: %s", dirl->dl_dir);
2455			/*
2456			 * Check for nesting conflicts with any existing entries.
2457			 * Note we skip any entries that are NOT marked OP_ADD because
2458			 * we don't care about directories that are tagged for deletion.
2459			 */
2460			TAILQ_FOREACH(xd2, &xf->xf_dirl, xd_next) {
2461				if ((xd2->xd_iflags & OP_ADD) &&
2462				    ((subdir_check(xd2->xd_dir, dirl->dl_dir) == 1) ||
2463				     (subdir_check(dirl->dl_dir, xd2->xd_dir) == 1))) {
2464					export_error(LOG_ERR, "%s conflicts with existing export %s",
2465						dirl->dl_dir, xd2->xd_dir);
2466					export_error_cleanup(xf);
2467					goto nextline;
2468				}
2469			}
2470			/*
2471			 * Scan exported file system for a matching exported directory
2472			 * or at least the insertion point of a new one.
2473			 */
2474			len = strlen(dirl->dl_dir);
2475			xd3 = NULL;
2476			cmp = 1;
2477			TAILQ_FOREACH(xd2, &xf->xf_dirl, xd_next) {
2478				dlen = strlen(xd2->xd_dir);
2479				cmp = strncmp(dirl->dl_dir, xd2->xd_dir, dlen);
2480				DEBUG(3, "     %s compare %s %d", dirl->dl_dir,
2481					xd2->xd_dir, cmp);
2482				if (!cmp) {
2483					if (len == dlen) /* found an exact match */
2484						break;
2485					/* dirl was actually longer than xd2 */
2486					cmp = 1;
2487				}
2488				if (cmp > 0)
2489					break;
2490				xd3 = xd2;
2491			}
2492
2493			if (!cmp) {
2494				xd = xd2;
2495				DEBUG(2, "     %s xd is %s", dirl->dl_dir, xd->xd_dir);
2496			} else {
2497				/* go ahead and create a new expdir structure */
2498				if (strncmp(dirl->dl_dir, mntonname, strlen(mntonname))) {
2499					export_error(LOG_ERR, "exported dir/fs mismatch: %s %s",
2500						dirl->dl_dir, mntonname);
2501					export_error_cleanup(xf);
2502					goto nextline;
2503				}
2504				/* first, get export path and ID */
2505				/* point subdir beyond mount path string */
2506				subdir = dirl->dl_dir + strlen(mntonname);
2507				/* skip "/" between mount and subdir */
2508				while (*subdir && (*subdir == '/'))
2509					subdir++;
2510				xid = get_export_id(ulp, subdir);
2511				if (!xid) {
2512					export_error(LOG_ERR, "unable to get export ID for %s", dirl->dl_dir);
2513					export_error_cleanup(xf);
2514					goto nextline;
2515				}
2516				xd = get_expdir();
2517				if (xd)
2518					xd->xd_dir = strdup(dirl->dl_dir);
2519				if (!xd || !xd->xd_dir) {
2520					if (xd)
2521						free_expdir(xd);
2522					export_error(LOG_ERR, "can't allocate memory for export %s", dirl->dl_dir);
2523					export_error_cleanup(xf);
2524					goto nextline;
2525				}
2526				xd->xd_xid = xid;
2527				DEBUG(2, "     %s new xd", xd->xd_dir);
2528			}
2529
2530			/* preflight the addition of these new export options */
2531			if (hang_options_setup(xd, opt_flags, &anon, tgrp, &secflavs, &need_export)) {
2532				export_error(LOG_ERR, "export option conflict for %s", xd->xd_dir);
2533				/* XXX what to do about already successful exports? */
2534				hang_options_cleanup(xd);
2535				if (cmp)
2536					free_expdir(xd);
2537				export_error_cleanup(xf);
2538				goto nextline;
2539			}
2540
2541			/*
2542			 * Send list of hosts to do_export for pushing the exports into
2543			 * the kernel (unless checkexports and the export is missing).
2544			 */
2545			if (need_export && !(checkexports && (opt_flags & OP_MISSING))) {
2546				int expcmd = checkexports ? NXA_CHECK : NXA_REPLACE;
2547				if (do_export(expcmd, xf, xd, tgrp, exflags, &anon, &secflavs)) {
2548					if ((errno == ENOTSUP) || (errno == EISDIR)) {
2549						/* if ENOTSUP report lack of NFS export support */
2550						/* if EISDIR report lack of extended readdir support */
2551						export_error(LOG_ERR, "kernel export registration failed: "
2552							"NFS exporting not supported by fstype \"%s\" (%s)",
2553							statfs(xf->xf_fsdir, &fsb) ? "?" : fsb.f_fstypename,
2554							(errno == EISDIR) ? "readdir" : "fh");
2555					} else {
2556						export_error(LOG_ERR, "kernel export registration failed");
2557					}
2558					hang_options_cleanup(xd);
2559					if (cmp)
2560						free_expdir(xd);
2561					export_error_cleanup(xf);
2562					goto nextline;
2563				}
2564				/* Success. Update the data structures.  */
2565				DEBUG(1, "kernel export registered for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path);
2566			} else {
2567				DEBUG(2, "kernel export already registered for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path);
2568			}
2569
2570			/* add mount subdirectories of this directory */
2571			dirl2 = dirl->dl_next;
2572			while (dirl2) {
2573				if (subdir_check(dirl->dl_dir, dirl2->dl_dir) != 1)
2574					break;
2575				error = hang_options_mountdir(xd, dirl2->dl_dir, opt_flags, tgrp, &secflavs);
2576				if (error == EEXIST) {
2577					export_error(LOG_WARNING, "mount subdirectory export option conflict for %s",
2578						dirl2->dl_dir);
2579				} else if (error == ENOMEM) {
2580					export_error(LOG_WARNING, "unable to add mount subdirectory for %s, %s",
2581						xd->xd_dir, dirl2->dl_dir);
2582				}
2583				dirl2 = dirl2->dl_next;
2584			}
2585			dirl = dirl2;
2586
2587			/* finalize export option additions */
2588			hang_options_finalize(xd);
2589
2590			/* mark that we've added exports to this xd */
2591			xd->xd_iflags |= OP_ADD;
2592
2593			if (cmp) {
2594				/* add new expdir to xf */
2595				if (xd3) {
2596					TAILQ_INSERT_AFTER(&xf->xf_dirl, xd3, xd, xd_next);
2597				} else {
2598					TAILQ_INSERT_HEAD(&xf->xf_dirl, xd, xd_next);
2599				}
2600			}
2601
2602		}
2603
2604		if ((xf->xf_flag & XF_LINKED) == 0) {
2605			/* Insert in the list in alphabetical order. */
2606			xf3 = NULL;
2607			TAILQ_FOREACH(xf2, &xfshead, xf_next) {
2608				if (strcmp(xf->xf_fsdir, xf2->xf_fsdir) < 0)
2609					break;
2610				xf3 = xf2;
2611			}
2612			if (xf3) {
2613				TAILQ_INSERT_AFTER(&xfshead, xf3, xf, xf_next);
2614			} else {
2615				TAILQ_INSERT_HEAD(&xfshead, xf, xf_next);
2616			}
2617			xf->xf_flag |= XF_LINKED;
2618		}
2619
2620		if (export_errors == saved_errors) {
2621			/* no errors, clear any previous errors for this entry */
2622			if (clear_export_errors(linenum))
2623				log(LOG_WARNING, "exports:%d: export entry OK (previous errors cleared)", linenum);
2624		}
2625nextline:
2626		if (!TAILQ_EMPTY(&netgroups))
2627			free_namelist(&netgroups);
2628		if (!TAILQ_EMPTY(&names))
2629			free_namelist(&names);
2630		if (dirhead) {
2631			free_dirlist(dirhead);
2632			dirhead = NULL;
2633		}
2634		/* release groups */
2635		switch (ngrp.gr_type) {
2636		case GT_NET:
2637			if (ngrp.gr_u.gt_net.nt_name)
2638				free(ngrp.gr_u.gt_net.nt_name);
2639			break;
2640		}
2641		while (tgrp) {
2642			grp = tgrp;
2643			tgrp = tgrp->gr_next;
2644			grp->gr_flags &= ~GF_SHOW;
2645			free_grp(grp);
2646		}
2647	}
2648
2649	fclose(exp_file);
2650
2651	if (config.verbose >= 5) {
2652		DEBUG(3, "========> get_exportlist() CURRENT EXPORTS UPDATED:");
2653		dump_exports();
2654	}
2655
2656exports_read:
2657	/*
2658	 * Now, find all existing structures still tagged for deletion.
2659	 * For each tagged group found, call nfssvc(NXA_DELETE) to delete
2660	 * the exports for the addresses that haven't had new/replacement
2661	 * exports registered.  We simply scan the current exports for
2662	 * an untagged match for each address.  If an exported directory
2663	 * loses all of its exports, we delete that expdir.
2664	 */
2665	xf = TAILQ_FIRST(&xfshead);
2666	while (xf && !checkexports) {
2667		xd = TAILQ_FIRST(&xf->xf_dirl);
2668		while (xd) {
2669			/*
2670			 * First check the list of mountdirs.  Since the kernel
2671			 * is not aware of these and they are not registered,
2672			 * we merely need to delete the data structures.
2673			 */
2674			TAILQ_FOREACH_SAFE(xd2, &xd->xd_mountdirs, xd_next, xd3) {
2675				TAILQ_FOREACH_SAFE(ht, &xd2->xd_hosts, ht_next, ht2)
2676					if (ht->ht_flags & OP_DEL) {
2677						TAILQ_REMOVE(&xd2->xd_hosts, ht, ht_next);
2678						free_host(ht);
2679					}
2680				if (xd2->xd_flags & OP_DEL)
2681					xd2->xd_flags = xd2->xd_oflags = 0;
2682				if (!(xd2->xd_flags & OP_DEFEXP) && TAILQ_EMPTY(&xd2->xd_hosts)) {
2683					/* No more exports here, delete */
2684					TAILQ_REMOVE(&xd->xd_mountdirs, xd2, xd_next);
2685					free_expdir(xd2);
2686				}
2687			}
2688			/*
2689			 * Now scan the xd_hosts list for hosts that are still
2690			 * tagged for deletion and move those to the htfree list.
2691			 */
2692			TAILQ_INIT(&htfree);
2693			TAILQ_FOREACH_SAFE(ht, &xd->xd_hosts, ht_next, ht2)
2694				if (ht->ht_flags & OP_DEL) {
2695					TAILQ_REMOVE(&xd->xd_hosts, ht, ht_next);
2696					TAILQ_INSERT_TAIL(&htfree, ht, ht_next);
2697				}
2698			/*
2699			 * Go through htfree and find the groups/addresses
2700			 * that are no longer being exported to and place
2701			 * those groups/addresses in the list tgrp.
2702			 * The hosts and groups/addresses that have been
2703			 * replaced with newer exports (and thus don't require
2704			 * deletion from the kernel) will be freed as we go.
2705			 */
2706			tgrp = NULL;
2707			TAILQ_FOREACH_SAFE(ht, &htfree, ht_next, ht2) {
2708				grp = ht->ht_grp;
2709				if (grp->gr_type == GT_NETGROUP) {
2710					/*
2711					 * netgroup entries should just be freed up now with the host.
2712					 * Don't bother trying to match up because they won't.
2713					 */
2714				} else if (!find_group_address_match_in_host_list(&xd->xd_hosts, grp)) {
2715					/* no conflicts, so we can safely delete these */
2716					/* steal the grp from the host */
2717					ht->ht_grp = NULL;
2718					if (!add_grp(&tgrp, grp)) {
2719						/* shouldn't happen...  */
2720						log(LOG_ERR, "failure to queue group for export deletion");
2721						/* ... but try to recover anyway */
2722						grp->gr_next = tgrp;
2723						tgrp = grp;
2724					}
2725				} else if (grp->gr_type == GT_HOST) {
2726					struct addrinfo *a1, *a2, *prea3, *a3, atmp, *anexttmp;
2727					/*
2728					 * Some or all of the addresses are still exported.
2729					 * Find any addresses whose exports haven't been replaced.
2730					 * a3 points to the location of the next address we will
2731					 * want to delete the export for.  a1 walks the array
2732					 * and a3 follows along/behind essentially compacting the
2733					 * array of addresses into only those that we want to
2734					 * delete exports for.  The effect is that addresses which
2735					 * are still exported will be "squeezed" out of the array.
2736					 */
2737					a1 = grp->gr_u.gt_hostinfo.h_ailist;
2738					prea3 = NULL;
2739					a3 = a1;
2740					while (a1) {
2741						/* scan exports host list for GT_HOSTs w/matching address */
2742						TAILQ_FOREACH(ht3, &xd->xd_hosts, ht_next) {
2743							if (ht3->ht_grp->gr_type != GT_HOST)
2744								continue;
2745							a2 = ht3->ht_grp->gr_u.gt_hostinfo.h_ailist;
2746							while (a2 && addrinfo_cmp(a1, a2))
2747								a2 = a2->ai_next;
2748							if (a2) /* we found a match */
2749								break;
2750						}
2751						if (!ht3) {
2752							/* didn't find address, so "add" it to the array */
2753							if (a3 != a1) {
2754								/* switch all elements except ai_next */
2755								atmp = *a3;
2756								anexttmp = a3->ai_next;
2757								*a3 = *a1;
2758								a3->ai_next = anexttmp;
2759								atmp.ai_next = a1->ai_next;
2760								*a1 = atmp;
2761							}
2762							prea3 = a3;
2763							a3 = a3->ai_next;
2764						}
2765						a1 = a1->ai_next;
2766					}
2767					if (a3 == grp->gr_u.gt_hostinfo.h_ailist) {
2768						/* a3 hasn't moved, so we know that all of */
2769						/* the addresses are being exported again */
2770						/* so we shouldn't delete any of the exports */
2771					} else {
2772						/* some of the addresses are being exported again */
2773						/* we've compacted the list of addresses that aren't */
2774						/* and here we will free up the rest of them */
2775						if (prea3)
2776							prea3->ai_next = NULL;
2777						freeaddrinfo(a3);
2778						/* steal the grp from the host */
2779						ht->ht_grp = NULL;
2780						if (!add_grp(&tgrp, grp)) {
2781							/* shouldn't happen...  */
2782							log(LOG_ERR, "failure to queue group for export deletion");
2783							/* ... but try to recover anyway */
2784							grp->gr_next = tgrp;
2785							tgrp = grp;
2786						}
2787					}
2788				}
2789				TAILQ_REMOVE(&htfree, ht, ht_next);
2790				free_host(ht);
2791			}
2792			if ((config.verbose >= 3) && tgrp) {
2793				struct grouplist *g;
2794				DEBUG(1, "deleting export for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path);
2795				g = tgrp;
2796				while (g) {
2797					DEBUG(3, "    %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g));
2798					g = g->gr_next;
2799				}
2800			}
2801			if (tgrp && do_export(NXA_DELETE, xf, xd, tgrp, 0, NULL, NULL)) {
2802				log(LOG_ERR, "kernel export unregistration failed for %s, %s%s",
2803					xd->xd_dir, grp_name(tgrp), tgrp->gr_next ? ", ..." : "");
2804			}
2805			while (tgrp) {
2806				grp = tgrp;
2807				tgrp = tgrp->gr_next;
2808				free_grp(grp);
2809			}
2810			if ((xd->xd_flags & (OP_DEL|OP_DEFEXP)) == (OP_DEL|OP_DEFEXP)) {
2811				DEBUG(1, "deleting default export for %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path);
2812				/* we need to delete this default export */
2813				xd->xd_flags = xd->xd_oflags = 0;
2814				if (do_export(NXA_DELETE, xf, xd, NULL, 0, NULL, NULL)) {
2815					log(LOG_ERR, "kernel export unregistration failed for %s,"
2816						" default export", xd->xd_dir);
2817				}
2818			}
2819			xd3 = TAILQ_NEXT(xd, xd_next);
2820			if (!(xd->xd_flags & OP_DEFEXP) && TAILQ_EMPTY(&xd->xd_hosts)) {
2821				TAILQ_REMOVE(&xf->xf_dirl, xd, xd_next);
2822				free_expdir(xd);
2823			} else {
2824				xd->xd_iflags &= ~OP_ADD;
2825				xd->xd_flags &= ~OP_DEL;
2826			}
2827			xd = xd3;
2828		}
2829
2830		xf2 = TAILQ_NEXT(xf, xf_next);
2831		if (TAILQ_EMPTY(&xf->xf_dirl)) {
2832			/* No more exports here, delete */
2833			TAILQ_REMOVE(&xfshead, xf, xf_next);
2834			free_expfs(xf);
2835		}
2836		xf = xf2;
2837	}
2838
2839	if (config.verbose >= 4) {
2840		DEBUG(2, "========> get_exportlist() NEW EXPORTS LIST:");
2841		dump_exports();
2842	}
2843
2844	if (!checkexports)
2845		uuidlist_save();
2846
2847	/*
2848	 * If we appear to be having problems resolving host names on startup,
2849	 * then we'll want to automatically recheck exports for a while.
2850	 *
2851	 * First time through, we make sure to set recheckexports_until.
2852	 * If we have problems then set the recheck timer - otherwise disable it.
2853	 * On subsequent export checks, turn off the recheck timer once we no
2854	 * longer need it or the timer expires.
2855	 */
2856	if (!checkexports && (recheckexports_until == 0)) {	/* first time through... */
2857		/* did we have any host names and were any of them problematic? */
2858		if (hostnamegoodcount != hostnamecount) {
2859			log(LOG_WARNING, "There seem to be problems resolving host names...");
2860			log(LOG_WARNING, "...will periodically recheck exports for a while.");
2861			recheckexports_until = time(NULL) + RECHECKEXPORTS_TIMEOUT; /* set the recheck timer */
2862		} else {
2863			recheckexports_until = -1; /* turn it off */
2864		}
2865	} else if (recheckexports_until > 0) {
2866		/* if we don't need to recheck any more, turn it off */
2867		if (hostnamegoodcount == hostnamecount) {
2868			recheckexports_until = -1;
2869		} else if (recheckexports_until < time(NULL)) {
2870			log(LOG_WARNING, "Giving up on automatic rechecking of exports.");
2871			recheckexports_until = -1;
2872		}
2873	}
2874	/* should we be rechecking exports? */
2875	if (!checkexports && ((recheckexports_until > 0) || missingexportcount))
2876		recheckexports = 1;
2877	else
2878		recheckexports = 0;
2879
2880	unlock_exports();
2881
2882	return (export_errors);
2883}
2884
2885/*
2886 * Allocate an exported file system structure
2887 */
2888struct expfs *
2889get_expfs(void)
2890{
2891	struct expfs *xf;
2892
2893	xf = malloc(sizeof(*xf));
2894	if (xf == NULL)
2895		return (NULL);
2896	memset(xf, 0, sizeof(*xf));
2897	TAILQ_INIT(&xf->xf_dirl);
2898	return (xf);
2899}
2900
2901/*
2902 * Allocate an exported directory structure
2903 */
2904struct expdir *
2905get_expdir(void)
2906{
2907	struct expdir *xd;
2908
2909	xd = malloc(sizeof(*xd));
2910	if (xd == NULL)
2911		return (NULL);
2912	memset(xd, 0, sizeof(*xd));
2913	TAILQ_INIT(&xd->xd_hosts);
2914	TAILQ_INIT(&xd->xd_mountdirs);
2915	return (xd);
2916}
2917
2918/*
2919 * Return the "name" of the given group
2920 */
2921static char unknown_group[] = "unknown group";
2922char *
2923grp_name(struct grouplist *grp)
2924{
2925	if (grp->gr_type == GT_NETGROUP)
2926		return (grp->gr_u.gt_netgroup);
2927	if (grp->gr_type == GT_NET)
2928		return (grp->gr_u.gt_net.nt_name);
2929	if (grp->gr_type == GT_HOST)
2930		return (grp->gr_u.gt_hostinfo.h_name);
2931	return (unknown_group);
2932}
2933
2934/*
2935 * Return a string (in a static buffer) for the (first) address of the given group.
2936 */
2937static char grpaddrbuf[MAXPATHLEN];
2938const char *
2939grp_addr(struct grouplist *grp)
2940{
2941	struct addrinfo *ai;
2942	void *sinaddr;
2943	const char *s = NULL;
2944
2945	if (grp->gr_type == GT_HOST) {
2946		if ((ai = grp->gr_u.gt_hostinfo.h_ailist)) {
2947			sinaddr = (ai->ai_family == AF_INET) ?
2948				(void*)&((struct sockaddr_in*)  AOK ai->ai_addr)->sin_addr :
2949				(void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr;
2950			if (inet_ntop(ai->ai_family, sinaddr, grpaddrbuf, sizeof(grpaddrbuf)))
2951				s = grpaddrbuf;
2952		}
2953	} else if (grp->gr_type == GT_NET) {
2954		sinaddr = (grp->gr_u.gt_net.nt_family == AF_INET) ?
2955			(void*)&grp->gr_u.gt_net.nt_net : (void*)&grp->gr_u.gt_net.nt_net6;
2956		if (inet_ntop(grp->gr_u.gt_net.nt_family, sinaddr, grpaddrbuf, sizeof(grpaddrbuf)))
2957			s = grpaddrbuf;
2958	} else if (grp->gr_type == GT_NETGROUP) {
2959		s = "";
2960	}
2961
2962	if (!s)
2963		s = "???";
2964	return (s);
2965}
2966
2967int
2968addrinfo_cmp(struct addrinfo *a1, struct addrinfo *a2)
2969{
2970	if (a1->ai_family != a2->ai_family)
2971		return (a1->ai_family - a2->ai_family);
2972	if (a1->ai_addrlen != a2->ai_addrlen)
2973		return (a1->ai_addrlen - a2->ai_addrlen);
2974	return bcmp(a1->ai_addr, a2->ai_addr, a1->ai_addrlen);
2975}
2976
2977/*
2978 * compare two group list elements
2979 */
2980int
2981grpcmp(struct grouplist *g1, struct grouplist *g2)
2982{
2983	struct netmsk *n1, *n2;
2984	struct addrinfo *a1, *a2;
2985	int rv;
2986
2987	rv = g1->gr_type - g2->gr_type;
2988	if (rv)
2989		return (rv);
2990	switch (g1->gr_type) {
2991	case GT_NETGROUP:
2992		rv = strcmp(g1->gr_u.gt_netgroup, g2->gr_u.gt_netgroup);
2993		break;
2994	case GT_NET:
2995		n1 = &g1->gr_u.gt_net;
2996		n2 = &g2->gr_u.gt_net;
2997		rv = strcmp(n1->nt_name, n2->nt_name);
2998		if (rv)
2999			break;
3000		rv = n1->nt_family - n2->nt_family;
3001		if (rv)
3002			break;
3003		if (n1->nt_family == AF_INET) {
3004			rv = bcmp(&n1->nt_net, &n2->nt_net, sizeof(n1->nt_net));
3005			if (rv)
3006				break;
3007			rv = bcmp(&n1->nt_mask, &n2->nt_mask, sizeof(n1->nt_mask));
3008		} else if (n1->nt_family == AF_INET6) {
3009			rv = bcmp(&n1->nt_net6, &n2->nt_net6, sizeof(n1->nt_net6));
3010			if (rv)
3011				break;
3012			rv = bcmp(&n1->nt_mask6, &n2->nt_mask6, sizeof(n1->nt_mask6));
3013		}
3014		break;
3015	case GT_HOST:
3016		rv = strcmp(g1->gr_u.gt_hostinfo.h_name, g2->gr_u.gt_hostinfo.h_name);
3017		if (rv)
3018			break;
3019		a1 = g1->gr_u.gt_hostinfo.h_ailist;
3020		a2 = g2->gr_u.gt_hostinfo.h_ailist;
3021		while (a1 && a2) {
3022			rv = addrinfo_cmp(a1, a2);
3023			if (rv)
3024				break;
3025			a1 = a1->ai_next;
3026			a2 = a2->ai_next;
3027		}
3028		if (!rv && !(!a1 && !a2)) {
3029			if (a1)
3030				rv = 1;
3031			else
3032				rv = -1;
3033		}
3034		break;
3035	}
3036	return (rv);
3037}
3038
3039/*
3040 * Return a group in the group cache that matches the given group.
3041 * If the group isn't yet in the cache, a new group will be allocated,
3042 * populated with the info from the given group, and added to the cache.
3043 * In any event, any memory referred to within grptmp->gr_u has been
3044 * either used in a new group cache entry or freed.
3045 */
3046struct grouplist *
3047get_grp(struct grouplist *grptmp)
3048{
3049	struct grouplist *g, *g2;
3050	int cmp = 1, clean_up_gr_u = 1;
3051
3052	if (config.verbose >= 7) {
3053		DEBUG(5, "get_grp: %s %s", grp_name(grptmp), grp_addr(grptmp));
3054		g = grpcache;
3055		while (g) {
3056			DEBUG(6, "grpcache: %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g));
3057			g = g->gr_cache;
3058		}
3059	}
3060
3061	g2 = NULL;
3062	g = grpcache;
3063	while (g && ((cmp = grpcmp(grptmp, g)) > 0)) {
3064		g2 = g;
3065		g = g->gr_cache;
3066	}
3067
3068	if (!cmp) {
3069		g->gr_refcnt++;
3070		if (config.verbose >= 7)
3071			DEBUG(5, "get_grp: found %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g));
3072		g->gr_flags |= grptmp->gr_flags;
3073		goto out;
3074	}
3075
3076	g = malloc(sizeof(*g));
3077	if (g == NULL)
3078		goto out;
3079	memset(g, 0, sizeof(*g));
3080	g->gr_refcnt = 1;
3081	g->gr_type = grptmp->gr_type;
3082	g->gr_flags = grptmp->gr_flags;
3083	g->gr_u = grptmp->gr_u; /* memory allocations in *grptmp->gr_u are now owned by g->gr_u */
3084	clean_up_gr_u = 0;
3085
3086	if (g2) {
3087		g->gr_cache = g2->gr_cache;
3088		g2->gr_cache = g;
3089	} else {
3090		g->gr_cache = grpcache;
3091		grpcache = g;
3092	}
3093
3094	if (config.verbose >= 7) {
3095		DEBUG(5, "get_grp: ----- NEW %p %d %s %s", g, g->gr_refcnt, grp_name(g), grp_addr(g));
3096		g2 = grpcache;
3097		while (g2) {
3098			DEBUG(6, "grpcache: %p %d %s %s", g2, g2->gr_refcnt, grp_name(g2), grp_addr(g2));
3099			g2 = g2->gr_cache;
3100		}
3101	}
3102
3103out:
3104	if (clean_up_gr_u) {
3105		/* free up the contents of grptmp->gr_u */
3106		switch (grptmp->gr_type) {
3107		case GT_HOST:
3108			if (grptmp->gr_u.gt_hostinfo.h_ailist)
3109				freeaddrinfo(grptmp->gr_u.gt_hostinfo.h_ailist);
3110			if (grptmp->gr_u.gt_hostinfo.h_name)
3111				free(grptmp->gr_u.gt_hostinfo.h_name);
3112			break;
3113		case GT_NET:
3114			if (grptmp->gr_u.gt_net.nt_name)
3115				free(grptmp->gr_u.gt_net.nt_name);
3116			break;
3117		case GT_NETGROUP:
3118			if (grptmp->gr_u.gt_netgroup)
3119				free(grptmp->gr_u.gt_netgroup);
3120			break;
3121		}
3122	}
3123
3124	return (g);
3125}
3126
3127/*
3128 * Free up a group list.
3129 */
3130void
3131free_grp(struct grouplist *grp)
3132{
3133	struct grouplist **g;
3134
3135	/* decrement reference count */
3136	grp->gr_refcnt--;
3137
3138	if (config.verbose >= 7)
3139		DEBUG(5, "free_grp: %p %d %s %s", grp, grp->gr_refcnt, grp_name(grp), grp_addr(grp));
3140
3141	if (grp->gr_refcnt > 0)
3142		return;
3143
3144	/* remove group from grpcache list */
3145	g = &grpcache;
3146	while (*g && (*g != grp))
3147		g = &(*g)->gr_cache;
3148	if (*g)
3149		*g = (*g)->gr_cache;
3150
3151	/* free up the memory */
3152	if (grp->gr_type == GT_HOST) {
3153		if (grp->gr_u.gt_hostinfo.h_ailist)
3154			freeaddrinfo(grp->gr_u.gt_hostinfo.h_ailist);
3155		if (grp->gr_u.gt_hostinfo.h_name)
3156			free(grp->gr_u.gt_hostinfo.h_name);
3157	} else if (grp->gr_type == GT_NET) {
3158		if (grp->gr_u.gt_net.nt_name)
3159			free(grp->gr_u.gt_net.nt_name);
3160	} else if (grp->gr_type == GT_NETGROUP) {
3161		if (grp->gr_u.gt_netgroup)
3162			free(grp->gr_u.gt_netgroup);
3163	}
3164	free((caddr_t)grp);
3165}
3166
3167/*
3168 * insert a group list element into a group list
3169 */
3170int
3171add_grp(struct grouplist **glp, struct grouplist *newg)
3172{
3173	struct grouplist *g1, *g2;
3174	int cmp = 1;
3175
3176	g2 = NULL;
3177	g1 = *glp;
3178	while (g1 && ((cmp = grpcmp(newg, g1)) > 0)) {
3179		g2 = g1;
3180		g1 = g1->gr_next;
3181	}
3182	if (!cmp) {
3183		/* already in list, make sure SHOW bit is set */
3184		if (newg->gr_flags & GF_SHOW)
3185			g1->gr_flags |= GF_SHOW;
3186		return (0);
3187	}
3188	if (g2) {
3189		newg->gr_next = g2->gr_next;
3190		g2->gr_next = newg;
3191	} else {
3192		newg->gr_next = *glp;
3193		*glp = newg;
3194	}
3195	return (1);
3196}
3197
3198/*
3199 * insert a host list element into a host list
3200 * (identical to add_grp(), but takes a host list and
3201 *  allows "duplicates" if one is tagged for deletion)
3202 */
3203int
3204add_host(struct hosttqh *head, struct host *newht)
3205{
3206	struct host *ht;
3207	int cmp = 1;
3208
3209	TAILQ_FOREACH(ht, head, ht_next)
3210		if ((cmp = grpcmp(newht->ht_grp, ht->ht_grp)) <= 0)
3211			break;
3212	if (!cmp && !(ht->ht_flags & OP_DEL)) {
3213		/* already in list, make sure SHOW bit is set */
3214		if (newht->ht_flags & OP_SHOW)
3215			ht->ht_flags |= OP_SHOW;
3216		return (0);
3217	}
3218	if (ht)
3219		TAILQ_INSERT_BEFORE(ht, newht, ht_next);
3220	else
3221		TAILQ_INSERT_TAIL(head, newht, ht_next);
3222	return (1);
3223}
3224
3225/*
3226 * report/record an export error message
3227 */
3228void
3229export_error(int level, const char *fmt, ...)
3230{
3231	struct errlist *elnew, *el, *elp;
3232	char *s = NULL;
3233	va_list ap;
3234
3235	export_errors++;
3236
3237#pragma clang diagnostic push
3238#pragma clang diagnostic ignored "-Wformat-nonliteral"
3239	va_start(ap, fmt);
3240	vasprintf(&s, fmt, ap);
3241	va_end(ap);
3242#pragma clang diagnostic pop
3243
3244	/* check if we've already logged this error */
3245	LIST_FOREACH(el, &xerrs, el_next) {
3246		if (linenum < el->el_linenum)
3247			continue;
3248		if (linenum > el->el_linenum) {
3249			el = NULL;
3250			break;
3251		}
3252		if (!strcmp(el->el_msg, s))
3253			break;
3254	}
3255
3256	/* log the message if we haven't already */
3257	if (!el)
3258		log(level, "exports:%d: %s", linenum, s);
3259
3260	/* add this error to the list */
3261	elnew = malloc(sizeof(*elnew));
3262	if (!elnew) {
3263		free(s);
3264		return;
3265	}
3266	elnew->el_linenum = linenum;
3267	elnew->el_msg = s;
3268	elp = NULL;
3269	LIST_FOREACH(el, &xerrs, el_next) {
3270		if (linenum < el->el_linenum)
3271			break;
3272		elp = el;
3273	}
3274	if (elp)
3275		LIST_INSERT_AFTER(elp, elnew, el_next);
3276	else
3277		LIST_INSERT_HEAD(&xerrs, elnew, el_next);
3278}
3279
3280/*
3281 * clear export errors on the give line# (or all if line#=0)
3282 */
3283int
3284clear_export_errors(uint32_t linenum)
3285{
3286	struct errlist *el, *elnext;
3287	int cleared = 0;
3288
3289	LIST_FOREACH_SAFE(el, &xerrs, el_next, elnext) {
3290		if (linenum) {
3291			if (linenum < el->el_linenum)
3292				break;
3293			if (linenum > el->el_linenum)
3294				continue;
3295		}
3296		cleared = 1;
3297		LIST_REMOVE(el, el_next);
3298		if (el->el_msg)
3299			free(el->el_msg);
3300		free(el);
3301	}
3302
3303	return (cleared);
3304}
3305
3306/*
3307 * Clean up upon an error in get_exportlist().
3308 */
3309void
3310export_error_cleanup(struct expfs *xf)
3311{
3312	if (xf && (xf->xf_flag & XF_LINKED) == 0)
3313		free_expfs(xf);
3314}
3315
3316/*
3317 * Search the export list for a matching fs.
3318 */
3319struct expfs *
3320ex_search(u_char *uuid)
3321{
3322	struct expfs *xf;
3323
3324	TAILQ_FOREACH(xf, &xfshead, xf_next) {
3325		if (!bcmp(xf->xf_uuid, uuid, 16))
3326			return (xf);
3327	}
3328	return (xf);
3329}
3330
3331/*
3332 * add a directory to a dirlist (sorted)
3333 *
3334 * Note that the list is sorted to place subdirectories
3335 * after the entry for their matching parent directory.
3336 * This isn't strictly sorted because other directories may
3337 * have similar names with characters that sort lower than '/'.
3338 * For example: /export /export.test /export/subdir
3339 */
3340int
3341add_dir(struct dirlist **dlpp, char *cp)
3342{
3343	struct dirlist *newdl, *dl, *dl2, *dl3, *dlstop;
3344	int cplen, dlen, cmp;
3345
3346	dlstop = NULL;
3347	dl2 = NULL;
3348	dl = *dlpp;
3349	cplen = strlen(cp);
3350
3351	while (dl && (dl != dlstop)) {
3352		dlen = strlen(dl->dl_dir);
3353		cmp = strncmp(cp, dl->dl_dir, dlen);
3354		DEBUG(3, "add_dir: %s compare %s %d", cp, dl->dl_dir, cmp);
3355		if (cmp < 0)
3356			break;
3357		if (cmp == 0) {
3358			if (cplen == dlen) /* duplicate */
3359				return (EEXIST);
3360			if (cp[dlen] == '/') {
3361				/*
3362				 * Find the next entry that isn't a
3363				 * subdirectory of this directory so
3364				 * we know when to stop looking for
3365				 * the insertion point.
3366				 */
3367				DEBUG(3, "add_dir: %s compare %s %d subdir match",
3368					cp, dl->dl_dir, cmp);
3369				dlstop = dl->dl_next;
3370				while (dlstop && (subdir_check(dl->dl_dir, dlstop->dl_dir) == 1))
3371					dlstop = dlstop->dl_next;
3372			} else {
3373				/*
3374				 * The new dir should go after this directory and
3375				 * its subdirectories.  So, skip subdirs of this dir.
3376				 */
3377				DEBUG(3, "add_dir: %s compare %s %d partial match",
3378					cp, dl->dl_dir, cmp);
3379				dl3 = dl;
3380				dl2 = dl;
3381				dl = dl->dl_next;
3382				while (dl && (subdir_check(dl3->dl_dir, dl->dl_dir) == 1)) {
3383					dl2 = dl;
3384					dl = dl->dl_next;
3385					}
3386				continue;
3387			}
3388		}
3389		dl2 = dl;
3390		dl = dl->dl_next;
3391	}
3392	if (dl && (dl == dlstop))
3393		DEBUG(3, "add_dir: %s stopped before %s", cp, dlstop->dl_dir);
3394	newdl = malloc(sizeof(*dl));
3395	if (newdl == NULL) {
3396		log(LOG_ERR, "can't allocate memory to add dir %s", cp);
3397		return (ENOMEM);
3398	}
3399	newdl->dl_dir = cp;
3400	if (dl2 == NULL) {
3401		newdl->dl_next = *dlpp;
3402		*dlpp = newdl;
3403	} else {
3404		newdl->dl_next = dl;
3405		dl2->dl_next = newdl;
3406	}
3407	if (config.verbose >= 6) {
3408		dl = *dlpp;
3409		while (dl) {
3410			DEBUG(4, "DIRLIST: %s", dl->dl_dir);
3411			dl = dl->dl_next;
3412		}
3413	}
3414	return (0);
3415}
3416
3417/*
3418 * free up all the elements in a dirlist
3419 */
3420void
3421free_dirlist(struct dirlist *dl)
3422{
3423	struct dirlist *dl2;
3424
3425	while (dl) {
3426		dl2 = dl->dl_next;
3427		if (dl->dl_dir)
3428			free(dl->dl_dir);
3429		free(dl);
3430		dl = dl2;
3431	}
3432}
3433
3434/*
3435 * add a name to a namelist
3436 *
3437 * returns 0 on success, -1 on duplicate, or error
3438 */
3439int
3440add_name(struct namelisttqh *names, char *name)
3441{
3442	struct namelist *nl;
3443
3444	TAILQ_FOREACH(nl, names, nl_next)
3445		if (!strcmp(nl->nl_name, name))
3446			return (-1);
3447	nl = malloc(sizeof(*nl));
3448	if (!nl)
3449		return (ENOMEM);
3450	nl->nl_name = strdup(name);
3451	if (!nl->nl_name) {
3452		free(nl);
3453		return (ENOMEM);
3454	}
3455	TAILQ_INSERT_TAIL(names, nl, nl_next);
3456	return (0);
3457}
3458
3459/*
3460 * free up all the elements in a namelist
3461 */
3462void
3463free_namelist(struct namelisttqh *names)
3464{
3465	struct namelist *nl, *nlnext;
3466
3467	TAILQ_FOREACH_SAFE(nl, names, nl_next, nlnext) {
3468		TAILQ_REMOVE(names, nl, nl_next);
3469		if (nl->nl_name)
3470			free(nl->nl_name);
3471		free(nl);
3472	}
3473}
3474
3475/*
3476 * find a host list entry that has the same group
3477 */
3478struct host *
3479find_group_match_in_host_list(struct hosttqh *head, struct grouplist *grp)
3480{
3481	struct host *ht;
3482
3483	TAILQ_FOREACH(ht, head, ht_next)
3484		if (!grpcmp(grp, ht->ht_grp))
3485			break;
3486	return (ht);
3487}
3488
3489/*
3490 * find a host list entry that has the same address
3491 */
3492struct host *
3493find_group_address_match_in_host_list(struct hosttqh *head, struct grouplist *grp)
3494{
3495	struct host *ht;
3496	struct netmsk *n1, *n2;
3497	struct addrinfo *a1, *a2;
3498	int i;
3499
3500	switch (grp->gr_type) {
3501	case GT_HOST:
3502		a1 = grp->gr_u.gt_hostinfo.h_ailist;
3503		while (a1) {
3504			TAILQ_FOREACH(ht, head, ht_next) {
3505				if (ht->ht_grp->gr_type != GT_HOST)
3506					continue;
3507				if (ht->ht_flags & OP_DEL)
3508					continue;
3509				a2 = ht->ht_grp->gr_u.gt_hostinfo.h_ailist;
3510				while (a2) {
3511					if (!addrinfo_cmp(a1, a2))
3512						return (ht);
3513					a2 = a2->ai_next;
3514				}
3515			}
3516			a1 = a1->ai_next;
3517		}
3518		break;
3519	case GT_NET:
3520		n1 = &grp->gr_u.gt_net;
3521		TAILQ_FOREACH(ht, head, ht_next) {
3522			if (ht->ht_grp->gr_type != GT_NET)
3523				continue;
3524			if (ht->ht_flags & OP_DEL)
3525				continue;
3526			n2 = &ht->ht_grp->gr_u.gt_net;
3527			if (n1->nt_family != n2->nt_family)
3528				continue;
3529			if (n1->nt_family == AF_INET) {
3530				in_addr_t ina1, ina2;
3531				ina1 = n1->nt_net & n1->nt_mask;
3532				ina2 = n2->nt_net & n2->nt_mask;
3533				if (ina1 == ina2)
3534					return (ht);
3535			} else if (n1->nt_family == AF_INET6) {
3536				struct in6_addr ina1, ina2;
3537				for (i=0; i < (int)sizeof(ina1.s6_addr); i++) {
3538					ina1.s6_addr[i] = n1->nt_net6.s6_addr[i] & n1->nt_mask6.s6_addr[i];
3539					ina2.s6_addr[i] = n2->nt_net6.s6_addr[i] & n2->nt_mask6.s6_addr[i];
3540				}
3541				if (!bcmp(&ina1, &ina2, sizeof(ina1)))
3542					return (ht);
3543			}
3544		}
3545		break;
3546	}
3547
3548	return (NULL);
3549}
3550
3551/*
3552 * compare two credentials
3553 */
3554int
3555crcmp(struct xucred *cr1, struct xucred *cr2)
3556{
3557	int i;
3558
3559	if (cr1 == cr2)
3560		return 0;
3561	if (cr1 == NULL || cr2 == NULL)
3562		return 1;
3563	if (cr1->cr_uid != cr2->cr_uid)
3564		return 1;
3565	if (cr1->cr_ngroups != cr2->cr_ngroups)
3566		return 1;
3567	/* XXX assumes groups will always be listed in some order */
3568	for (i=0; i < cr1->cr_ngroups; i++)
3569		if (cr1->cr_groups[i] != cr2->cr_groups[i])
3570			return 1;
3571	return (0);
3572}
3573
3574/*
3575 * tentatively hang export options for a list of groups off of an exported directory
3576 */
3577int
3578hang_options_setup(struct expdir *xd, int opt_flags, struct xucred *cr, struct grouplist *grp,
3579		   struct nfs_sec *secflavs, int *need_export)
3580{
3581	struct host *ht;
3582
3583	*need_export = 0;
3584
3585	if (!grp) {
3586		/* default export */
3587		if (xd->xd_flags & OP_DEFEXP) {
3588			/* exported directory already has default export! */
3589			if ((OP_EXOPTS(xd->xd_flags) == OP_EXOPTS(opt_flags)) &&
3590			    (!(opt_flags & (OP_MAPALL|OP_MAPROOT)) || !crcmp(&xd->xd_cred, cr)) &&
3591			    (!cmp_secflavs(&xd->xd_sec, secflavs))) {
3592				if (!(xd->xd_flags & OP_DEL))
3593					log(LOG_WARNING, "duplicate default export for %s", xd->xd_dir);
3594				xd->xd_flags &= ~OP_EXOPTMASK;
3595				xd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD;
3596				return (0);
3597			} else if (!(xd->xd_flags & OP_DEL)) {
3598				log(LOG_ERR, "multiple/conflicting default exports for %s", xd->xd_dir);
3599				return (EEXIST);
3600			}
3601		}
3602		xd->xd_flags &= ~OP_EXOPTMASK;
3603		xd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD;
3604		if (cr)
3605			xd->xd_cred = *cr;
3606		bcopy(secflavs, &xd->xd_sec, sizeof(struct nfs_sec));
3607		DEBUG(3, "hang_options_setup: %s default 0x%x", xd->xd_dir, xd->xd_flags);
3608		*need_export = 1;
3609		return (0);
3610	}
3611
3612	while (grp) {
3613		/* first check for an existing entry for this group */
3614		ht = find_group_match_in_host_list(&xd->xd_hosts, grp);
3615		if (ht) {
3616			/* found a match... */
3617			if ((OP_EXOPTS(ht->ht_flags) == OP_EXOPTS(opt_flags)) &&
3618			    (!(opt_flags & (OP_MAPALL|OP_MAPROOT)) || !crcmp(&ht->ht_cred, cr)) &&
3619			    (!cmp_secflavs(&ht->ht_sec, secflavs))) {
3620				/* options match, OK, it's the same export */
3621				if (!(ht->ht_flags & OP_ADD) && !(grp->gr_flags & GF_SHOW))
3622					ht->ht_flags &= ~OP_SHOW;
3623				ht->ht_flags |= OP_ADD;
3624				if (grp->gr_flags & GF_SHOW)
3625					ht->ht_flags |= OP_SHOW;
3626				grp = grp->gr_next;
3627				continue;
3628			}
3629			/* options don't match... */
3630			if (!(ht->ht_flags & OP_DEL)) {
3631				/* this is a new entry, so this is a conflict */
3632				log(LOG_ERR, "conflicting exports for %s, %s", xd->xd_dir, grp_name(grp));
3633				return (EEXIST);
3634			}
3635			/* this entry is marked for deletion, so there is no conflict */
3636			/* go ahead and add a new entry with the new options */
3637		}
3638		/* also check for an existing entry for any addresses in this group */
3639		ht = find_group_address_match_in_host_list(&xd->xd_hosts, grp);
3640		if (ht) {
3641			/* found a match... */
3642			if ((OP_EXOPTS(ht->ht_flags) != OP_EXOPTS(opt_flags)) ||
3643			    ((opt_flags & (OP_MAPALL|OP_MAPROOT)) && crcmp(&ht->ht_cred, cr)) ||
3644			    (cmp_secflavs(&ht->ht_sec, secflavs))) {
3645				/* ...with different options */
3646				log(LOG_ERR, "conflicting exports for %s, %s", xd->xd_dir, grp_name(grp));
3647				return (EEXIST);
3648			}
3649			/* ... with same options */
3650			log(LOG_WARNING, "duplicate export for %s, %s vs. %s", xd->xd_dir,
3651				grp_name(grp), grp_name(ht->ht_grp));
3652			grp = grp->gr_next;
3653			continue;
3654		}
3655		/* OK to add a new host */
3656		ht = get_host();
3657		if (!ht) {
3658			log(LOG_ERR, "Can't allocate memory for host: %s", grp_name(grp));
3659			return(ENOMEM);
3660		}
3661		ht->ht_flags = opt_flags | OP_ADD;
3662		ht->ht_cred = *cr;
3663		ht->ht_grp = grp;
3664		grp->gr_refcnt++;
3665		bcopy(secflavs, &ht->ht_sec, sizeof(struct nfs_sec));
3666		if (grp->gr_flags & GF_SHOW)
3667			ht->ht_flags |= OP_SHOW;
3668		if (config.verbose >= 6)
3669			DEBUG(4, "grp2host: %p %d %s %s", grp, grp->gr_refcnt, grp_name(grp), grp_addr(grp));
3670		if (!add_host(&xd->xd_hosts, ht)) {
3671			/* This shouldn't happen given the above checks */
3672			log(LOG_ERR, "duplicate host in export list: %s", grp_name(grp));
3673			free_host(ht);
3674			return (EEXIST);
3675		}
3676		*need_export = 1;
3677		DEBUG(3, "hang_options_setup: %s %s 0x%x", xd->xd_dir, grp_name(grp), opt_flags);
3678		grp = grp->gr_next;
3679	}
3680
3681	return (0);
3682}
3683
3684/*
3685 * make permanent the export options added via hang_options_setup()
3686 */
3687void
3688hang_options_finalize(struct expdir *xd)
3689{
3690	struct host *ht;
3691	struct expdir *mxd;
3692
3693	if (xd->xd_flags & OP_ADD) {
3694		xd->xd_iflags |= (xd->xd_flags & (OP_OFFLINE|OP_MISSING)) ? OP_OFFLINE : OP_ONLINE;
3695		merge_secflavs(&xd->xd_ssec, &xd->xd_sec);
3696		xd->xd_flags &= ~(OP_ADD|OP_DEL);
3697		/* update old options in case subsequent export fails */
3698		xd->xd_oflags = xd->xd_flags;
3699		xd->xd_ocred = xd->xd_cred;
3700		bcopy(&xd->xd_sec, &xd->xd_osec, sizeof(struct nfs_sec));
3701	}
3702
3703	TAILQ_FOREACH(ht, &xd->xd_hosts, ht_next) {
3704		if (!(ht->ht_flags & OP_ADD))
3705			continue;
3706		ht->ht_flags &= ~(OP_ADD|OP_DEL);
3707		xd->xd_iflags |= (ht->ht_flags & (OP_OFFLINE|OP_MISSING)) ? OP_OFFLINE : OP_ONLINE;
3708		merge_secflavs(&xd->xd_ssec, &ht->ht_sec);
3709	}
3710
3711	TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) {
3712		hang_options_finalize(mxd);
3713	}
3714}
3715
3716/*
3717 * cleanup/undo the export options added via hang_options_setup()
3718 */
3719void
3720hang_options_cleanup(struct expdir *xd)
3721{
3722	struct host *ht, *htnext;
3723
3724	if (xd->xd_flags & OP_ADD) {
3725		xd->xd_flags = xd->xd_oflags;
3726		xd->xd_cred = xd->xd_ocred;
3727		bcopy(&xd->xd_osec, &xd->xd_sec, sizeof(struct nfs_sec));
3728	}
3729
3730	TAILQ_FOREACH_SAFE(ht, &xd->xd_hosts, ht_next, htnext) {
3731		if (!(ht->ht_flags & OP_ADD))
3732			continue;
3733		if (ht->ht_flags & OP_DEL) {
3734			ht->ht_flags &= ~OP_ADD;
3735			continue;
3736		}
3737		TAILQ_REMOVE(&xd->xd_hosts, ht, ht_next);
3738		free_host(ht);
3739	}
3740
3741	/*
3742	 * Note: currently cleanup isn't called after handling mountdirs,
3743	 * so we don't have to bother cleaning up any of the mountdirs.
3744	 */
3745}
3746
3747/*
3748 * hang export options for mountable subdirectories of an exported directory
3749 */
3750int
3751hang_options_mountdir(struct expdir *xd, char *dir, int opt_flags, struct grouplist *grp, struct nfs_sec *secflavs)
3752{
3753	struct host *ht;
3754	struct expdir *mxd, *mxd2, *mxd3;
3755	int cmp;
3756
3757	/* check for existing mountdir */
3758	mxd = mxd3 = NULL;
3759	TAILQ_FOREACH(mxd2, &xd->xd_mountdirs, xd_next) {
3760		cmp = strcmp(dir, mxd2->xd_dir);
3761		if (!cmp) {
3762			/* found it */
3763			mxd = mxd2;
3764			break;
3765		} else if (cmp < 0) {
3766			/* found where it needs to be inserted */
3767			break;
3768		}
3769		mxd3 = mxd2;
3770	}
3771	if (!mxd) {
3772		mxd = get_expdir();
3773		if (mxd)
3774			mxd->xd_dir = strdup(dir);
3775		if (!mxd || !mxd->xd_dir) {
3776			if (mxd)
3777				free_expdir(mxd);
3778			log(LOG_ERR, "can't allocate memory for mountable sub-directory; %s", dir);
3779			return (ENOMEM);
3780		}
3781		if (mxd3) {
3782			TAILQ_INSERT_AFTER(&xd->xd_mountdirs, mxd3, mxd, xd_next);
3783		} else {
3784			TAILQ_INSERT_HEAD(&xd->xd_mountdirs, mxd, xd_next);
3785		}
3786	}
3787
3788	if (!grp) {
3789		/* default export */
3790		if (mxd->xd_flags & OP_DEFEXP) {
3791			/* exported directory already has default export! */
3792			if ((OP_EXOPTS(mxd->xd_flags) == OP_EXOPTS(opt_flags)) &&
3793			   (!cmp_secflavs(&mxd->xd_sec, secflavs))) {
3794				if (!(mxd->xd_flags & OP_DEL))
3795					log(LOG_WARNING, "duplicate default export for %s", mxd->xd_dir);
3796				mxd->xd_flags &= ~OP_EXOPTMASK;
3797				mxd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD;
3798				return (0);
3799			} else if (!(mxd->xd_flags & OP_DEL)) {
3800				log(LOG_ERR, "multiple/conflicting default exports for %s", mxd->xd_dir);
3801				return (EEXIST);
3802			}
3803		}
3804		mxd->xd_flags &= ~OP_EXOPTMASK;
3805		mxd->xd_flags |= opt_flags | OP_DEFEXP | OP_ADD;
3806		bcopy(secflavs, &mxd->xd_sec, sizeof(struct nfs_sec));
3807		DEBUG(3, "hang_options_mountdir: %s default 0x%x",
3808			mxd->xd_dir, mxd->xd_flags);
3809		return (0);
3810	}
3811
3812	while (grp) {
3813		/* first check for an existing entry for this group */
3814		ht = find_group_match_in_host_list(&mxd->xd_hosts, grp);
3815		if (ht) {
3816			/* found a match... */
3817			if ((OP_EXOPTS(ht->ht_flags) == OP_EXOPTS(opt_flags)) &&
3818			   (!cmp_secflavs(&ht->ht_sec, secflavs))) {
3819				/* options match, OK, it's the same export */
3820				ht->ht_flags |= OP_ADD;
3821				grp = grp->gr_next;
3822				continue;
3823			}
3824			/* options don't match... */
3825			if (!(ht->ht_flags & OP_DEL)) {
3826				/* this is a new entry, so this is a conflict */
3827				log(LOG_ERR, "conflicting mountdir exports for %s, %s",
3828					mxd->xd_dir, grp_name(grp));
3829				return (EEXIST);
3830			}
3831			/* this entry is marked for deletion, so there is no conflict */
3832			/* go ahead and add a new entry with the new options */
3833		}
3834		/* also check for an existing entry for any addresses in this group */
3835		ht = find_group_address_match_in_host_list(&mxd->xd_hosts, grp);
3836		if (ht) {
3837			/* found a match... */
3838			if ((OP_EXOPTS(ht->ht_flags) != OP_EXOPTS(opt_flags)) ||
3839			   (cmp_secflavs(&ht->ht_sec, secflavs))) {
3840				/* ...with different options */
3841				log(LOG_ERR, "conflicting mountdir exports for %s, %s",
3842					mxd->xd_dir, grp_name(grp));
3843				return (EEXIST);
3844			}
3845			/* ... with same options */
3846			log(LOG_WARNING, "duplicate mountdir export for %s, %s vs. %s",
3847				mxd->xd_dir, grp_name(grp), grp_name(ht->ht_grp));
3848			grp = grp->gr_next;
3849			continue;
3850		}
3851		/* OK to add a new host */
3852		ht = get_host();
3853		if (!ht) {
3854			log(LOG_ERR, "Can't allocate memory for host: %s", grp_name(grp));
3855			return(ENOMEM);
3856		}
3857		ht->ht_flags = opt_flags | OP_ADD;
3858		ht->ht_grp = grp;
3859		grp->gr_refcnt++;
3860		bcopy(secflavs, &ht->ht_sec, sizeof(struct nfs_sec));
3861		if (grp->gr_flags & GF_SHOW)
3862			ht->ht_flags |= OP_SHOW;
3863		if (config.verbose >= 6)
3864			DEBUG(4, "grp2host: %p %d %s %s", grp, grp->gr_refcnt, grp_name(grp), grp_addr(grp));
3865		if (!add_host(&mxd->xd_hosts, ht)) {
3866			/* This shouldn't happen given the above checks */
3867			log(LOG_ERR, "Can't add host to mountdir export list: %s", grp_name(grp));
3868			free_host(ht);
3869			return (EEXIST);
3870		}
3871		DEBUG(3, "hang_options_mountdir: %s %s 0x%x",
3872			mxd->xd_dir, grp_name(grp), opt_flags);
3873		grp = grp->gr_next;
3874	}
3875
3876	return (0);
3877}
3878
3879/*
3880 * Search for an exported directory on an exported file system that
3881 * a given host can mount and return the export options.
3882 *
3883 * Search order:
3884 * an exact match on exported directory path
3885 * an exact match on exported directory mountdir path
3886 * a subdir match on exported directory mountdir path with ALLDIRS
3887 * a subdir match on exported directory path with ALLDIRS
3888 */
3889int
3890expdir_search(struct expfs *xf, char *dirpath, struct sockaddr *sa, int *options, struct nfs_sec *secflavs)
3891{
3892	struct expdir *xd, *mxd;
3893	struct host *hp;
3894	int cmp = 1, chkalldirs = 0;
3895
3896	TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) {
3897		if ((cmp = subdir_check(xd->xd_dir, dirpath)) >= 0)
3898			break;
3899	}
3900	if (!xd) {
3901		DEBUG(1, "expdir_search: no matching export: %s", dirpath);
3902		return (0);
3903	}
3904
3905	DEBUG(1, "expdir_search: %s -> %s", dirpath, xd->xd_dir);
3906
3907	if (cmp == 0) {
3908		/* exact match on exported directory path */
3909check_xd_hosts:
3910		/* find options for this host */
3911		hp = find_host(&xd->xd_hosts, sa);
3912		if (hp && (!chkalldirs || (hp->ht_flags & OP_ALLDIRS))) {
3913			DEBUG(2, "expdir_search: %s host %s", dirpath,
3914				(chkalldirs ? "alldirs" : "match"));
3915			*options = hp->ht_flags;
3916			bcopy(&hp->ht_sec, secflavs, sizeof(struct nfs_sec));
3917		} else if ((xd->xd_flags & OP_DEFEXP) &&
3918		           (!chkalldirs || (xd->xd_flags & OP_ALLDIRS))) {
3919			DEBUG(2, "expdir_search: %s defexp %s",
3920				dirpath, (chkalldirs ? "alldirs" : "match"));
3921			*options = xd->xd_flags;
3922			bcopy(&xd->xd_sec, secflavs, sizeof(struct nfs_sec));
3923		} else {
3924			/* not exported to this host */
3925			*options = 0;
3926			DEBUG(2, "expdir_search: %s NO match", dirpath);
3927			return (0);
3928		}
3929		return (1);
3930	}
3931
3932	/* search for a matching mountdir */
3933	TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) {
3934		cmp = subdir_check(mxd->xd_dir, dirpath);
3935		if (cmp < 0)
3936			continue;
3937		DEBUG(1, "expdir_search: %s subdir path match %s",
3938			dirpath, mxd->xd_dir);
3939		chkalldirs = (cmp != 0);
3940		/* found a match on a mountdir */
3941		hp = find_host(&mxd->xd_hosts, sa);
3942		if (hp && (!chkalldirs || (hp->ht_flags & OP_ALLDIRS))) {
3943			DEBUG(2, "expdir_search: %s -> %s subdir host %s",
3944				dirpath, mxd->xd_dir, (chkalldirs ? "alldirs" : "match"));
3945			*options = hp->ht_flags;
3946			bcopy(&hp->ht_sec, secflavs, sizeof(struct nfs_sec));
3947			return (1);
3948		} else if ((mxd->xd_flags & OP_DEFEXP) &&
3949			   (!chkalldirs || (mxd->xd_flags & OP_ALLDIRS))) {
3950			DEBUG(2, "expdir_search: %s -> %s subdir defexp %s",
3951				dirpath, mxd->xd_dir, (chkalldirs ? "alldirs" : "match"));
3952			*options = mxd->xd_flags;
3953			bcopy(&mxd->xd_sec, secflavs, sizeof(struct nfs_sec));
3954			return (1);
3955		}
3956		/* not exported to this host */
3957	}
3958
3959	DEBUG(1, "expdir_search: %s NO match, check alldirs", dirpath);
3960	chkalldirs = 1;
3961	goto check_xd_hosts;
3962}
3963
3964/*
3965 * search a host list for a match for the given address
3966 */
3967struct host *
3968find_host(struct hosttqh *head, struct sockaddr *sa)
3969{
3970	struct host *hp;
3971	struct grouplist *grp;
3972	struct addrinfo *ai;
3973	int i;
3974
3975	TAILQ_FOREACH(hp, head, ht_next) {
3976		grp = hp->ht_grp;
3977		switch (grp->gr_type) {
3978		case GT_HOST:
3979			for (ai = grp->gr_u.gt_hostinfo.h_ailist; ai; ai = ai->ai_next) {
3980				if (ai->ai_family != sa->sa_family)
3981					continue;
3982				if (ai->ai_addrlen != sa->sa_len)
3983					continue;
3984				if (ai->ai_family == AF_INET) {
3985					struct sockaddr_in *sin1, *sin2;
3986					sin1 = (struct sockaddr_in*) AOK ai->ai_addr;
3987					sin2 = (struct sockaddr_in*) AOK sa;
3988					if (!bcmp(&sin1->sin_addr, &sin2->sin_addr, sizeof(sin1->sin_addr)))
3989						return (hp);
3990				} else if (ai->ai_family == AF_INET6) {
3991					struct sockaddr_in6 *sin1, *sin2;
3992					sin1 = (struct sockaddr_in6*) AOK ai->ai_addr;
3993					sin2 = (struct sockaddr_in6*) AOK sa;
3994					if (!bcmp(&sin1->sin6_addr, &sin2->sin6_addr, sizeof(sin1->sin6_addr)))
3995						return (hp);
3996				}
3997			}
3998			break;
3999		case GT_NET:
4000			if (grp->gr_u.gt_net.nt_family != sa->sa_family)
4001				break;
4002			if (grp->gr_u.gt_net.nt_family == AF_INET) {
4003				in_addr_t ina = ((struct sockaddr_in*) AOK sa)->sin_addr.s_addr;
4004				if ((ina & grp->gr_u.gt_net.nt_mask) == grp->gr_u.gt_net.nt_net)
4005					return (hp);
4006			} else if (grp->gr_u.gt_net.nt_family == AF_INET6) {
4007				struct sockaddr_in6 *sa6 = (struct sockaddr_in6*) AOK sa;
4008				struct in6_addr ina;
4009				for (i=0; i < (int)sizeof(ina.s6_addr); i++)
4010					ina.s6_addr[i] = sa6->sin6_addr.s6_addr[i] & grp->gr_u.gt_net.nt_mask6.s6_addr[i];
4011				if (!bcmp(&ina, &grp->gr_u.gt_net.nt_net6, sizeof(ina)))
4012					return (hp);
4013			}
4014			break;
4015		}
4016	}
4017
4018	return (NULL);
4019}
4020
4021/*
4022 * Parse the option string and update fields.
4023 * Option arguments may either be -<option>=<value> or
4024 * -<option> <value>
4025 */
4026int
4027do_opt( char **cpp,
4028	char **endcpp,
4029	struct grouplist *ngrp,
4030	int *hostcountp,
4031	int *opt_flagsp,
4032	int *exflagsp,
4033	struct xucred *cr,
4034	struct nfs_sec *sec_flavs,
4035	char *fspath,
4036	u_char *fsuuid)
4037{
4038	char *cpoptarg = NULL, *cpoptend = NULL;
4039	char *cp, *endcp, *cpopt, *cpu, savedc, savedc2 = '\0', savedc3 = '\0';
4040	int mapallflag, usedarg;
4041	int i, rv = 0;
4042	size_t len;
4043
4044	cpopt = *cpp;
4045	cpopt++;
4046	cp = *endcpp;
4047	savedc = *cp;
4048	*cp = '\0';
4049	while (cpopt && *cpopt) {
4050		mapallflag = 1;
4051		usedarg = -2;
4052		if (NULL != (cpoptend = strchr(cpopt, ','))) {
4053			*cpoptend++ = '\0';
4054			if (NULL != (cpoptarg = strchr(cpopt, '='))) {
4055				savedc3 = *cpoptarg;
4056				*cpoptarg++ = '\0';
4057			}
4058		} else {
4059			if (NULL != (cpoptarg = strchr(cpopt, '='))) {
4060				savedc3 = *cpoptarg;
4061				*cpoptarg++ = '\0';
4062			} else {
4063				*cp = savedc;
4064				nextfield(&cp, &endcp);
4065				**endcpp = '\0';
4066				if (endcp > cp && *cp != '-') {
4067					cpoptarg = cp;
4068					savedc2 = *endcp;
4069					*endcp = '\0';
4070					usedarg = 0;
4071				}
4072			}
4073		}
4074		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
4075			*exflagsp |= NX_READONLY;
4076			*opt_flagsp |= OP_READONLY;
4077		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
4078			!(mapallflag = strcmp(cpopt, "mapall")) ||
4079			!strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
4080			usedarg++;
4081			rv = parsecred(cpoptarg, cr);
4082			if (rv) {
4083				log(LOG_ERR, "map credential error");
4084				goto out;
4085			} else if (mapallflag == 0) {
4086				*exflagsp |= NX_MAPALL;
4087				*opt_flagsp |= OP_MAPALL;
4088			} else {
4089				*exflagsp |= NX_MAPROOT;
4090				*opt_flagsp |= OP_MAPROOT;
4091			}
4092		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
4093			!strcmp(cpopt, "m"))) {
4094			if (*opt_flagsp & OP_MASK) {
4095				log(LOG_ERR, "Network option conflict");
4096				rv = 1;
4097				goto out;
4098			}
4099			if (get_net(cpoptarg, &ngrp->gr_u.gt_net, 1)) {
4100				log(LOG_ERR, "Bad mask: %s", cpoptarg);
4101				rv = 1;
4102				goto out;
4103			}
4104			usedarg++;
4105			*opt_flagsp |= OP_MASK;
4106		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
4107			!strcmp(cpopt, "n"))) {
4108			if (*opt_flagsp & OP_NET) {
4109				log(LOG_ERR, "Network option conflict");
4110				rv = 1;
4111				goto out;
4112			}
4113			if (get_net(cpoptarg, &ngrp->gr_u.gt_net, 0)) {
4114				log(LOG_ERR, "Bad net: %s", cpoptarg);
4115				rv = 1;
4116				goto out;
4117			}
4118			ngrp->gr_type = GT_NET;
4119			*hostcountp = *hostcountp + 1;
4120			usedarg++;
4121			*opt_flagsp |= OP_NET;
4122		} else if (cpoptarg && (!strcmp(cpopt, "sec"))) {
4123			if (*opt_flagsp & OP_SECFLAV)
4124				log(LOG_WARNING, "A security option was already specified and will be replaced.");
4125			if (get_sec_flavors(cpoptarg, sec_flavs)) {
4126				log(LOG_ERR, "Bad security option: %s", cpoptarg);
4127				rv = 1;
4128				goto out;
4129			}
4130			usedarg++;
4131			*opt_flagsp |= OP_SECFLAV;
4132		} else if (!strcmp(cpopt, "alldirs")) {
4133			*opt_flagsp |= OP_ALLDIRS;
4134		} else if (!strcmp(cpopt, "32bitclients")) {
4135			*exflagsp |= NX_32BITCLIENTS;
4136			*opt_flagsp |= OP_32BITCLIENTS;
4137		} else if (!strcmp(cpopt, "manglednames")) {
4138			*exflagsp |= NX_MANGLEDNAMES;
4139			*opt_flagsp |= OP_MANGLEDNAMES;
4140		} else if (!strcmp(cpopt, "fspath")) {
4141			if (!cpoptarg) {
4142				log(LOG_WARNING, "export option '%s' missing a value.", cpopt);
4143			} else if (cpoptarg[0] != '/') {
4144				log(LOG_ERR, "invalid fspath: %s", cpoptarg);
4145				rv = 1;
4146				goto out;
4147			} else {
4148				len = strlcpy(fspath, cpoptarg, MAXPATHLEN);
4149				if (len >= MAXPATHLEN) {
4150					log(LOG_ERR, "%s option path too long: %s", cpopt, cpoptarg);
4151					rv = 1;
4152					goto out;
4153				}
4154				*opt_flagsp |= OP_FSPATH;
4155			}
4156		} else if (!strcmp(cpopt, "fsuuid")) {
4157			if (!cpoptarg) {
4158				log(LOG_WARNING, "export option '%s' missing a value.", cpopt);
4159			} else {
4160				cpu = cpoptarg;
4161				for (i=0; i < 16; i++, cpu+=2) {
4162					if (*cpu == '-')
4163						cpu++;
4164					if (!isxdigit(*cpu) || !isxdigit(*(cpu+1))) {
4165						log(LOG_ERR, "invalid fsuuid: %s", cpoptarg);
4166						rv = 1;
4167						goto out;
4168					}
4169					fsuuid[i] = HEXSTRTOI(cpu);
4170				}
4171				*opt_flagsp |= OP_FSUUID;
4172			}
4173		} else if (!strcmp(cpopt, "offline")) {
4174			*exflagsp |= NX_OFFLINE;
4175			*opt_flagsp |= OP_OFFLINE;
4176		} else {
4177			log(LOG_WARNING, "unrecognized export option: %s", cpopt);
4178			goto out;
4179		}
4180		if (usedarg >= 0) {
4181			*endcp = savedc2;
4182			**endcpp = savedc;
4183			if (usedarg > 0) {
4184				*cpp = cp;
4185				*endcpp = endcp;
4186			}
4187			return (0);
4188		}
4189		if (cpoptend)
4190			*(cpoptend-1) = ',';
4191		if (savedc3) {
4192			*(cpoptarg-1) = savedc3;
4193			savedc3 = '\0';
4194		}
4195		cpopt = cpoptend;
4196	}
4197out:
4198	if (savedc2)
4199		*endcp = savedc2;
4200	if (cpoptend)
4201		*(cpoptend-1) = ',';
4202	if (savedc3)
4203		*(cpoptarg-1) = savedc3;
4204	**endcpp = savedc;
4205	return (rv);
4206}
4207
4208/*
4209 * Translate a character string to the corresponding list of network
4210 * addresses for a hostname.
4211 */
4212int
4213get_host_addresses(char *cp, struct grouplist *grp)
4214{
4215	struct addrinfo *ailist, aihints;
4216
4217	if (grp->gr_type != GT_NULL)
4218		return (1);
4219
4220	bzero(&aihints, sizeof(aihints));
4221	aihints.ai_socktype = SOCK_STREAM;
4222	if (getaddrinfo(cp, NULL, &aihints, &ailist)) {
4223		log(LOG_ERR, "getaddrinfo() failed for %s", cp);
4224		hostnamecount++;
4225		return (1);
4226	}
4227	hostnamecount++;
4228	hostnamegoodcount++;
4229	grp->gr_type = GT_HOST;
4230	grp->gr_u.gt_hostinfo.h_ailist = ailist;
4231	grp->gr_u.gt_hostinfo.h_name = strdup(cp);
4232	return (0);
4233}
4234
4235
4236/*
4237 * Free up an exported directory structure
4238 */
4239void
4240free_expdir(struct expdir *xd)
4241{
4242	struct expdir *xd2;
4243
4244	free_hosts(&xd->xd_hosts);
4245	while ((xd2 = TAILQ_FIRST(&xd->xd_mountdirs))) {
4246		TAILQ_REMOVE(&xd->xd_mountdirs, xd2, xd_next);
4247		free_expdir(xd2);
4248	}
4249	if (xd->xd_dir)
4250		free(xd->xd_dir);
4251	free(xd);
4252}
4253
4254/*
4255 * Free up an exportfs structure
4256 */
4257void
4258free_expfs(struct expfs *xf)
4259{
4260	struct expdir *xd;
4261
4262	while ((xd = TAILQ_FIRST(&xf->xf_dirl))) {
4263		TAILQ_REMOVE(&xf->xf_dirl, xd, xd_next);
4264		free_expdir(xd);
4265	}
4266	if (xf->xf_fsdir)
4267		free(xf->xf_fsdir);
4268	free(xf);
4269}
4270
4271/*
4272 * Free up a host.
4273 */
4274void
4275free_host(struct host *hp)
4276{
4277	if (hp->ht_grp)
4278		free_grp(hp->ht_grp);
4279	free(hp);
4280}
4281
4282/*
4283 * Free up a list of hosts.
4284 */
4285void
4286free_hosts(struct hosttqh *head)
4287{
4288	struct host *hp, *hp2;
4289
4290	TAILQ_FOREACH_SAFE(hp, head, ht_next, hp2) {
4291		TAILQ_REMOVE(head, hp, ht_next);
4292		free_host(hp);
4293	}
4294}
4295
4296/*
4297 * Allocate a host structure
4298 */
4299struct host *
4300get_host(void)
4301{
4302	struct host *hp;
4303
4304	hp = malloc(sizeof(struct host));
4305	if (hp == NULL)
4306		return (NULL);
4307	hp->ht_flags = 0;
4308	return (hp);
4309}
4310
4311/*
4312 * Do the NFSSVC_EXPORT syscall to push the export info into the kernel.
4313 */
4314int
4315do_export(
4316	int expcmd,
4317	struct expfs *xf,
4318	struct expdir *xd,
4319	struct grouplist *grplist,
4320	int exflags,
4321	struct xucred *cr,
4322	struct nfs_sec *secflavs)
4323{
4324	struct nfs_export_args nxa;
4325	struct nfs_export_net_args *netargs, *na;
4326	struct grouplist *grp;
4327	struct addrinfo *ai;
4328	struct sockaddr_in *sin, *imask;
4329	struct sockaddr_in6 *sin6, *imask6;
4330	uint32_t net;
4331
4332	nxa.nxa_flags = expcmd;
4333	if ((exflags & NX_OFFLINE) && (expcmd != NXA_CHECK))
4334		nxa.nxa_flags |= NXA_OFFLINE;
4335	nxa.nxa_fsid = xf->xf_fsid;
4336	nxa.nxa_fspath = xf->xf_fsdir;
4337	nxa.nxa_exppath = xd->xd_xid->xid_path;
4338	nxa.nxa_expid = xd->xd_xid->xid_id;
4339
4340	/* first, count the number of hosts/nets we're pushing in for this export */
4341	/* !grplist => default export */
4342	nxa.nxa_netcount = (!grplist ? 1 : 0);
4343	grp = grplist;
4344	while (grp) {
4345		if (grp->gr_type == GT_HOST) {
4346			/* count # addresses given for this host */
4347			for (ai = grp->gr_u.gt_hostinfo.h_ailist; ai; ai = ai->ai_next)
4348				if ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6))
4349					nxa.nxa_netcount++;
4350		} else if (grp->gr_type == GT_NET) {
4351			nxa.nxa_netcount++;
4352		}
4353		grp = grp->gr_next;
4354	}
4355
4356	netargs = malloc(nxa.nxa_netcount * sizeof(struct nfs_export_net_args));
4357	if (!netargs) {
4358		/* XXX we could possibly fall back to pushing them in, one-by-one */
4359		log(LOG_ERR, "do_export(): malloc failed for %d net args", nxa.nxa_netcount);
4360		return (1);
4361	}
4362	nxa.nxa_nets = netargs;
4363
4364#define INIT_NETARG(N, F) \
4365	do { \
4366		(N)->nxna_flags = exflags; \
4367		(N)->nxna_cred = cr ? *cr : def_anon; \
4368		memset(&(N)->nxna_sec, 0, sizeof(struct nfs_sec)); \
4369		if (secflavs != NULL) \
4370			bcopy(secflavs, &(N)->nxna_sec, sizeof(struct nfs_sec)); \
4371		if ((F) == AF_INET) { \
4372			sin = (struct sockaddr_in*)&(N)->nxna_addr; \
4373			imask = (struct sockaddr_in*)&(N)->nxna_mask; \
4374			memset(sin, 0, sizeof(*sin)); \
4375			memset(imask, 0, sizeof(*imask)); \
4376			sin->sin_family = AF_INET; \
4377			sin->sin_len = sizeof(*sin); \
4378			imask->sin_family = AF_INET; \
4379			imask->sin_len = sizeof(*imask); \
4380		} else if ((F) == AF_INET6) { \
4381			sin6 = (struct sockaddr_in6*)&(N)->nxna_addr; \
4382			imask6 = (struct sockaddr_in6*)&(N)->nxna_mask; \
4383			memset(sin6, 0, sizeof(*sin6)); \
4384			memset(imask6, 0, sizeof(*imask6)); \
4385			sin6->sin6_family = AF_INET6; \
4386			sin6->sin6_len = sizeof(*sin6); \
4387			imask6->sin6_family = AF_INET6; \
4388			imask6->sin6_len = sizeof(*imask6); \
4389		} \
4390	} while (0)
4391
4392	na = netargs;
4393	if (!grplist) {
4394		/* default export, no address */
4395		INIT_NETARG(na, AF_INET);
4396		sin->sin_len = 0;
4397		imask->sin_len = 0;
4398		na++;
4399	}
4400	grp = grplist;
4401	while (grp) {
4402		switch (grp->gr_type) {
4403		case GT_HOST:
4404			/* handle each host address in the list */
4405			for (ai = grp->gr_u.gt_hostinfo.h_ailist; ai; ai = ai->ai_next) {
4406				if ((ai->ai_family != AF_INET) && (ai->ai_family != AF_INET6))
4407					continue;
4408				INIT_NETARG(na, ai->ai_family);
4409				if (ai->ai_family == AF_INET) {
4410					sin->sin_addr.s_addr = ((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr.s_addr;
4411					imask->sin_len = 0;
4412				} else if (ai->ai_family == AF_INET6) {
4413					bcopy(&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr, &sin6->sin6_addr,
4414						sizeof(sin6->sin6_addr));
4415					imask6->sin6_len = 0;
4416				}
4417				na++;
4418			}
4419			break;
4420		case GT_NET:
4421			INIT_NETARG(na, grp->gr_u.gt_net.nt_family);
4422			if (grp->gr_u.gt_net.nt_family == AF_INET) {
4423				if (grp->gr_u.gt_net.nt_mask)
4424				    imask->sin_addr.s_addr = grp->gr_u.gt_net.nt_mask;
4425				else {
4426				    net = ntohl(grp->gr_u.gt_net.nt_net);
4427				    if (IN_CLASSA(net))
4428					imask->sin_addr.s_addr = inet_addr("255.0.0.0");
4429				    else if (IN_CLASSB(net))
4430					imask->sin_addr.s_addr =
4431					    inet_addr("255.255.0.0");
4432				    else
4433					imask->sin_addr.s_addr =
4434					    inet_addr("255.255.255.0");
4435				    grp->gr_u.gt_net.nt_mask = imask->sin_addr.s_addr;
4436				}
4437				sin->sin_addr.s_addr = grp->gr_u.gt_net.nt_net;
4438				na++;
4439			} else if (grp->gr_u.gt_net.nt_family == AF_INET6) {
4440				bcopy(&grp->gr_u.gt_net.nt_net6, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
4441				bcopy(&grp->gr_u.gt_net.nt_mask6, &imask6->sin6_addr, sizeof(imask6->sin6_addr));
4442				na++;
4443			}
4444			break;
4445		case GT_NETGROUP:
4446			break;
4447		default:
4448			log(LOG_ERR, "Bad grouptype");
4449			free(netargs);
4450			return (1);
4451		}
4452
4453		grp = grp->gr_next;
4454	}
4455
4456	if (nfssvc(NFSSVC_EXPORT, &nxa)) {
4457		if ((expcmd != NXA_CHECK) && (expcmd != NXA_DELETE) && (errno == EPERM)) {
4458			log(LOG_ERR, "Can't change attributes for %s.  See 'exports' man page.",
4459				xd->xd_dir);
4460			free(netargs);
4461			return (1);
4462		}
4463		log(LOG_ERR, "Can't %sexport %s: %s (%d)",
4464			(expcmd == NXA_DELETE) ? "un" : "",
4465			xd->xd_dir, strerror(errno), errno);
4466		free(netargs);
4467		return (1);
4468	}
4469
4470	free(netargs);
4471	return (0);
4472}
4473
4474/*
4475 * Translate a net address.
4476 */
4477int
4478get_net(char *cp, struct netmsk *net, int maskflg)
4479{
4480	struct netent *np;
4481	uint32_t netaddr;
4482	struct in_addr inetaddr, inetaddr2;
4483	struct in6_addr inet6addr;
4484	const char *name;
4485	sa_family_t family;
4486	char addrbuf[2*INET6_ADDRSTRLEN];
4487
4488	if (NULL != (np = getnetbyname(cp))) {
4489		inetaddr = inet_makeaddr(np->n_net, 0);
4490		family = AF_INET;
4491	} else if (inet_pton(AF_INET6, cp, &inet6addr) == 1) {
4492		family = AF_INET6;
4493	} else if (isdigit(*cp)) {
4494		if ((netaddr = inet_network(cp)) == INADDR_NONE)
4495			return (1);
4496		inetaddr = inet_makeaddr(netaddr, 0);
4497		family = AF_INET;
4498		/*
4499		 * Due to arbritrary subnet masks, you don't know how many
4500		 * bits to shift the address to make it into a network,
4501		 * however you do know how to make a network address into
4502		 * a host with host == 0 and then compare them.
4503		 * (What a pest)
4504		 */
4505		if (!maskflg) {
4506			setnetent(0);
4507			while (NULL != (np = getnetent())) {
4508				inetaddr2 = inet_makeaddr(np->n_net, 0);
4509				if (inetaddr2.s_addr == inetaddr.s_addr)
4510					break;
4511			}
4512			endnetent();
4513		}
4514	} else {
4515		return (1);
4516	}
4517	if (maskflg) {
4518		if (net->nt_family == AF_UNSPEC) {
4519			net->nt_family = family;
4520		} else if (net->nt_family != family) {
4521			log(LOG_ERR, "net mask family (%d) does not match net address family (%d): %s", family, net->nt_family, cp);
4522			return (1);
4523		}
4524		if (family == AF_INET6)
4525			net->nt_mask6 = inet6addr;
4526		else
4527			net->nt_mask = inetaddr.s_addr;
4528	} else {
4529		if (net->nt_family == AF_UNSPEC) {
4530			net->nt_family = family;
4531		} else if (net->nt_family != family) {
4532			log(LOG_ERR, "net mask family (%d) does not match net address family (%d): %s", net->nt_family, family, cp);
4533			return (1);
4534		}
4535		if (np) {
4536			name = np->n_name;
4537		} else if (family == AF_INET6) {
4538			name = inet_ntop(AF_INET6, &inet6addr, addrbuf, sizeof(addrbuf));
4539			if (!name) {
4540				log(LOG_ERR, "can't convert IPv6 addr to name: %s", cp);
4541				return (1);
4542			}
4543		} else {
4544			name = inet_ntoa(inetaddr);
4545		}
4546		net->nt_name = strdup(name);
4547		if (net->nt_name == NULL) {
4548			log(LOG_ERR, "can't allocate memory for net: %s", cp);
4549			return (1);
4550		}
4551		if (family == AF_INET6)
4552			net->nt_net6 = inet6addr;
4553		else
4554			net->nt_net = inetaddr.s_addr;
4555		DEBUG(3, "got net: %s", net->nt_name);
4556	}
4557	return (0);
4558}
4559
4560/*
4561 * Parse security flavors
4562 */
4563int
4564get_sec_flavors(char *flavorlist, struct nfs_sec *sec_flavs)
4565{
4566	char *flavorlistcopy;
4567	char *flavor;
4568	u_int32_t flav_bits;
4569
4570#define SYS_BIT   0x00000001
4571#define KRB5_BIT  0x00000002
4572#define KRB5I_BIT 0x00000004
4573#define KRB5P_BIT 0x00000008
4574
4575	/* try to make a copy of the string so we don't butcher the original */
4576	flavorlistcopy = strdup(flavorlist);
4577	if (flavorlistcopy)
4578		flavorlist = flavorlistcopy;
4579
4580	bzero(sec_flavs, sizeof(struct nfs_sec));
4581	flav_bits = 0;
4582	while ( ((flavor = strsep(&flavorlist, ":")) != NULL) && (sec_flavs->count < NX_MAX_SEC_FLAVORS)) {
4583		if (flavor[0] == '\0')
4584			continue;
4585		if (!strcmp("krb5p", flavor)) {
4586			if (flav_bits & KRB5P_BIT) {
4587				log(LOG_WARNING, "krb5p appears more than once: %s", flavorlist);
4588				continue;
4589			}
4590			flav_bits |= KRB5P_BIT;
4591			sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5P;
4592		} else if (!strcmp("krb5i", flavor)) {
4593			if (flav_bits & KRB5I_BIT) {
4594				log(LOG_WARNING, "krb5i appears more than once: %s", flavorlist);
4595				continue;
4596			}
4597			flav_bits |= KRB5I_BIT;
4598			sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5I;
4599		} else if (!strcmp("krb5", flavor)) {
4600			if (flav_bits & KRB5_BIT) {
4601				log(LOG_WARNING, "krb5 appears more than once: %s", flavorlist);
4602				continue;
4603			}
4604			flav_bits |= KRB5_BIT;
4605			sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_KRB5;
4606		} else if (!strcmp("sys", flavor)) {
4607			if (flav_bits & SYS_BIT) {
4608				log(LOG_WARNING, "Security mechanism 'sys' appears more than once: %s", flavorlist);
4609				continue;
4610			}
4611			flav_bits |= SYS_BIT;
4612			sec_flavs->flavors[sec_flavs->count++] = RPCAUTH_SYS;
4613		} else {
4614			log(LOG_ERR, "Unknown security mechanism '%s'.  See the exports(5) man page.", flavor);
4615			bzero(sec_flavs, sizeof(struct nfs_sec));
4616			break;
4617		}
4618	}
4619
4620	if (flavorlistcopy)
4621		free(flavorlistcopy);
4622
4623	if (sec_flavs->count)
4624		return 0;
4625	else
4626		return 1;
4627}
4628
4629/*
4630 * Compare two security flavor structs
4631 */
4632int
4633cmp_secflavs(struct nfs_sec *sf1, struct nfs_sec *sf2)
4634{
4635	int32_t i;
4636
4637	if (sf1->count != sf2->count)
4638		return 1;
4639	for (i = 0; i < sf1->count; i++)
4640		if (sf1->flavors[i] != sf2->flavors[i])
4641			return 1;
4642	return 0;
4643}
4644
4645/*
4646 * merge new security flavors into a current set
4647 */
4648void
4649merge_secflavs(struct nfs_sec *cur, struct nfs_sec *new)
4650{
4651	int32_t i, j;
4652
4653	for (i = 0; i < new->count; i++) {
4654		for (j = 0; j < cur->count; j++)
4655			if (new->flavors[i] == cur->flavors[j])
4656				break;
4657		if (j < cur->count)
4658			continue;
4659		if (cur->count < NX_MAX_SEC_FLAVORS) {
4660			cur->flavors[j] = new->flavors[i];
4661			cur->count++;
4662		}
4663	}
4664}
4665
4666/*
4667 * Find the next field in a line.
4668 * Fields are separated by white space.
4669 * Space, tab, and quote characters may be escaped.
4670 * Quoted strings are not broken at white space.
4671 */
4672void
4673nextfield(char **line_start, char **line_end)
4674{
4675	char *a, q;
4676	u_int32_t esc;
4677
4678	if (line_start == NULL)
4679		return;
4680	a = *line_start;
4681
4682	/* Skip white space */
4683	while (*a == ' ' || *a == '\t')
4684		a++;
4685	*line_start = a;
4686
4687	/* Stop at end of line */
4688	if (*a == '\n' || *a == '\0') {
4689		*line_end = a;
4690		return;
4691	}
4692
4693	/* Check for single or double quote */
4694	if (*a == '\'' || *a == '"') {
4695		q = *a;
4696		a++;
4697		for (esc = 0; *a != '\0'; a++) {
4698			if (esc)
4699				esc = 0;
4700			else if (*a == '\\')
4701				esc = 1;
4702			else if (*a == q || *a == '\n')
4703				break;
4704		}
4705		if (*a == q)
4706			a++;
4707		*line_end = a;
4708		return;
4709	}
4710
4711	/* Skip to next non-escaped white space or end of line */
4712	for (;; a++) {
4713		if (*a == '\0' || *a == '\n')
4714			break;
4715		else if (*a == '\\') {
4716			a++;
4717			if (*a == '\n' || *a == '\0')
4718				break;
4719		} else if (*a == ' ' || *a == '\t')
4720			break;
4721	}
4722
4723	*line_end = a;
4724}
4725
4726/*
4727 * Get an exports file line. Skip over blank lines and handle line continuations.
4728 * (char *line is a global)
4729 */
4730int
4731get_export_entry(void)
4732{
4733	char *p, *cp, *newline;
4734	size_t len, totlen;
4735	int cont_line;
4736
4737	if (linenum == 0)
4738		linenum = 1;
4739	else
4740		linenum += entrylines;
4741	entrylines = 1;
4742
4743	/*
4744	 * Loop around ignoring blank lines and getting all continuation lines.
4745	 */
4746	totlen = 0;
4747	do {
4748		if ((p = fgetln(exp_file, &len)) == NULL)
4749			return (0);
4750		cp = p + len - 1;
4751		cont_line = 0;
4752		while (cp >= p &&
4753		       (*cp == ' ' || *cp == '\t' || *cp == '\n' ||
4754			*cp == '\\')) {
4755			if (*cp == '\\')
4756				cont_line = 1;
4757			cp--;
4758			len--;
4759		}
4760		if (linesize < (totlen + len + 1)) {
4761			newline = realloc(line, (totlen + len + 1));
4762			if (!newline) {
4763				log(LOG_ERR, "Exports line too long, can't allocate memory");
4764				return (0);
4765			}
4766			line = newline;
4767			linesize = (totlen + len + 1);
4768		}
4769		memcpy(line + totlen, p, len);
4770		totlen += len;
4771		line[totlen] = '\0';
4772		if (cont_line) {
4773			entrylines++;
4774		} else if (totlen == 0) {
4775			linenum += entrylines;
4776			entrylines = 1;
4777		}
4778	} while (totlen == 0 || cont_line);
4779	return (1);
4780}
4781
4782/*
4783 * Parse a description of a credential.
4784 */
4785int
4786parsecred(char *namelist, struct xucred *cr)
4787{
4788	char *namelistcopy;
4789	char *name;
4790	int cnt;
4791	char *names;
4792	struct passwd *pw;
4793	struct group *gr;
4794	int ngroups, groups[NGROUPS];
4795
4796	/* try to make a copy of the string so we don't butcher the original */
4797	namelistcopy = strdup(namelist);
4798	if (namelistcopy)
4799		namelist = namelistcopy;
4800
4801	/*
4802	 * Set up the unpriviledged user.
4803	 */
4804	cr->cr_version = XUCRED_VERSION;
4805	cr->cr_uid = -2;
4806	cr->cr_groups[0] = -2;
4807	cr->cr_ngroups = 1;
4808	/*
4809	 * Get the user's password table entry.
4810	 */
4811	names = strsep(&namelist, " \t\n");
4812	name = strsep(&names, ":");
4813	if (isdigit(*name) || *name == '-')
4814		pw = getpwuid(atoi(name));
4815	else
4816		pw = getpwnam(name);
4817	/*
4818	 * Credentials specified as those of a user.
4819	 */
4820	if (names == NULL) {
4821		if (pw == NULL) {
4822			log(LOG_ERR, "Unknown user: %s", name);
4823			if (namelistcopy)
4824				free(namelistcopy);
4825			return (ENOENT);
4826		}
4827		cr->cr_uid = pw->pw_uid;
4828		ngroups = NGROUPS;
4829		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
4830			log(LOG_NOTICE, "Too many groups for %s", pw->pw_name);
4831		/* Convert from int's to gid_t's */
4832		cr->cr_ngroups = (ngroups <= NGROUPS) ? ngroups : NGROUPS;
4833		for (cnt = 0; cnt < cr->cr_ngroups; cnt++)
4834			cr->cr_groups[cnt] = groups[cnt];
4835		if (namelistcopy)
4836			free(namelistcopy);
4837		goto out;
4838	}
4839	/*
4840	 * Explicit credential specified as a colon separated list:
4841	 *	uid:gid:gid:...
4842	 */
4843	if (pw != NULL)
4844		cr->cr_uid = pw->pw_uid;
4845	else if (isdigit(*name) || *name == '-')
4846		cr->cr_uid = atoi(name);
4847	else {
4848		log(LOG_ERR, "Unknown user: %s", name);
4849		if (namelistcopy)
4850			free(namelistcopy);
4851		return (ENOENT);
4852	}
4853	cr->cr_ngroups = 0;
4854	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
4855		name = strsep(&names, ":");
4856		if (isdigit(*name) || *name == '-') {
4857			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
4858		} else {
4859			if ((gr = getgrnam(name)) == NULL) {
4860				log(LOG_ERR, "Unknown group: %s", name);
4861				continue;
4862			}
4863			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
4864		}
4865	}
4866	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
4867		log(LOG_ERR, "Too many groups in %s", namelist);
4868	if (namelistcopy)
4869		free(namelistcopy);
4870out:
4871	if (config.verbose >= 5) {
4872		char buf[256];
4873		snprintf_cred(buf, sizeof(buf), cr);
4874		DEBUG(3, "got cred: %s", buf);
4875	}
4876	if (cr->cr_ngroups < 1) {
4877		log(LOG_ERR, "no groups found: %s", namelist);
4878		return (EINVAL);
4879	}
4880	return (0);
4881}
4882
4883#define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
4884/*
4885 * Routines that maintain the remote mounttab
4886 */
4887void
4888get_mountlist(void)
4889{
4890	struct mountlist *mlp, *lastmlp;
4891	char *host, *dir, *cp;
4892	char str[STRSIZ];
4893	FILE *mlfile;
4894	int hlen, dlen;
4895
4896	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
4897		if (errno != ENOENT)
4898			log(LOG_ERR, "Can't open %s: %s (%d)",
4899			    _PATH_RMOUNTLIST, strerror(errno), errno);
4900		else
4901			DEBUG(1, "Can't open %s: %s (%d)",
4902			    _PATH_RMOUNTLIST, strerror(errno), errno);
4903		return;
4904	}
4905	lastmlp = NULL;
4906	while (fgets(str, STRSIZ, mlfile) != NULL) {
4907		cp = str;
4908		host = strsep(&cp, " \t\n");
4909		dir = strsep(&cp, " \t\n");
4910		if ((host == NULL) || (dir == NULL))
4911			continue;
4912		hlen = strlen(host);
4913		if (hlen > RPCMNT_NAMELEN)
4914			hlen = RPCMNT_NAMELEN;
4915		dlen = strlen(dir);
4916		if (dlen > RPCMNT_PATHLEN)
4917			dlen = RPCMNT_PATHLEN;
4918		mlp = malloc(sizeof(*mlp));
4919		if (mlp) {
4920			mlp->ml_host = malloc(hlen+1);
4921			mlp->ml_dir = malloc(dlen+1);
4922		}
4923		if (!mlp || !mlp->ml_host || !mlp->ml_dir) {
4924			log(LOG_ERR, "can't allocate memory while reading in mount list: %s %s",
4925				host, dir);
4926			if (mlp) {
4927				if (mlp->ml_host)
4928					free(mlp->ml_host);
4929				if (mlp->ml_dir)
4930					free(mlp->ml_dir);
4931				free(mlp);
4932			}
4933			break;
4934		}
4935		strncpy(mlp->ml_host, host, hlen);
4936		mlp->ml_host[hlen] = '\0';
4937		strncpy(mlp->ml_dir, dir, dlen);
4938		mlp->ml_dir[dlen] = '\0';
4939		mlp->ml_next = NULL;
4940		if (lastmlp)
4941			lastmlp->ml_next = mlp;
4942		else
4943			mlhead = mlp;
4944		lastmlp = mlp;
4945	}
4946	fclose(mlfile);
4947}
4948
4949void
4950del_mlist(char *host, char *dir)
4951{
4952	struct mountlist *mlp, **mlpp;
4953	struct mountlist *mlp2;
4954	FILE *mlfile;
4955	int fnd = 0;
4956
4957	mlpp = &mlhead;
4958	mlp = mlhead;
4959	while (mlp) {
4960		if (!strcmp(mlp->ml_host, host) &&
4961		    (!dir || !strcmp(mlp->ml_dir, dir))) {
4962			fnd = 1;
4963			mlp2 = mlp;
4964			*mlpp = mlp = mlp->ml_next;
4965			free(mlp2->ml_host);
4966			free(mlp2->ml_dir);
4967			free(mlp2);
4968		} else {
4969			mlpp = &mlp->ml_next;
4970			mlp = mlp->ml_next;
4971		}
4972	}
4973	if (fnd) {
4974		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
4975			log(LOG_ERR, "Can't write %s: %s (%d)",
4976			    _PATH_RMOUNTLIST, strerror(errno), errno);
4977			return;
4978		}
4979		mlp = mlhead;
4980		while (mlp) {
4981			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dir);
4982			mlp = mlp->ml_next;
4983		}
4984		fclose(mlfile);
4985	}
4986}
4987
4988void
4989add_mlist(char *host, char *dir)
4990{
4991	struct mountlist *mlp, **mlpp;
4992	FILE *mlfile;
4993	int hlen, dlen;
4994
4995	mlpp = &mlhead;
4996	mlp = mlhead;
4997	while (mlp) {
4998		if (!strcmp(mlp->ml_host, host) && !strcmp(mlp->ml_dir, dir))
4999			return;
5000		mlpp = &mlp->ml_next;
5001		mlp = mlp->ml_next;
5002	}
5003
5004	hlen = strlen(host);
5005	if (hlen > RPCMNT_NAMELEN)
5006		hlen = RPCMNT_NAMELEN;
5007	dlen = strlen(dir);
5008	if (dlen > RPCMNT_PATHLEN)
5009		dlen = RPCMNT_PATHLEN;
5010
5011	mlp = malloc(sizeof(*mlp));
5012	if (mlp) {
5013		mlp->ml_host = malloc(hlen+1);
5014		mlp->ml_dir = malloc(dlen+1);
5015	}
5016	if (!mlp || !mlp->ml_host || !mlp->ml_dir) {
5017		if (mlp) {
5018			if (mlp->ml_host)
5019				free(mlp->ml_host);
5020			if (mlp->ml_dir)
5021				free(mlp->ml_dir);
5022			free(mlp);
5023		}
5024		log(LOG_ERR, "can't allocate memory to add to mount list: %s %s", host, dir);
5025		return;
5026	}
5027	strncpy(mlp->ml_host, host, hlen);
5028	mlp->ml_host[hlen] = '\0';
5029	strncpy(mlp->ml_dir, dir, dlen);
5030	mlp->ml_dir[dlen] = '\0';
5031	mlp->ml_next = NULL;
5032	*mlpp = mlp;
5033	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
5034		log(LOG_ERR, "Can't append %s: %s (%d)",
5035		    _PATH_RMOUNTLIST, strerror(errno), errno);
5036		return;
5037	}
5038	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dir);
5039	fclose(mlfile);
5040}
5041
5042/*
5043 * Check options for consistency.
5044 */
5045int
5046check_options(int opt_flags)
5047{
5048	if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL)) {
5049		log(LOG_ERR, "-mapall and -maproot mutually exclusive");
5050		return (1);
5051	}
5052	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
5053		log(LOG_ERR, "-mask requires -net");
5054		return (1);
5055	}
5056	return (0);
5057}
5058
5059/*
5060 * Check an absolute directory path for any symbolic links. Return true
5061 * if no symbolic links are found.
5062 */
5063int
5064check_dirpath(char *dir)
5065{
5066	char *cp;
5067	int ret = 1;
5068	struct stat sb;
5069
5070	for (cp = dir + 1; *cp && ret; cp++)
5071		if (*cp == '/') {
5072			*cp = '\0';
5073			if ((lstat(dir, &sb) < 0) || !S_ISDIR(sb.st_mode))
5074				ret = 0;
5075			*cp = '/';
5076		}
5077	if (ret && ((lstat(dir, &sb) < 0) || !S_ISDIR(sb.st_mode)))
5078		ret = 0;
5079	return (ret);
5080}
5081
5082void
5083snprintf_cred(char *buf, int buflen, struct xucred *cr)
5084{
5085	char crbuf2[32];
5086	int i;
5087
5088	buf[0] = '\0';
5089	if (!cr)
5090		return;
5091	snprintf(crbuf2, sizeof(crbuf2), "%d", cr->cr_uid);
5092	strlcat(buf, crbuf2, buflen);
5093	for (i=0; i < cr->cr_ngroups; i++) {
5094		snprintf(crbuf2, sizeof(crbuf2), ":%d", cr->cr_groups[i]);
5095		strlcat(buf, crbuf2, buflen);
5096	}
5097}
5098
5099void
5100snprintf_flags(char *buf, int buflen, int flags, struct xucred *cr)
5101{
5102	char crbuf[256];
5103
5104	if (flags & (OP_MAPALL|OP_MAPROOT))
5105		snprintf_cred(crbuf, sizeof(crbuf), cr);
5106	else
5107		crbuf[0] = '\0';
5108
5109	snprintf(buf, buflen, "FLAGS: 0x%08x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s %s",
5110		flags,
5111		(flags & OP_DEL) ? " DEL" : "",
5112		(flags & OP_ADD) ? " ADD" : "",
5113		(flags & OP_DEFEXP) ? " DEFEXP" : "",
5114		(flags & OP_MISSING) ? " MISSING" : "",
5115		(flags & OP_SHOW) ? " SHOW" : "",
5116		(flags & OP_ONLINE) ? " ONLINE" : "",
5117		(flags & OP_OFFLINE) ? " OFFLINE" : "",
5118		(flags & OP_FSUUID) ? " FSUUID" : "",
5119		(flags & OP_FSPATH) ? " FSPATH" : "",
5120		(flags & OP_32BITCLIENTS) ? " 32" : "",
5121		(flags & OP_READONLY) ? " READONLY" : "",
5122		(flags & OP_ALLDIRS) ? " ALLDIRS" : "",
5123		(flags & OP_NET) ? " NET" : "",
5124		(flags & OP_MASK) ? " MASK" : "",
5125		(flags & OP_SECFLAV) ? " SEC" : "",
5126		(flags & OP_MAPALL) ? " MAPALL" : "",
5127		(flags & OP_MAPROOT) ? " MAPROOT" : "",
5128		crbuf);
5129}
5130
5131void
5132dump_expdir(struct expfs *xf, struct expdir *xd, int mdir)
5133{
5134	struct expdir *mxd;
5135	struct host *ht;
5136	struct grouplist *gr;
5137	char buf[2048];
5138	struct addrinfo *ai;
5139	void *sinaddr;
5140	char addrbuf[2*INET6_ADDRSTRLEN];
5141	const char *s;
5142
5143	snprintf_flags(buf, sizeof(buf), xd->xd_iflags, NULL);
5144	DEBUG(1, "   %s  %s", xd->xd_dir, buf);
5145	if (!mdir) {
5146		DEBUG(1, "   %s/%s", xf->xf_fsdir, xd->xd_xid->xid_path);
5147		DEBUG(1, "   XID: 0x%08x", xd->xd_xid->xid_id);
5148	}
5149	snprintf_flags(buf, sizeof(buf), xd->xd_flags, mdir ? NULL : &xd->xd_cred);
5150	DEBUG(1, "   %s", buf);
5151	DEBUG(1, "   HOSTS:");
5152
5153	TAILQ_FOREACH(ht, &xd->xd_hosts, ht_next) {
5154		snprintf_flags(buf, sizeof(buf), ht->ht_flags, mdir ? NULL : &ht->ht_cred);
5155		DEBUG(1, "      * %s", buf);
5156		gr = ht->ht_grp;
5157		if (gr) {
5158			switch(gr->gr_type) {
5159			case GT_NET:
5160				sinaddr = (gr->gr_u.gt_net.nt_family == AF_INET) ?
5161					(void*)&gr->gr_u.gt_net.nt_net : (void*)&gr->gr_u.gt_net.nt_net6;
5162				if (inet_ntop(gr->gr_u.gt_net.nt_family, sinaddr, addrbuf, sizeof(addrbuf)))
5163					s = addrbuf;
5164				else
5165					s = "???";
5166				snprintf(buf, sizeof(buf), "%s %s/", grp_name(gr), s);
5167				sinaddr = (gr->gr_u.gt_net.nt_family == AF_INET) ?
5168					(void*)&gr->gr_u.gt_net.nt_mask : (void*)&gr->gr_u.gt_net.nt_mask6;
5169				if (inet_ntop(gr->gr_u.gt_net.nt_family, sinaddr, addrbuf, sizeof(addrbuf)))
5170					s = addrbuf;
5171				else
5172					s = "???";
5173				strlcat(buf, s, sizeof(buf));
5174				break;
5175			case GT_HOST:
5176				ai = gr->gr_u.gt_hostinfo.h_ailist;
5177				sinaddr = (ai->ai_family == AF_INET) ?
5178					(void*)&((struct sockaddr_in*) AOK ai->ai_addr)->sin_addr :
5179					(void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr;
5180				if (inet_ntop(ai->ai_family, sinaddr, addrbuf, sizeof(addrbuf)))
5181					s = addrbuf;
5182				else
5183					s = "???";
5184				snprintf(buf, sizeof(buf), "%s %s", grp_name(gr), s);
5185				ai = ai->ai_next;
5186				while (ai) {
5187					strlcat(buf, " ", sizeof(buf));
5188					sinaddr = (ai->ai_family == AF_INET) ?
5189						(void*)&((struct sockaddr_in*)  AOK ai->ai_addr)->sin_addr :
5190						(void*)&((struct sockaddr_in6*) AOK ai->ai_addr)->sin6_addr;
5191					if (inet_ntop(ai->ai_family, sinaddr, addrbuf, sizeof(addrbuf)))
5192						s = addrbuf;
5193					else
5194						s = "???";
5195					if (strlcat(buf, s, sizeof(buf)) > 2000) {
5196						strlcat(buf, " ...", sizeof(buf));
5197						break;
5198					}
5199					ai = ai->ai_next;
5200				}
5201				break;
5202			default:
5203				snprintf(buf, sizeof(buf), "%s", grp_name(gr));
5204				break;
5205			}
5206			DEBUG(1, "        %s", buf);
5207		} /* ht_grp */
5208	} /* for xd_hosts list */
5209
5210	if (mdir)
5211		return;
5212
5213	DEBUG(1, "   MOUNTDIRS:");
5214	TAILQ_FOREACH(mxd, &xd->xd_mountdirs, xd_next) {
5215		dump_expdir(xf, mxd, 1);
5216	}
5217}
5218
5219void
5220dump_exports(void)
5221{
5222	struct expfs *xf;
5223	struct expdir *xd;
5224	char buf[64];
5225
5226	if (!config.verbose)
5227		return;
5228
5229	TAILQ_FOREACH(xf, &xfshead, xf_next) {
5230		DEBUG(1, "** %s %s (0x%08x)", xf->xf_fsdir,
5231			uuidstring(xf->xf_uuid, buf), UUID2FSID(xf->xf_uuid));
5232		TAILQ_FOREACH(xd, &xf->xf_dirl, xd_next) {
5233			dump_expdir(xf, xd, 0);
5234		}
5235	}
5236}
5237
5238/*
5239 * code to monitor list of mounts
5240 */
5241static struct statfs *sfs[2];
5242static int size[2], cnt[2], cur, lastfscnt;
5243#define PREV	((cur + 1) & 1)
5244
5245static int
5246sfscmp(const void *arg1, const void *arg2)
5247{
5248	const struct statfs *sfs1 = arg1;
5249	const struct statfs *sfs2 = arg2;
5250	return strcmp(sfs1->f_mntonname, sfs2->f_mntonname);
5251}
5252
5253static void
5254get_mounts(void)
5255{
5256	cur = (cur + 1) % 2;
5257	while (size[cur] < (lastfscnt = getfsstat(sfs[cur], size[cur] * sizeof(struct statfs), MNT_NOWAIT))) {
5258		free(sfs[cur]);
5259		size[cur] = lastfscnt + 32;
5260		sfs[cur] = malloc(size[cur] * sizeof(struct statfs));
5261		if (!sfs[cur])
5262			err(1, "no memory");
5263	}
5264	cnt[cur] = lastfscnt;
5265	qsort(sfs[cur], cnt[cur], sizeof(struct statfs), sfscmp);
5266}
5267
5268static int
5269check_xpaths(char *path)
5270{
5271	struct dirlist *dirl = xpaths;
5272
5273	while (dirl) {
5274		if (subdir_check(path, dirl->dl_dir) >= 0) {
5275			DEBUG(1, "check_for_mount_changes: %s %s\n", path, dirl->dl_dir);
5276			return (1);
5277		}
5278		dirl = dirl->dl_next;
5279	}
5280
5281	return (0);
5282}
5283
5284int
5285check_for_mount_changes(void)
5286{
5287	int i, j, cmp, gotmount = 0;
5288
5289#define RETURN_IF_DONE	do { if (gotmount && (config.verbose < 3)) return (gotmount); } while (0)
5290
5291	get_mounts();
5292
5293	if (!xpaths_complete) {
5294		DEBUG(1, "check_for_mount_changes: xpaths not complete\n");
5295		return (1);
5296	}
5297
5298	for (i=j=0; (i < cnt[PREV]) && (j < cnt[cur]); ) {
5299		cmp = sfscmp(&sfs[PREV][i], &sfs[cur][j]);
5300		if (!cmp) {
5301			i++;
5302			j++;
5303			continue;
5304		}
5305		if (cmp < 0) {
5306			DEBUG(1, "- %s\n", sfs[PREV][i].f_mntonname);
5307			gotmount |= check_xpaths(sfs[PREV][i].f_mntonname);
5308			RETURN_IF_DONE;
5309			i++;
5310		}
5311		if (cmp > 0) {
5312			DEBUG(1, "+ %s\n", sfs[cur][j].f_mntonname);
5313			gotmount |= check_xpaths(sfs[cur][j].f_mntonname);
5314			RETURN_IF_DONE;
5315			j++;
5316		}
5317	}
5318	while (i < cnt[PREV]) {
5319		DEBUG(1, "- %s\n", sfs[PREV][i].f_mntonname);
5320		gotmount |= check_xpaths(sfs[PREV][i].f_mntonname);
5321		RETURN_IF_DONE;
5322		i++;
5323	}
5324	while (j < cnt[cur]) {
5325		DEBUG(1, "+ %s\n", sfs[cur][j].f_mntonname);
5326		gotmount |= check_xpaths(sfs[cur][j].f_mntonname);
5327		RETURN_IF_DONE;
5328		j++;
5329	}
5330
5331	return (gotmount);
5332}
5333
5334