]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mountd/mountd.c
MFV: r363292
[FreeBSD/FreeBSD.git] / usr.sbin / mountd / mountd.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1989, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Herb Hasler and Rick Macklem at The University of Guelph.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1989, 1993\n\
38         The Regents of the University of California.  All rights reserved.\n";
39 #endif /*not lint*/
40
41 #if 0
42 #ifndef lint
43 static char sccsid[] = "@(#)mountd.c    8.15 (Berkeley) 5/1/95";
44 #endif /*not lint*/
45 #endif
46
47 #include <sys/cdefs.h>
48 __FBSDID("$FreeBSD$");
49
50 #include <sys/param.h>
51 #include <sys/conf.h>
52 #include <sys/fcntl.h>
53 #include <sys/fnv_hash.h>
54 #include <sys/linker.h>
55 #include <sys/module.h>
56 #include <sys/mount.h>
57 #include <sys/queue.h>
58 #include <sys/stat.h>
59 #include <sys/sysctl.h>
60 #include <sys/syslog.h>
61
62 #include <rpc/rpc.h>
63 #include <rpc/rpc_com.h>
64 #include <rpc/pmap_clnt.h>
65 #include <rpc/pmap_prot.h>
66 #include <rpcsvc/mount.h>
67 #include <nfs/nfsproto.h>
68 #include <nfs/nfssvc.h>
69 #include <nfsserver/nfs.h>
70
71 #include <fs/nfs/nfsport.h>
72
73 #include <arpa/inet.h>
74
75 #include <ctype.h>
76 #include <err.h>
77 #include <errno.h>
78 #include <grp.h>
79 #include <libutil.h>
80 #include <limits.h>
81 #include <netdb.h>
82 #include <pwd.h>
83 #include <signal.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include "pathnames.h"
89 #include "mntopts.h"
90
91 #ifdef DEBUG
92 #include <stdarg.h>
93 #endif
94
95 /*
96  * Structures for keeping the mount list and export list
97  */
98 struct mountlist {
99         char    ml_host[MNTNAMLEN+1];
100         char    ml_dirp[MNTPATHLEN+1];
101
102         SLIST_ENTRY(mountlist)  next;
103 };
104
105 struct dirlist {
106         struct dirlist  *dp_left;
107         struct dirlist  *dp_right;
108         int             dp_flag;
109         struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
110         char            *dp_dirp;
111 };
112 /* dp_flag bits */
113 #define DP_DEFSET       0x1
114 #define DP_HOSTSET      0x2
115
116 /*
117  * maproot/mapall credentials.
118  */
119 struct expcred {
120         uid_t           cr_uid;
121         int             cr_ngroups;
122         gid_t           cr_groups[NGROUPS_MAX + 1];
123 };
124
125 struct exportlist {
126         struct dirlist  *ex_dirl;
127         struct dirlist  *ex_defdir;
128         struct grouplist *ex_grphead;
129         int             ex_flag;
130         fsid_t          ex_fs;
131         char            *ex_fsdir;
132         char            *ex_indexfile;
133         struct expcred  ex_defanon;
134         uint64_t        ex_defexflags;
135         int             ex_numsecflavors;
136         int             ex_secflavors[MAXSECFLAVORS];
137         int             ex_defnumsecflavors;
138         int             ex_defsecflavors[MAXSECFLAVORS];
139
140         SLIST_ENTRY(exportlist) entries;
141 };
142 /* ex_flag bits */
143 #define EX_LINKED       0x1
144 #define EX_DONE         0x2
145 #define EX_DEFSET       0x4
146 #define EX_PUBLICFH     0x8
147
148 SLIST_HEAD(exportlisthead, exportlist);
149
150 struct netmsk {
151         struct sockaddr_storage nt_net;
152         struct sockaddr_storage nt_mask;
153         char            *nt_name;
154 };
155
156 union grouptypes {
157         struct addrinfo *gt_addrinfo;
158         struct netmsk   gt_net;
159 };
160
161 struct grouplist {
162         int gr_type;
163         union grouptypes gr_ptr;
164         struct grouplist *gr_next;
165         struct expcred gr_anon;
166         uint64_t gr_exflags;
167         int gr_flag;
168         int gr_numsecflavors;
169         int gr_secflavors[MAXSECFLAVORS];
170 };
171 /* Group types */
172 #define GT_NULL         0x0
173 #define GT_HOST         0x1
174 #define GT_NET          0x2
175 #define GT_DEFAULT      0x3
176 #define GT_IGNORE       0x5
177
178 /* Group flags */
179 #define GR_FND          0x1
180
181 struct hostlist {
182         int              ht_flag;       /* Uses DP_xx bits */
183         struct grouplist *ht_grp;
184         struct hostlist  *ht_next;
185 };
186
187 struct fhreturn {
188         int     fhr_flag;
189         int     fhr_vers;
190         nfsfh_t fhr_fh;
191         int     fhr_numsecflavors;
192         int     *fhr_secflavors;
193 };
194
195 #define GETPORT_MAXTRY  20      /* Max tries to get a port # */
196
197 /*
198  * How long to delay a reload of exports when there are RPC request(s)
199  * to process, in usec.  Must be less than 1second.
200  */
201 #define RELOADDELAY     250000
202
203 /* Global defs */
204 static char     *add_expdir(struct dirlist **, char *, int);
205 static void     add_dlist(struct dirlist **, struct dirlist *,
206                     struct grouplist *, int, struct exportlist *,
207                     struct expcred *, uint64_t);
208 static void     add_mlist(char *, char *);
209 static int      check_dirpath(char *);
210 static int      check_options(struct dirlist *);
211 static int      checkmask(struct sockaddr *sa);
212 static int      chk_host(struct dirlist *, struct sockaddr *, int *, int *,
213                     int *, int **);
214 static char     *strsep_quote(char **stringp, const char *delim);
215 static int      create_service(struct netconfig *nconf);
216 static void     complete_service(struct netconfig *nconf, char *port_str);
217 static void     clearout_service(void);
218 static void     del_mlist(char *hostp, char *dirp);
219 static struct dirlist   *dirp_search(struct dirlist *, char *);
220 static int      do_export_mount(struct exportlist *, struct statfs *);
221 static int      do_mount(struct exportlist *, struct grouplist *, uint64_t,
222                     struct expcred *, char *, int, struct statfs *, int, int *);
223 static int      do_opt(char **, char **, struct exportlist *,
224                     struct grouplist *, int *, uint64_t *, struct expcred *);
225 static struct exportlist        *ex_search(fsid_t *, struct exportlisthead *);
226 static struct exportlist        *get_exp(void);
227 static void     free_dir(struct dirlist *);
228 static void     free_exp(struct exportlist *);
229 static void     free_grp(struct grouplist *);
230 static void     free_host(struct hostlist *);
231 static void     free_v4rootexp(void);
232 static void     get_exportlist_one(int);
233 static void     get_exportlist(int);
234 static void     insert_exports(struct exportlist *, struct exportlisthead *);
235 static void     free_exports(struct exportlisthead *);
236 static void     read_exportfile(int);
237 static int      compare_nmount_exportlist(struct iovec *, int, char *);
238 static int      compare_export(struct exportlist *, struct exportlist *);
239 static int      compare_cred(struct expcred *, struct expcred *);
240 static int      compare_secflavor(int *, int *, int);
241 static void     delete_export(struct iovec *, int, struct statfs *, char *);
242 static int      get_host(char *, struct grouplist *, struct grouplist *);
243 static struct hostlist *get_ht(void);
244 static int      get_line(void);
245 static void     get_mountlist(void);
246 static int      get_net(char *, struct netmsk *, int);
247 static void     getexp_err(struct exportlist *, struct grouplist *, const char *);
248 static struct grouplist *get_grp(void);
249 static void     hang_dirp(struct dirlist *, struct grouplist *,
250                     struct exportlist *, int, struct expcred *, uint64_t);
251 static void     huphandler(int sig);
252 static int      makemask(struct sockaddr_storage *ssp, int bitlen);
253 static void     mntsrv(struct svc_req *, SVCXPRT *);
254 static void     nextfield(char **, char **);
255 static void     out_of_mem(void);
256 static void     parsecred(char *, struct expcred *);
257 static int      parsesec(char *, struct exportlist *);
258 static int      put_exlist(struct dirlist *, XDR *, struct dirlist *,
259                     int *, int);
260 static void     *sa_rawaddr(struct sockaddr *sa, int *nbytes);
261 static int      sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
262                     struct sockaddr *samask);
263 static int      scan_tree(struct dirlist *, struct sockaddr *);
264 static void     usage(void);
265 static int      xdr_dir(XDR *, char *);
266 static int      xdr_explist(XDR *, caddr_t);
267 static int      xdr_explist_brief(XDR *, caddr_t);
268 static int      xdr_explist_common(XDR *, caddr_t, int);
269 static int      xdr_fhs(XDR *, caddr_t);
270 static int      xdr_mlist(XDR *, caddr_t);
271 static void     terminate(int);
272 static void     cp_cred(struct expcred *, struct expcred *);
273
274 #define EXPHASH(f)      (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
275 static struct exportlisthead *exphead = NULL;
276 static struct exportlisthead *oldexphead = NULL;
277 static int exphashsize = 0;
278 static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
279 static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
280 static char **exnames;
281 static char **hosts = NULL;
282 static int force_v2 = 0;
283 static int resvport_only = 1;
284 static int nhosts = 0;
285 static int dir_only = 1;
286 static int dolog = 0;
287 static int got_sighup = 0;
288 static int xcreated = 0;
289
290 static char *svcport_str = NULL;
291 static int mallocd_svcport = 0;
292 static int *sock_fd;
293 static int sock_fdcnt;
294 static int sock_fdpos;
295 static int suspend_nfsd = 0;
296
297 static int opt_flags;
298 static int have_v6 = 1;
299
300 static int v4root_phase = 0;
301 static char v4root_dirpath[PATH_MAX + 1];
302 static struct exportlist *v4root_ep = NULL;
303 static int has_publicfh = 0;
304 static int has_set_publicfh = 0;
305
306 static struct pidfh *pfh = NULL;
307 /* Bits for opt_flags above */
308 #define OP_MAPROOT      0x01
309 #define OP_MAPALL       0x02
310 /* 0x4 free */
311 #define OP_MASK         0x08
312 #define OP_NET          0x10
313 #define OP_ALLDIRS      0x40
314 #define OP_HAVEMASK     0x80    /* A mask was specified or inferred. */
315 #define OP_QUIET        0x100
316 #define OP_MASKLEN      0x200
317 #define OP_SEC          0x400
318
319 #ifdef DEBUG
320 static int debug = 1;
321 static void     SYSLOG(int, const char *, ...) __printflike(2, 3);
322 #define syslog SYSLOG
323 #else
324 static int debug = 0;
325 #endif
326
327 /*
328  * The LOGDEBUG() syslog() calls are always compiled into the daemon.
329  * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty.
330  * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG.
331  */
332 static int logdebug = 0;
333 #define LOGDEBUG(format, ...)                                           \
334     (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0)
335
336 /*
337  * Similar to strsep(), but it allows for quoted strings
338  * and escaped characters.
339  *
340  * It returns the string (or NULL, if *stringp is NULL),
341  * which is a de-quoted version of the string if necessary.
342  *
343  * It modifies *stringp in place.
344  */
345 static char *
346 strsep_quote(char **stringp, const char *delim)
347 {
348         char *srcptr, *dstptr, *retval;
349         char quot = 0;
350         
351         if (stringp == NULL || *stringp == NULL)
352                 return (NULL);
353
354         srcptr = dstptr = retval = *stringp;
355
356         while (*srcptr) {
357                 /*
358                  * We're looking for several edge cases here.
359                  * First:  if we're in quote state (quot != 0),
360                  * then we ignore the delim characters, but otherwise
361                  * process as normal, unless it is the quote character.
362                  * Second:  if the current character is a backslash,
363                  * we take the next character as-is, without checking
364                  * for delim, quote, or backslash.  Exception:  if the
365                  * next character is a NUL, that's the end of the string.
366                  * Third:  if the character is a quote character, we toggle
367                  * quote state.
368                  * Otherwise:  check the current character for NUL, or
369                  * being in delim, and end the string if either is true.
370                  */
371                 if (*srcptr == '\\') {
372                         srcptr++;
373                         /*
374                          * The edge case here is if the next character
375                          * is NUL, we want to stop processing.  But if
376                          * it's not NUL, then we simply want to copy it.
377                          */
378                         if (*srcptr) {
379                                 *dstptr++ = *srcptr++;
380                         }
381                         continue;
382                 }
383                 if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
384                         quot = *srcptr++;
385                         continue;
386                 }
387                 if (quot && *srcptr == quot) {
388                         /* End of the quoted part */
389                         quot = 0;
390                         srcptr++;
391                         continue;
392                 }
393                 if (!quot && strchr(delim, *srcptr))
394                         break;
395                 *dstptr++ = *srcptr++;
396         }
397
398         *stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
399         *dstptr = 0; /* Terminate the string */
400         return (retval);
401 }
402
403 /*
404  * Mountd server for NFS mount protocol as described in:
405  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
406  * The optional arguments are the exports file name
407  * default: _PATH_EXPORTS
408  * and "-n" to allow nonroot mount.
409  */
410 int
411 main(int argc, char **argv)
412 {
413         fd_set readfds;
414         struct netconfig *nconf;
415         char *endptr, **hosts_bak;
416         void *nc_handle;
417         pid_t otherpid;
418         in_port_t svcport;
419         int c, k, s;
420         int maxrec = RPC_MAXDATASIZE;
421         int attempt_cnt, port_len, port_pos, ret;
422         char **port_list;
423         uint64_t curtime, nexttime;
424         struct timeval tv;
425         struct timespec tp;
426         sigset_t sighup_mask;
427
428         /* Check that another mountd isn't already running. */
429         pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
430         if (pfh == NULL) {
431                 if (errno == EEXIST)
432                         errx(1, "mountd already running, pid: %d.", otherpid);
433                 warn("cannot open or create pidfile");
434         }
435
436         s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
437         if (s < 0)
438                 have_v6 = 0;
439         else
440                 close(s);
441
442         while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
443                 switch (c) {
444                 case '2':
445                         force_v2 = 1;
446                         break;
447                 case 'e':
448                         /* now a no-op, since this is the default */
449                         break;
450                 case 'n':
451                         resvport_only = 0;
452                         break;
453                 case 'r':
454                         dir_only = 0;
455                         break;
456                 case 'd':
457                         debug = debug ? 0 : 1;
458                         break;
459                 case 'l':
460                         dolog = 1;
461                         break;
462                 case 'p':
463                         endptr = NULL;
464                         svcport = (in_port_t)strtoul(optarg, &endptr, 10);
465                         if (endptr == NULL || *endptr != '\0' ||
466                             svcport == 0 || svcport >= IPPORT_MAX)
467                                 usage();
468                         svcport_str = strdup(optarg);
469                         break;
470                 case 'h':
471                         ++nhosts;
472                         hosts_bak = hosts;
473                         hosts_bak = realloc(hosts, nhosts * sizeof(char *));
474                         if (hosts_bak == NULL) {
475                                 if (hosts != NULL) {
476                                         for (k = 0; k < nhosts; k++) 
477                                                 free(hosts[k]);
478                                         free(hosts);
479                                         out_of_mem();
480                                 }
481                         }
482                         hosts = hosts_bak;
483                         hosts[nhosts - 1] = strdup(optarg);
484                         if (hosts[nhosts - 1] == NULL) {
485                                 for (k = 0; k < (nhosts - 1); k++) 
486                                         free(hosts[k]);
487                                 free(hosts);
488                                 out_of_mem();
489                         }
490                         break;
491                 case 'S':
492                         suspend_nfsd = 1;
493                         break;
494                 default:
495                         usage();
496                 }
497
498         if (modfind("nfsd") < 0) {
499                 /* Not present in kernel, try loading it */
500                 if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
501                         errx(1, "NFS server is not available");
502         }
503
504         argc -= optind;
505         argv += optind;
506         if (argc > 0)
507                 exnames = argv;
508         else
509                 exnames = exnames_default;
510         openlog("mountd", LOG_PID, LOG_DAEMON);
511         if (debug)
512                 warnx("getting export list");
513         get_exportlist(0);
514         if (debug)
515                 warnx("getting mount list");
516         get_mountlist();
517         if (debug)
518                 warnx("here we go");
519         if (debug == 0) {
520                 daemon(0, 0);
521                 signal(SIGINT, SIG_IGN);
522                 signal(SIGQUIT, SIG_IGN);
523         }
524         signal(SIGHUP, huphandler);
525         signal(SIGTERM, terminate);
526         signal(SIGPIPE, SIG_IGN);
527
528         pidfile_write(pfh);
529
530         rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
531         rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
532         rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
533
534         if (!resvport_only) {
535                 if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
536                     &resvport_only, sizeof(resvport_only)) != 0 &&
537                     errno != ENOENT) {
538                         syslog(LOG_ERR, "sysctl: %m");
539                         exit(1);
540                 }
541         }
542
543         /*
544          * If no hosts were specified, add a wildcard entry to bind to
545          * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
546          * list.
547          */
548         if (nhosts == 0) {
549                 hosts = malloc(sizeof(char *));
550                 if (hosts == NULL)
551                         out_of_mem();
552                 hosts[0] = "*";
553                 nhosts = 1;
554         } else {
555                 hosts_bak = hosts;
556                 if (have_v6) {
557                         hosts_bak = realloc(hosts, (nhosts + 2) *
558                             sizeof(char *));
559                         if (hosts_bak == NULL) {
560                                 for (k = 0; k < nhosts; k++)
561                                         free(hosts[k]);
562                                 free(hosts);
563                                 out_of_mem();
564                         } else
565                                 hosts = hosts_bak;
566                         nhosts += 2;
567                         hosts[nhosts - 2] = "::1";
568                 } else {
569                         hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
570                         if (hosts_bak == NULL) {
571                                 for (k = 0; k < nhosts; k++)
572                                         free(hosts[k]);
573                                 free(hosts);
574                                 out_of_mem();
575                         } else {
576                                 nhosts += 1;
577                                 hosts = hosts_bak;
578                         }
579                 }
580
581                 hosts[nhosts - 1] = "127.0.0.1";
582         }
583
584         attempt_cnt = 1;
585         sock_fdcnt = 0;
586         sock_fd = NULL;
587         port_list = NULL;
588         port_len = 0;
589         nc_handle = setnetconfig();
590         while ((nconf = getnetconfig(nc_handle))) {
591                 if (nconf->nc_flag & NC_VISIBLE) {
592                         if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
593                             "inet6") == 0) {
594                                 /* DO NOTHING */
595                         } else {
596                                 ret = create_service(nconf);
597                                 if (ret == 1)
598                                         /* Ignore this call */
599                                         continue;
600                                 if (ret < 0) {
601                                         /*
602                                          * Failed to bind port, so close off
603                                          * all sockets created and try again
604                                          * if the port# was dynamically
605                                          * assigned via bind(2).
606                                          */
607                                         clearout_service();
608                                         if (mallocd_svcport != 0 &&
609                                             attempt_cnt < GETPORT_MAXTRY) {
610                                                 free(svcport_str);
611                                                 svcport_str = NULL;
612                                                 mallocd_svcport = 0;
613                                         } else {
614                                                 errno = EADDRINUSE;
615                                                 syslog(LOG_ERR,
616                                                     "bindresvport_sa: %m");
617                                                 exit(1);
618                                         }
619
620                                         /* Start over at the first service. */
621                                         free(sock_fd);
622                                         sock_fdcnt = 0;
623                                         sock_fd = NULL;
624                                         nc_handle = setnetconfig();
625                                         attempt_cnt++;
626                                 } else if (mallocd_svcport != 0 &&
627                                     attempt_cnt == GETPORT_MAXTRY) {
628                                         /*
629                                          * For the last attempt, allow
630                                          * different port #s for each nconf
631                                          * by saving the svcport_str and
632                                          * setting it back to NULL.
633                                          */
634                                         port_list = realloc(port_list,
635                                             (port_len + 1) * sizeof(char *));
636                                         if (port_list == NULL)
637                                                 out_of_mem();
638                                         port_list[port_len++] = svcport_str;
639                                         svcport_str = NULL;
640                                         mallocd_svcport = 0;
641                                 }
642                         }
643                 }
644         }
645
646         /*
647          * Successfully bound the ports, so call complete_service() to
648          * do the rest of the setup on the service(s).
649          */
650         sock_fdpos = 0;
651         port_pos = 0;
652         nc_handle = setnetconfig();
653         while ((nconf = getnetconfig(nc_handle))) {
654                 if (nconf->nc_flag & NC_VISIBLE) {
655                         if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
656                             "inet6") == 0) {
657                                 /* DO NOTHING */
658                         } else if (port_list != NULL) {
659                                 if (port_pos >= port_len) {
660                                         syslog(LOG_ERR, "too many port#s");
661                                         exit(1);
662                                 }
663                                 complete_service(nconf, port_list[port_pos++]);
664                         } else
665                                 complete_service(nconf, svcport_str);
666                 }
667         }
668         endnetconfig(nc_handle);
669         free(sock_fd);
670         if (port_list != NULL) {
671                 for (port_pos = 0; port_pos < port_len; port_pos++)
672                         free(port_list[port_pos]);
673                 free(port_list);
674         }
675
676         if (xcreated == 0) {
677                 syslog(LOG_ERR, "could not create any services");
678                 exit(1);
679         }
680
681         /* Expand svc_run() here so that we can call get_exportlist(). */
682         curtime = nexttime = 0;
683         sigemptyset(&sighup_mask);
684         sigaddset(&sighup_mask, SIGHUP);
685         for (;;) {
686                 clock_gettime(CLOCK_MONOTONIC, &tp);
687                 curtime = tp.tv_sec;
688                 curtime = curtime * 1000000 + tp.tv_nsec / 1000;
689                 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
690                 if (got_sighup && curtime >= nexttime) {
691                         got_sighup = 0;
692                         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
693                         get_exportlist(1);
694                         clock_gettime(CLOCK_MONOTONIC, &tp);
695                         nexttime = tp.tv_sec;
696                         nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
697                             RELOADDELAY;
698                 } else
699                         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
700
701                 /*
702                  * If a reload is pending, poll for received request(s),
703                  * otherwise set a RELOADDELAY timeout, since a SIGHUP
704                  * could be processed between the got_sighup test and
705                  * the select() system call.
706                  */
707                 tv.tv_sec = 0;
708                 if (got_sighup)
709                         tv.tv_usec = 0;
710                 else
711                         tv.tv_usec = RELOADDELAY;
712                 readfds = svc_fdset;
713                 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) {
714                 case -1:
715                         if (errno == EINTR) {
716                                 /* Allow a reload now. */
717                                 nexttime = 0;
718                                 continue;
719                         }
720                         syslog(LOG_ERR, "mountd died: select: %m");
721                         exit(1);
722                 case 0:
723                         /* Allow a reload now. */
724                         nexttime = 0;
725                         continue;
726                 default:
727                         svc_getreqset(&readfds);
728                 }
729         }
730
731
732 /*
733  * This routine creates and binds sockets on the appropriate
734  * addresses. It gets called one time for each transport.
735  * It returns 0 upon success, 1 for ingore the call and -1 to indicate
736  * bind failed with EADDRINUSE.
737  * Any file descriptors that have been created are stored in sock_fd and
738  * the total count of them is maintained in sock_fdcnt.
739  */
740 static int
741 create_service(struct netconfig *nconf)
742 {
743         struct addrinfo hints, *res = NULL;
744         struct sockaddr_in *sin;
745         struct sockaddr_in6 *sin6;
746         struct __rpc_sockinfo si;
747         int aicode;
748         int fd;
749         int nhostsbak;
750         int one = 1;
751         int r;
752         u_int32_t host_addr[4];  /* IPv4 or IPv6 */
753         int mallocd_res;
754
755         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
756             (nconf->nc_semantics != NC_TPI_COTS) &&
757             (nconf->nc_semantics != NC_TPI_COTS_ORD))
758                 return (1);     /* not my type */
759
760         /*
761          * XXX - using RPC library internal functions.
762          */
763         if (!__rpc_nconf2sockinfo(nconf, &si)) {
764                 syslog(LOG_ERR, "cannot get information for %s",
765                     nconf->nc_netid);
766                 return (1);
767         }
768
769         /* Get mountd's address on this transport */
770         memset(&hints, 0, sizeof hints);
771         hints.ai_family = si.si_af;
772         hints.ai_socktype = si.si_socktype;
773         hints.ai_protocol = si.si_proto;
774
775         /*
776          * Bind to specific IPs if asked to
777          */
778         nhostsbak = nhosts;
779         while (nhostsbak > 0) {
780                 --nhostsbak;
781                 sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
782                 if (sock_fd == NULL)
783                         out_of_mem();
784                 sock_fd[sock_fdcnt++] = -1;     /* Set invalid for now. */
785                 mallocd_res = 0;
786
787                 hints.ai_flags = AI_PASSIVE;
788
789                 /*      
790                  * XXX - using RPC library internal functions.
791                  */
792                 if ((fd = __rpc_nconf2fd(nconf)) < 0) {
793                         int non_fatal = 0;
794                         if (errno == EAFNOSUPPORT &&
795                             nconf->nc_semantics != NC_TPI_CLTS) 
796                                 non_fatal = 1;
797                                 
798                         syslog(non_fatal ? LOG_DEBUG : LOG_ERR, 
799                             "cannot create socket for %s", nconf->nc_netid);
800                         if (non_fatal != 0)
801                                 continue;
802                         exit(1);
803                 }
804
805                 switch (hints.ai_family) {
806                 case AF_INET:
807                         if (inet_pton(AF_INET, hosts[nhostsbak],
808                             host_addr) == 1) {
809                                 hints.ai_flags |= AI_NUMERICHOST;
810                         } else {
811                                 /*
812                                  * Skip if we have an AF_INET6 address.
813                                  */
814                                 if (inet_pton(AF_INET6, hosts[nhostsbak],
815                                     host_addr) == 1) {
816                                         close(fd);
817                                         continue;
818                                 }
819                         }
820                         break;
821                 case AF_INET6:
822                         if (inet_pton(AF_INET6, hosts[nhostsbak],
823                             host_addr) == 1) {
824                                 hints.ai_flags |= AI_NUMERICHOST;
825                         } else {
826                                 /*
827                                  * Skip if we have an AF_INET address.
828                                  */
829                                 if (inet_pton(AF_INET, hosts[nhostsbak],
830                                     host_addr) == 1) {
831                                         close(fd);
832                                         continue;
833                                 }
834                         }
835
836                         /*
837                          * We're doing host-based access checks here, so don't
838                          * allow v4-in-v6 to confuse things. The kernel will
839                          * disable it by default on NFS sockets too.
840                          */
841                         if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
842                             sizeof one) < 0) {
843                                 syslog(LOG_ERR,
844                                     "can't disable v4-in-v6 on IPv6 socket");
845                                 exit(1);
846                         }
847                         break;
848                 default:
849                         break;
850                 }
851
852                 /*
853                  * If no hosts were specified, just bind to INADDR_ANY
854                  */
855                 if (strcmp("*", hosts[nhostsbak]) == 0) {
856                         if (svcport_str == NULL) {
857                                 res = malloc(sizeof(struct addrinfo));
858                                 if (res == NULL) 
859                                         out_of_mem();
860                                 mallocd_res = 1;
861                                 res->ai_flags = hints.ai_flags;
862                                 res->ai_family = hints.ai_family;
863                                 res->ai_protocol = hints.ai_protocol;
864                                 switch (res->ai_family) {
865                                 case AF_INET:
866                                         sin = malloc(sizeof(struct sockaddr_in));
867                                         if (sin == NULL) 
868                                                 out_of_mem();
869                                         sin->sin_family = AF_INET;
870                                         sin->sin_port = htons(0);
871                                         sin->sin_addr.s_addr = htonl(INADDR_ANY);
872                                         res->ai_addr = (struct sockaddr*) sin;
873                                         res->ai_addrlen = (socklen_t)
874                                             sizeof(struct sockaddr_in);
875                                         break;
876                                 case AF_INET6:
877                                         sin6 = malloc(sizeof(struct sockaddr_in6));
878                                         if (sin6 == NULL)
879                                                 out_of_mem();
880                                         sin6->sin6_family = AF_INET6;
881                                         sin6->sin6_port = htons(0);
882                                         sin6->sin6_addr = in6addr_any;
883                                         res->ai_addr = (struct sockaddr*) sin6;
884                                         res->ai_addrlen = (socklen_t)
885                                             sizeof(struct sockaddr_in6);
886                                         break;
887                                 default:
888                                         syslog(LOG_ERR, "bad addr fam %d",
889                                             res->ai_family);
890                                         exit(1);
891                                 }
892                         } else { 
893                                 if ((aicode = getaddrinfo(NULL, svcport_str,
894                                     &hints, &res)) != 0) {
895                                         syslog(LOG_ERR,
896                                             "cannot get local address for %s: %s",
897                                             nconf->nc_netid,
898                                             gai_strerror(aicode));
899                                         close(fd);
900                                         continue;
901                                 }
902                         }
903                 } else {
904                         if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
905                             &hints, &res)) != 0) {
906                                 syslog(LOG_ERR,
907                                     "cannot get local address for %s: %s",
908                                     nconf->nc_netid, gai_strerror(aicode));
909                                 close(fd);
910                                 continue;
911                         }
912                 }
913
914                 /* Store the fd. */
915                 sock_fd[sock_fdcnt - 1] = fd;
916
917                 /* Now, attempt the bind. */
918                 r = bindresvport_sa(fd, res->ai_addr);
919                 if (r != 0) {
920                         if (errno == EADDRINUSE && mallocd_svcport != 0) {
921                                 if (mallocd_res != 0) {
922                                         free(res->ai_addr);
923                                         free(res);
924                                 } else
925                                         freeaddrinfo(res);
926                                 return (-1);
927                         }
928                         syslog(LOG_ERR, "bindresvport_sa: %m");
929                         exit(1);
930                 }
931
932                 if (svcport_str == NULL) {
933                         svcport_str = malloc(NI_MAXSERV * sizeof(char));
934                         if (svcport_str == NULL)
935                                 out_of_mem();
936                         mallocd_svcport = 1;
937
938                         if (getnameinfo(res->ai_addr,
939                             res->ai_addr->sa_len, NULL, NI_MAXHOST,
940                             svcport_str, NI_MAXSERV * sizeof(char),
941                             NI_NUMERICHOST | NI_NUMERICSERV))
942                                 errx(1, "Cannot get port number");
943                 }
944                 if (mallocd_res != 0) {
945                         free(res->ai_addr);
946                         free(res);
947                 } else
948                         freeaddrinfo(res);
949                 res = NULL;
950         }
951         return (0);
952 }
953
954 /*
955  * Called after all the create_service() calls have succeeded, to complete
956  * the setup and registration.
957  */
958 static void
959 complete_service(struct netconfig *nconf, char *port_str)
960 {
961         struct addrinfo hints, *res = NULL;
962         struct __rpc_sockinfo si;
963         struct netbuf servaddr;
964         SVCXPRT *transp = NULL;
965         int aicode, fd, nhostsbak;
966         int registered = 0;
967
968         if ((nconf->nc_semantics != NC_TPI_CLTS) &&
969             (nconf->nc_semantics != NC_TPI_COTS) &&
970             (nconf->nc_semantics != NC_TPI_COTS_ORD))
971                 return; /* not my type */
972
973         /*
974          * XXX - using RPC library internal functions.
975          */
976         if (!__rpc_nconf2sockinfo(nconf, &si)) {
977                 syslog(LOG_ERR, "cannot get information for %s",
978                     nconf->nc_netid);
979                 return;
980         }
981
982         nhostsbak = nhosts;
983         while (nhostsbak > 0) {
984                 --nhostsbak;
985                 if (sock_fdpos >= sock_fdcnt) {
986                         /* Should never happen. */
987                         syslog(LOG_ERR, "Ran out of socket fd's");
988                         return;
989                 }
990                 fd = sock_fd[sock_fdpos++];
991                 if (fd < 0)
992                         continue;
993
994                 /*
995                  * Using -1 tells listen(2) to use
996                  * kern.ipc.soacceptqueue for the backlog.
997                  */
998                 if (nconf->nc_semantics != NC_TPI_CLTS)
999                         listen(fd, -1);
1000
1001                 if (nconf->nc_semantics == NC_TPI_CLTS )
1002                         transp = svc_dg_create(fd, 0, 0);
1003                 else 
1004                         transp = svc_vc_create(fd, RPC_MAXDATASIZE,
1005                             RPC_MAXDATASIZE);
1006
1007                 if (transp != (SVCXPRT *) NULL) {
1008                         if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
1009                             NULL)) 
1010                                 syslog(LOG_ERR,
1011                                     "can't register %s MOUNTVERS service",
1012                                     nconf->nc_netid);
1013                         if (!force_v2) {
1014                                 if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
1015                                     mntsrv, NULL)) 
1016                                         syslog(LOG_ERR,
1017                                             "can't register %s MOUNTVERS3 service",
1018                                             nconf->nc_netid);
1019                         }
1020                 } else 
1021                         syslog(LOG_WARNING, "can't create %s services",
1022                             nconf->nc_netid);
1023
1024                 if (registered == 0) {
1025                         registered = 1;
1026                         memset(&hints, 0, sizeof hints);
1027                         hints.ai_flags = AI_PASSIVE;
1028                         hints.ai_family = si.si_af;
1029                         hints.ai_socktype = si.si_socktype;
1030                         hints.ai_protocol = si.si_proto;
1031
1032                         if ((aicode = getaddrinfo(NULL, port_str, &hints,
1033                             &res)) != 0) {
1034                                 syslog(LOG_ERR, "cannot get local address: %s",
1035                                     gai_strerror(aicode));
1036                                 exit(1);
1037                         }
1038
1039                         servaddr.buf = malloc(res->ai_addrlen);
1040                         memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
1041                         servaddr.len = res->ai_addrlen;
1042
1043                         rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
1044                         rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
1045
1046                         xcreated++;
1047                         freeaddrinfo(res);
1048                 }
1049         } /* end while */
1050 }
1051
1052 /*
1053  * Clear out sockets after a failure to bind one of them, so that the
1054  * cycle of socket creation/binding can start anew.
1055  */
1056 static void
1057 clearout_service(void)
1058 {
1059         int i;
1060
1061         for (i = 0; i < sock_fdcnt; i++) {
1062                 if (sock_fd[i] >= 0) {
1063                         shutdown(sock_fd[i], SHUT_RDWR);
1064                         close(sock_fd[i]);
1065                 }
1066         }
1067 }
1068
1069 static void
1070 usage(void)
1071 {
1072         fprintf(stderr,
1073                 "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
1074                 "[-S] [-h <bindip>] [export_file ...]\n");
1075         exit(1);
1076 }
1077
1078 /*
1079  * The mount rpc service
1080  */
1081 void
1082 mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1083 {
1084         struct exportlist *ep;
1085         struct dirlist *dp;
1086         struct fhreturn fhr;
1087         struct stat stb;
1088         struct statfs fsb;
1089         char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1090         int lookup_failed = 1;
1091         struct sockaddr *saddr;
1092         u_short sport;
1093         char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1094         int bad = 0, defset, hostset;
1095         sigset_t sighup_mask;
1096         int numsecflavors, *secflavorsp;
1097
1098         sigemptyset(&sighup_mask);
1099         sigaddset(&sighup_mask, SIGHUP);
1100         saddr = svc_getrpccaller(transp)->buf;
1101         switch (saddr->sa_family) {
1102         case AF_INET6:
1103                 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1104                 break;
1105         case AF_INET:
1106                 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1107                 break;
1108         default:
1109                 syslog(LOG_ERR, "request from unknown address family");
1110                 return;
1111         }
1112         switch (rqstp->rq_proc) {
1113         case MOUNTPROC_MNT:
1114         case MOUNTPROC_UMNT:
1115         case MOUNTPROC_UMNTALL:
1116                 lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1117                     sizeof host, NULL, 0, 0);
1118         }
1119         getnameinfo(saddr, saddr->sa_len, numerichost,
1120             sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1121         switch (rqstp->rq_proc) {
1122         case NULLPROC:
1123                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1124                         syslog(LOG_ERR, "can't send reply");
1125                 return;
1126         case MOUNTPROC_MNT:
1127                 if (sport >= IPPORT_RESERVED && resvport_only) {
1128                         syslog(LOG_NOTICE,
1129                             "mount request from %s from unprivileged port",
1130                             numerichost);
1131                         svcerr_weakauth(transp);
1132                         return;
1133                 }
1134                 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1135                         syslog(LOG_NOTICE, "undecodable mount request from %s",
1136                             numerichost);
1137                         svcerr_decode(transp);
1138                         return;
1139                 }
1140
1141                 /*
1142                  * Get the real pathname and make sure it is a directory
1143                  * or a regular file if the -r option was specified
1144                  * and it exists.
1145                  */
1146                 if (realpath(rpcpath, dirpath) == NULL ||
1147                     stat(dirpath, &stb) < 0 ||
1148                     statfs(dirpath, &fsb) < 0) {
1149                         chdir("/");     /* Just in case realpath doesn't */
1150                         syslog(LOG_NOTICE,
1151                             "mount request from %s for non existent path %s",
1152                             numerichost, dirpath);
1153                         if (debug)
1154                                 warnx("stat failed on %s", dirpath);
1155                         bad = ENOENT;   /* We will send error reply later */
1156                 }
1157                 if (!bad &&
1158                     !S_ISDIR(stb.st_mode) &&
1159                     (dir_only || !S_ISREG(stb.st_mode))) {
1160                         syslog(LOG_NOTICE,
1161                             "mount request from %s for non-directory path %s",
1162                             numerichost, dirpath);
1163                         if (debug)
1164                                 warnx("mounting non-directory %s", dirpath);
1165                         bad = ENOTDIR;  /* We will send error reply later */
1166                 }
1167
1168                 /* Check in the exports list */
1169                 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1170                 if (bad)
1171                         ep = NULL;
1172                 else
1173                         ep = ex_search(&fsb.f_fsid, exphead);
1174                 hostset = defset = 0;
1175                 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1176                     &numsecflavors, &secflavorsp) ||
1177                     ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1178                       chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1179                        &secflavorsp)) ||
1180                     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1181                      scan_tree(ep->ex_dirl, saddr) == 0))) {
1182                         if (bad) {
1183                                 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1184                                     (caddr_t)&bad))
1185                                         syslog(LOG_ERR, "can't send reply");
1186                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1187                                 return;
1188                         }
1189                         if (hostset & DP_HOSTSET) {
1190                                 fhr.fhr_flag = hostset;
1191                                 fhr.fhr_numsecflavors = numsecflavors;
1192                                 fhr.fhr_secflavors = secflavorsp;
1193                         } else {
1194                                 fhr.fhr_flag = defset;
1195                                 fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1196                                 fhr.fhr_secflavors = ep->ex_defsecflavors;
1197                         }
1198                         fhr.fhr_vers = rqstp->rq_vers;
1199                         /* Get the file handle */
1200                         memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1201                         if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1202                                 bad = errno;
1203                                 syslog(LOG_ERR, "can't get fh for %s", dirpath);
1204                                 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1205                                     (caddr_t)&bad))
1206                                         syslog(LOG_ERR, "can't send reply");
1207                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1208                                 return;
1209                         }
1210                         if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1211                             (caddr_t)&fhr))
1212                                 syslog(LOG_ERR, "can't send reply");
1213                         if (!lookup_failed)
1214                                 add_mlist(host, dirpath);
1215                         else
1216                                 add_mlist(numerichost, dirpath);
1217                         if (debug)
1218                                 warnx("mount successful");
1219                         if (dolog)
1220                                 syslog(LOG_NOTICE,
1221                                     "mount request succeeded from %s for %s",
1222                                     numerichost, dirpath);
1223                 } else {
1224                         if (!bad)
1225                                 bad = EACCES;
1226                         syslog(LOG_NOTICE,
1227                             "mount request denied from %s for %s",
1228                             numerichost, dirpath);
1229                 }
1230
1231                 if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1232                     (caddr_t)&bad))
1233                         syslog(LOG_ERR, "can't send reply");
1234                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1235                 return;
1236         case MOUNTPROC_DUMP:
1237                 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1238                         syslog(LOG_ERR, "can't send reply");
1239                 else if (dolog)
1240                         syslog(LOG_NOTICE,
1241                             "dump request succeeded from %s",
1242                             numerichost);
1243                 return;
1244         case MOUNTPROC_UMNT:
1245                 if (sport >= IPPORT_RESERVED && resvport_only) {
1246                         syslog(LOG_NOTICE,
1247                             "umount request from %s from unprivileged port",
1248                             numerichost);
1249                         svcerr_weakauth(transp);
1250                         return;
1251                 }
1252                 if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1253                         syslog(LOG_NOTICE, "undecodable umount request from %s",
1254                             numerichost);
1255                         svcerr_decode(transp);
1256                         return;
1257                 }
1258                 if (realpath(rpcpath, dirpath) == NULL) {
1259                         syslog(LOG_NOTICE, "umount request from %s "
1260                             "for non existent path %s",
1261                             numerichost, dirpath);
1262                 }
1263                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1264                         syslog(LOG_ERR, "can't send reply");
1265                 if (!lookup_failed)
1266                         del_mlist(host, dirpath);
1267                 del_mlist(numerichost, dirpath);
1268                 if (dolog)
1269                         syslog(LOG_NOTICE,
1270                             "umount request succeeded from %s for %s",
1271                             numerichost, dirpath);
1272                 return;
1273         case MOUNTPROC_UMNTALL:
1274                 if (sport >= IPPORT_RESERVED && resvport_only) {
1275                         syslog(LOG_NOTICE,
1276                             "umountall request from %s from unprivileged port",
1277                             numerichost);
1278                         svcerr_weakauth(transp);
1279                         return;
1280                 }
1281                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1282                         syslog(LOG_ERR, "can't send reply");
1283                 if (!lookup_failed)
1284                         del_mlist(host, NULL);
1285                 del_mlist(numerichost, NULL);
1286                 if (dolog)
1287                         syslog(LOG_NOTICE,
1288                             "umountall request succeeded from %s",
1289                             numerichost);
1290                 return;
1291         case MOUNTPROC_EXPORT:
1292                 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1293                         if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1294                             (caddr_t)NULL))
1295                                 syslog(LOG_ERR, "can't send reply");
1296                 if (dolog)
1297                         syslog(LOG_NOTICE,
1298                             "export request succeeded from %s",
1299                             numerichost);
1300                 return;
1301         default:
1302                 svcerr_noproc(transp);
1303                 return;
1304         }
1305 }
1306
1307 /*
1308  * Xdr conversion for a dirpath string
1309  */
1310 static int
1311 xdr_dir(XDR *xdrsp, char *dirp)
1312 {
1313         return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1314 }
1315
1316 /*
1317  * Xdr routine to generate file handle reply
1318  */
1319 static int
1320 xdr_fhs(XDR *xdrsp, caddr_t cp)
1321 {
1322         struct fhreturn *fhrp = (struct fhreturn *)cp;
1323         u_long ok = 0, len, auth;
1324         int i;
1325
1326         if (!xdr_long(xdrsp, &ok))
1327                 return (0);
1328         switch (fhrp->fhr_vers) {
1329         case 1:
1330                 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1331         case 3:
1332                 len = NFSX_V3FH;
1333                 if (!xdr_long(xdrsp, &len))
1334                         return (0);
1335                 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1336                         return (0);
1337                 if (fhrp->fhr_numsecflavors) {
1338                         if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1339                                 return (0);
1340                         for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1341                                 if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1342                                         return (0);
1343                         return (1);
1344                 } else {
1345                         auth = AUTH_SYS;
1346                         len = 1;
1347                         if (!xdr_long(xdrsp, &len))
1348                                 return (0);
1349                         return (xdr_long(xdrsp, &auth));
1350                 }
1351         }
1352         return (0);
1353 }
1354
1355 static int
1356 xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1357 {
1358         struct mountlist *mlp;
1359         int true = 1;
1360         int false = 0;
1361         char *strp;
1362
1363         SLIST_FOREACH(mlp, &mlhead, next) {
1364                 if (!xdr_bool(xdrsp, &true))
1365                         return (0);
1366                 strp = &mlp->ml_host[0];
1367                 if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1368                         return (0);
1369                 strp = &mlp->ml_dirp[0];
1370                 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1371                         return (0);
1372         }
1373         if (!xdr_bool(xdrsp, &false))
1374                 return (0);
1375         return (1);
1376 }
1377
1378 /*
1379  * Xdr conversion for export list
1380  */
1381 static int
1382 xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1383 {
1384         struct exportlist *ep;
1385         int false = 0;
1386         int putdef;
1387         sigset_t sighup_mask;
1388         int i;
1389
1390         sigemptyset(&sighup_mask);
1391         sigaddset(&sighup_mask, SIGHUP);
1392         sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1393
1394         for (i = 0; i < exphashsize; i++)
1395                 SLIST_FOREACH(ep, &exphead[i], entries) {
1396                         putdef = 0;
1397                         if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1398                                        &putdef, brief))
1399                                 goto errout;
1400                         if (ep->ex_defdir && putdef == 0 &&
1401                                 put_exlist(ep->ex_defdir, xdrsp, NULL,
1402                                 &putdef, brief))
1403                                 goto errout;
1404                 }
1405         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1406         if (!xdr_bool(xdrsp, &false))
1407                 return (0);
1408         return (1);
1409 errout:
1410         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1411         return (0);
1412 }
1413
1414 /*
1415  * Called from xdr_explist() to traverse the tree and export the
1416  * directory paths.
1417  */
1418 static int
1419 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1420         int brief)
1421 {
1422         struct grouplist *grp;
1423         struct hostlist *hp;
1424         int true = 1;
1425         int false = 0;
1426         int gotalldir = 0;
1427         char *strp;
1428
1429         if (dp) {
1430                 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1431                         return (1);
1432                 if (!xdr_bool(xdrsp, &true))
1433                         return (1);
1434                 strp = dp->dp_dirp;
1435                 if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1436                         return (1);
1437                 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1438                         gotalldir = 1;
1439                         *putdefp = 1;
1440                 }
1441                 if (brief) {
1442                         if (!xdr_bool(xdrsp, &true))
1443                                 return (1);
1444                         strp = "(...)";
1445                         if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1446                                 return (1);
1447                 } else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1448                     (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1449                         hp = dp->dp_hosts;
1450                         while (hp) {
1451                                 grp = hp->ht_grp;
1452                                 if (grp->gr_type == GT_HOST) {
1453                                         if (!xdr_bool(xdrsp, &true))
1454                                                 return (1);
1455                                         strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1456                                         if (!xdr_string(xdrsp, &strp,
1457                                             MNTNAMLEN))
1458                                                 return (1);
1459                                 } else if (grp->gr_type == GT_NET) {
1460                                         if (!xdr_bool(xdrsp, &true))
1461                                                 return (1);
1462                                         strp = grp->gr_ptr.gt_net.nt_name;
1463                                         if (!xdr_string(xdrsp, &strp,
1464                                             MNTNAMLEN))
1465                                                 return (1);
1466                                 }
1467                                 hp = hp->ht_next;
1468                                 if (gotalldir && hp == (struct hostlist *)NULL) {
1469                                         hp = adp->dp_hosts;
1470                                         gotalldir = 0;
1471                                 }
1472                         }
1473                 }
1474                 if (!xdr_bool(xdrsp, &false))
1475                         return (1);
1476                 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1477                         return (1);
1478         }
1479         return (0);
1480 }
1481
1482 static int
1483 xdr_explist(XDR *xdrsp, caddr_t cp)
1484 {
1485
1486         return xdr_explist_common(xdrsp, cp, 0);
1487 }
1488
1489 static int
1490 xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1491 {
1492
1493         return xdr_explist_common(xdrsp, cp, 1);
1494 }
1495
1496 static char *line;
1497 static size_t linesize;
1498 static FILE *exp_file;
1499
1500 /*
1501  * Get the export list from one, currently open file
1502  */
1503 static void
1504 get_exportlist_one(int passno)
1505 {
1506         struct exportlist *ep;
1507         struct grouplist *grp, *tgrp, *savgrp;
1508         struct dirlist *dirhead;
1509         struct statfs fsb;
1510         struct expcred anon;
1511         char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1512         int len, has_host, got_nondir, dirplen, netgrp;
1513         uint64_t exflags;
1514
1515         v4root_phase = 0;
1516         dirhead = (struct dirlist *)NULL;
1517         while (get_line()) {
1518                 if (debug)
1519                         warnx("got line %s", line);
1520                 cp = line;
1521                 nextfield(&cp, &endcp);
1522                 if (*cp == '#')
1523                         goto nextline;
1524
1525                 /*
1526                  * Set defaults.
1527                  */
1528                 has_host = FALSE;
1529                 anon.cr_uid = UID_NOBODY;
1530                 anon.cr_ngroups = 1;
1531                 anon.cr_groups[0] = GID_NOGROUP;
1532                 exflags = MNT_EXPORTED;
1533                 got_nondir = 0;
1534                 opt_flags = 0;
1535                 ep = (struct exportlist *)NULL;
1536                 dirp = NULL;
1537
1538                 /*
1539                  * Handle the V4 root dir.
1540                  */
1541                 if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1542                         /*
1543                          * V4: just indicates that it is the v4 root point,
1544                          * so skip over that and set v4root_phase.
1545                          */
1546                         if (v4root_phase > 0) {
1547                                 syslog(LOG_ERR, "V4:duplicate line, ignored");
1548                                 goto nextline;
1549                         }
1550                         v4root_phase = 1;
1551                         cp += 3;
1552                         nextfield(&cp, &endcp);
1553                 }
1554
1555                 /*
1556                  * Create new exports list entry
1557                  */
1558                 len = endcp-cp;
1559                 tgrp = grp = get_grp();
1560                 while (len > 0) {
1561                         if (len > MNTNAMLEN) {
1562                             getexp_err(ep, tgrp, "mountpoint too long");
1563                             goto nextline;
1564                         }
1565                         if (*cp == '-') {
1566                             if (ep == (struct exportlist *)NULL) {
1567                                 getexp_err(ep, tgrp,
1568                                     "flag before export path definition");
1569                                 goto nextline;
1570                             }
1571                             if (debug)
1572                                 warnx("doing opt %s", cp);
1573                             got_nondir = 1;
1574                             if (do_opt(&cp, &endcp, ep, grp, &has_host,
1575                                 &exflags, &anon)) {
1576                                 getexp_err(ep, tgrp, NULL);
1577                                 goto nextline;
1578                             }
1579                         } else if (*cp == '/') {
1580                             savedc = *endcp;
1581                             *endcp = '\0';
1582                             if (v4root_phase > 1) {
1583                                     if (dirp != NULL) {
1584                                         getexp_err(ep, tgrp, "Multiple V4 dirs");
1585                                         goto nextline;
1586                                     }
1587                             }
1588                             if (check_dirpath(cp) &&
1589                                 statfs(cp, &fsb) >= 0) {
1590                                 if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1591                                     syslog(LOG_ERR, "Warning: exporting of "
1592                                         "automounted fs %s not supported", cp);
1593                                 if (got_nondir) {
1594                                     getexp_err(ep, tgrp, "dirs must be first");
1595                                     goto nextline;
1596                                 }
1597                                 if (v4root_phase == 1) {
1598                                     if (dirp != NULL) {
1599                                         getexp_err(ep, tgrp, "Multiple V4 dirs");
1600                                         goto nextline;
1601                                     }
1602                                     if (strlen(v4root_dirpath) == 0) {
1603                                         strlcpy(v4root_dirpath, cp,
1604                                             sizeof (v4root_dirpath));
1605                                     } else if (strcmp(v4root_dirpath, cp)
1606                                         != 0) {
1607                                         syslog(LOG_ERR,
1608                                             "different V4 dirpath %s", cp);
1609                                         getexp_err(ep, tgrp, NULL);
1610                                         goto nextline;
1611                                     }
1612                                     dirp = cp;
1613                                     v4root_phase = 2;
1614                                     got_nondir = 1;
1615                                     ep = get_exp();
1616                                 } else {
1617                                     if (ep) {
1618                                         if (fsidcmp(&ep->ex_fs, &fsb.f_fsid)
1619                                             != 0) {
1620                                                 getexp_err(ep, tgrp,
1621                                                     "fsid mismatch");
1622                                                 goto nextline;
1623                                         }
1624                                     } else {
1625                                         /*
1626                                          * See if this directory is already
1627                                          * in the list.
1628                                          */
1629                                         ep = ex_search(&fsb.f_fsid, exphead);
1630                                         if (ep == (struct exportlist *)NULL) {
1631                                             ep = get_exp();
1632                                             ep->ex_fs = fsb.f_fsid;
1633                                             ep->ex_fsdir = strdup(fsb.f_mntonname);
1634                                             if (ep->ex_fsdir == NULL)
1635                                                 out_of_mem();
1636                                             if (debug)
1637                                                 warnx(
1638                                                   "making new ep fs=0x%x,0x%x",
1639                                                   fsb.f_fsid.val[0],
1640                                                   fsb.f_fsid.val[1]);
1641                                         } else if (debug)
1642                                             warnx("found ep fs=0x%x,0x%x",
1643                                                 fsb.f_fsid.val[0],
1644                                                 fsb.f_fsid.val[1]);
1645                                     }
1646
1647                                     /*
1648                                      * Add dirpath to export mount point.
1649                                      */
1650                                     dirp = add_expdir(&dirhead, cp, len);
1651                                     dirplen = len;
1652                                 }
1653                             } else {
1654                                 getexp_err(ep, tgrp,
1655                                     "symbolic link in export path or statfs failed");
1656                                 goto nextline;
1657                             }
1658                             *endcp = savedc;
1659                         } else {
1660                             savedc = *endcp;
1661                             *endcp = '\0';
1662                             got_nondir = 1;
1663                             if (ep == (struct exportlist *)NULL) {
1664                                 getexp_err(ep, tgrp,
1665                                     "host(s) before export path definition");
1666                                 goto nextline;
1667                             }
1668
1669                             /*
1670                              * Get the host or netgroup.
1671                              */
1672                             setnetgrent(cp);
1673                             netgrp = getnetgrent(&hst, &usr, &dom);
1674                             do {
1675                                 if (has_host) {
1676                                     grp->gr_next = get_grp();
1677                                     grp = grp->gr_next;
1678                                 }
1679                                 if (netgrp) {
1680                                     if (hst == 0) {
1681                                         syslog(LOG_ERR,
1682                                 "null hostname in netgroup %s, skipping", cp);
1683                                         grp->gr_type = GT_IGNORE;
1684                                     } else if (get_host(hst, grp, tgrp)) {
1685                                         syslog(LOG_ERR,
1686                         "bad host %s in netgroup %s, skipping", hst, cp);
1687                                         grp->gr_type = GT_IGNORE;
1688                                     }
1689                                 } else if (get_host(cp, grp, tgrp)) {
1690                                     syslog(LOG_ERR, "bad host %s, skipping", cp);
1691                                     grp->gr_type = GT_IGNORE;
1692                                 }
1693                                 has_host = TRUE;
1694                             } while (netgrp && getnetgrent(&hst, &usr, &dom));
1695                             endnetgrent();
1696                             *endcp = savedc;
1697                         }
1698                         cp = endcp;
1699                         nextfield(&cp, &endcp);
1700                         len = endcp - cp;
1701                 }
1702                 if (check_options(dirhead)) {
1703                         getexp_err(ep, tgrp, NULL);
1704                         goto nextline;
1705                 }
1706                 if (!has_host) {
1707                         grp->gr_type = GT_DEFAULT;
1708                         if (debug)
1709                                 warnx("adding a default entry");
1710
1711                 /*
1712                  * Don't allow a network export coincide with a list of
1713                  * host(s) on the same line.
1714                  */
1715                 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1716                         getexp_err(ep, tgrp, "network/host conflict");
1717                         goto nextline;
1718
1719                 /*
1720                  * If an export list was specified on this line, make sure
1721                  * that we have at least one valid entry, otherwise skip it.
1722                  */
1723                 } else {
1724                         grp = tgrp;
1725                         while (grp && grp->gr_type == GT_IGNORE)
1726                                 grp = grp->gr_next;
1727                         if (! grp) {
1728                             getexp_err(ep, tgrp, "no valid entries");
1729                             goto nextline;
1730                         }
1731                 }
1732
1733                 if (v4root_phase == 1) {
1734                         getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1735                         goto nextline;
1736                 }
1737
1738                 /*
1739                  * Loop through hosts, pushing the exports into the kernel.
1740                  * After loop, tgrp points to the start of the list and
1741                  * grp points to the last entry in the list.
1742                  * Do not do the do_mount() for passno == 1, since the
1743                  * second pass will do it, as required.
1744                  */
1745                 grp = tgrp;
1746                 do {
1747                         grp->gr_exflags = exflags;
1748                         cp_cred(&grp->gr_anon, &anon);
1749                         if (v4root_phase == 2 && passno == 0)
1750                                 LOGDEBUG("do_mount v4root");
1751                         if (passno == 0 && do_mount(ep, grp, exflags, &anon,
1752                             dirp, dirplen, &fsb, ep->ex_numsecflavors,
1753                             ep->ex_secflavors)) {
1754                                 getexp_err(ep, tgrp, NULL);
1755                                 goto nextline;
1756                         }
1757                 } while (grp->gr_next && (grp = grp->gr_next));
1758
1759                 /*
1760                  * For V4: don't enter in mount lists.
1761                  */
1762                 if (v4root_phase > 0 && v4root_phase <= 2) {
1763                         /*
1764                          * These structures are used for the reload,
1765                          * so save them for that case.  Otherwise, just
1766                          * free them up now.
1767                          */
1768                         if (passno == 1 && ep != NULL) {
1769                                 savgrp = tgrp;
1770                                 while (tgrp != NULL) {
1771                                         /*
1772                                          * Save the security flavors and exflags
1773                                          * for this host set in the groups.
1774                                          */
1775                                         tgrp->gr_numsecflavors =
1776                                             ep->ex_numsecflavors;
1777                                         if (ep->ex_numsecflavors > 0)
1778                                                 memcpy(tgrp->gr_secflavors,
1779                                                     ep->ex_secflavors,
1780                                                     sizeof(ep->ex_secflavors));
1781                                         tgrp = tgrp->gr_next;
1782                                 }
1783                                 if (v4root_ep == NULL) {
1784                                         v4root_ep = ep;
1785                                         ep = NULL;      /* Don't free below. */
1786                                 }
1787                                 grp->gr_next = v4root_ep->ex_grphead;
1788                                 v4root_ep->ex_grphead = savgrp;
1789                         }
1790                         if (ep != NULL)
1791                                 free_exp(ep);
1792                         while (tgrp != NULL) {
1793                                 grp = tgrp;
1794                                 tgrp = tgrp->gr_next;
1795                                 free_grp(grp);
1796                         }
1797                         goto nextline;
1798                 }
1799
1800                 /*
1801                  * Success. Update the data structures.
1802                  */
1803                 if (has_host) {
1804                         hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags);
1805                         grp->gr_next = ep->ex_grphead;
1806                         ep->ex_grphead = tgrp;
1807                 } else {
1808                         hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1809                                 opt_flags, &anon, exflags);
1810                         free_grp(grp);
1811                 }
1812                 dirhead = (struct dirlist *)NULL;
1813                 if ((ep->ex_flag & EX_LINKED) == 0) {
1814                         insert_exports(ep, exphead);
1815
1816                         ep->ex_flag |= EX_LINKED;
1817                 }
1818 nextline:
1819                 v4root_phase = 0;
1820                 if (dirhead) {
1821                         free_dir(dirhead);
1822                         dirhead = (struct dirlist *)NULL;
1823                 }
1824         }
1825 }
1826
1827 /*
1828  * Get the export list from all specified files
1829  */
1830 static void
1831 get_exportlist(int passno)
1832 {
1833         struct export_args export;
1834         struct iovec *iov;
1835         struct statfs *mntbufp;
1836         char errmsg[255];
1837         int num, i;
1838         int iovlen;
1839         struct nfsex_args eargs;
1840         FILE *debug_file;
1841
1842         if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) {
1843                 fclose(debug_file);
1844                 logdebug = 1;
1845         } else
1846                 logdebug = 0;
1847         LOGDEBUG("passno=%d", passno);
1848         v4root_dirpath[0] = '\0';
1849         free_v4rootexp();
1850         if (passno == 1) {
1851                 /*
1852                  * Save the current lists as old ones, so that the new lists
1853                  * can be compared with the old ones in the 2nd pass.
1854                  */
1855                 for (i = 0; i < exphashsize; i++) {
1856                         SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]);
1857                         SLIST_INIT(&exphead[i]);
1858                 }
1859
1860                 /* Note that the public fh has not yet been set. */
1861                 has_set_publicfh = 0;
1862
1863                 /* Read the export file(s) and process them */
1864                 read_exportfile(passno);
1865         } else {
1866                 /*
1867                  * Just make the old lists empty.
1868                  * exphashsize == 0 for the first call, before oldexphead
1869                  * has been initialized-->loop won't be executed.
1870                  */
1871                 for (i = 0; i < exphashsize; i++)
1872                         SLIST_INIT(&oldexphead[i]);
1873         }
1874
1875         bzero(&export, sizeof(export));
1876         export.ex_flags = MNT_DELEXPORT;
1877         iov = NULL;
1878         iovlen = 0;
1879         bzero(errmsg, sizeof(errmsg));
1880
1881         if (suspend_nfsd != 0)
1882                 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1883         /*
1884          * Delete the old V4 root dir.
1885          */
1886         bzero(&eargs, sizeof (eargs));
1887         eargs.export.ex_flags = MNT_DELEXPORT;
1888         if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 &&
1889             errno != ENOENT)
1890                 syslog(LOG_ERR, "Can't delete exports for V4:");
1891
1892         build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1893         build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1894         build_iovec(&iov, &iovlen, "from", NULL, 0);
1895         build_iovec(&iov, &iovlen, "update", NULL, 0);
1896         build_iovec(&iov, &iovlen, "export", &export,
1897             sizeof(export));
1898         build_iovec(&iov, &iovlen, "errmsg", errmsg,
1899             sizeof(errmsg));
1900
1901         /*
1902          * For passno == 1, compare the old and new lists updating the kernel
1903          * exports for any cases that have changed.
1904          * This call is doing the second pass through the lists.
1905          * If it fails, fall back on the bulk reload.
1906          */
1907         if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) ==
1908             0) {
1909                 LOGDEBUG("compareok");
1910                 /* Free up the old lists. */
1911                 free_exports(oldexphead);
1912         } else {
1913                 LOGDEBUG("doing passno=0");
1914                 /*
1915                  * Clear flag that notes if a public fh has been exported.
1916                  * It is set by do_mount() if MNT_EXPUBLIC is set for the entry.
1917                  */
1918                 has_publicfh = 0;
1919
1920                 /* exphead == NULL if not yet allocated (first call). */
1921                 if (exphead != NULL) {
1922                         /*
1923                          * First, get rid of the old lists.
1924                          */
1925                         free_exports(exphead);
1926                         free_exports(oldexphead);
1927                 }
1928
1929                 /*
1930                  * And delete exports that are in the kernel for all local
1931                  * filesystems.
1932                  * XXX: Should know how to handle all local exportable
1933                  * filesystems.
1934                  */
1935                 num = getmntinfo(&mntbufp, MNT_NOWAIT);
1936
1937                 /* Allocate hash tables, for first call. */
1938                 if (exphead == NULL) {
1939                         /* Target an average linked list length of 10. */
1940                         exphashsize = num / 10;
1941                         if (exphashsize < 1)
1942                                 exphashsize = 1;
1943                         else if (exphashsize > 100000)
1944                                 exphashsize = 100000;
1945                         exphead = malloc(exphashsize * sizeof(*exphead));
1946                         oldexphead = malloc(exphashsize * sizeof(*oldexphead));
1947                         if (exphead == NULL || oldexphead == NULL)
1948                                 errx(1, "Can't malloc hash tables");
1949
1950                         for (i = 0; i < exphashsize; i++) {
1951                                 SLIST_INIT(&exphead[i]);
1952                                 SLIST_INIT(&oldexphead[i]);
1953                         }
1954                 }
1955         
1956                 for (i = 0; i < num; i++)
1957                         delete_export(iov, iovlen, &mntbufp[i], errmsg);
1958
1959
1960                 /* Read the export file(s) and process them */
1961                 read_exportfile(0);
1962         }
1963
1964         if (iov != NULL) {
1965                 /* Free strings allocated by strdup() in getmntopts.c */
1966                 free(iov[0].iov_base); /* fstype */
1967                 free(iov[2].iov_base); /* fspath */
1968                 free(iov[4].iov_base); /* from */
1969                 free(iov[6].iov_base); /* update */
1970                 free(iov[8].iov_base); /* export */
1971                 free(iov[10].iov_base); /* errmsg */
1972
1973                 /* free iov, allocated by realloc() */
1974                 free(iov);
1975                 iovlen = 0;
1976         }
1977
1978         /*
1979          * If there was no public fh, clear any previous one set.
1980          */
1981         if (has_publicfh == 0) {
1982                 LOGDEBUG("clear public fh");
1983                 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1984         }
1985
1986         /* Resume the nfsd. If they weren't suspended, this is harmless. */
1987         (void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1988         LOGDEBUG("eo get_exportlist");
1989 }
1990
1991 /*
1992  * Insert an export entry in the appropriate list.
1993  */
1994 static void
1995 insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
1996 {
1997         uint32_t i;
1998
1999         i = EXPHASH(&ep->ex_fs);
2000         LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i);
2001         SLIST_INSERT_HEAD(&exhp[i], ep, entries);
2002 }
2003
2004 /*
2005  * Free up the exports lists passed in as arguments.
2006  */
2007 static void
2008 free_exports(struct exportlisthead *exhp)
2009 {
2010         struct exportlist *ep, *ep2;
2011         int i;
2012
2013         for (i = 0; i < exphashsize; i++) {
2014                 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
2015                         SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
2016                         free_exp(ep);
2017                 }
2018                 SLIST_INIT(&exhp[i]);
2019         }
2020 }
2021
2022 /*
2023  * Read the exports file(s) and call get_exportlist_one() for each line.
2024  */
2025 static void
2026 read_exportfile(int passno)
2027 {
2028         int done, i;
2029
2030         /*
2031          * Read in the exports file and build the list, calling
2032          * nmount() as we go along to push the export rules into the kernel.
2033          */
2034         done = 0;
2035         for (i = 0; exnames[i] != NULL; i++) {
2036                 if (debug)
2037                         warnx("reading exports from %s", exnames[i]);
2038                 if ((exp_file = fopen(exnames[i], "r")) == NULL) {
2039                         syslog(LOG_WARNING, "can't open %s", exnames[i]);
2040                         continue;
2041                 }
2042                 get_exportlist_one(passno);
2043                 fclose(exp_file);
2044                 done++;
2045         }
2046         if (done == 0) {
2047                 syslog(LOG_ERR, "can't open any exports file");
2048                 exit(2);
2049         }
2050 }
2051
2052 /*
2053  * Compare the export lists against the old ones and do nmount() operations
2054  * for any cases that have changed.  This avoids doing nmount() for entries
2055  * that have not changed.
2056  * Return 0 upon success, 1 otherwise.
2057  */
2058 static int
2059 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
2060 {
2061         struct exportlist *ep, *oep;
2062         struct grouplist *grp;
2063         struct statfs fs, ofs;
2064         int i, ret;
2065
2066         /*
2067          * Loop through the current list and look for an entry in the old
2068          * list.
2069          * If found, check to see if it the same.
2070          *        If it is not the same, delete and re-export.
2071          *        Then mark it done on the old list.
2072          * else (not found)
2073          *        export it.
2074          * Any entries left in the old list after processing must have their
2075          * exports deleted.
2076          */
2077         for (i = 0; i < exphashsize; i++)
2078                 SLIST_FOREACH(ep, &exphead[i], entries) {
2079                         LOGDEBUG("foreach ep=%s", ep->ex_fsdir);
2080                         oep = ex_search(&ep->ex_fs, oldexphead);
2081                         if (oep != NULL) {
2082                                 /*
2083                                  * Check the mount paths are the same.
2084                                  * If not, return 1 so that the reload of the
2085                                  * exports will be done in bulk, the
2086                                  * passno == 0 way.
2087                                  */
2088                                 LOGDEBUG("found old exp");
2089                                 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2090                                         return (1);
2091                                 LOGDEBUG("same fsdir");
2092                                 /*
2093                                  * Test to see if the entry is the same.
2094                                  * If not the same delete exports and
2095                                  * re-export.
2096                                  */
2097                                 if (compare_export(ep, oep) != 0) {
2098                                         /*
2099                                          * Clear has_publicfh if if was set
2100                                          * in the old exports, but only if it
2101                                          * has not been set during processing of
2102                                          * the exports for this pass, as
2103                                          * indicated by has_set_publicfh.
2104                                          */
2105                                         if (has_set_publicfh == 0 &&
2106                                             (oep->ex_flag & EX_PUBLICFH) != 0)
2107                                                 has_publicfh = 0;
2108
2109                                         /* Delete and re-export. */
2110                                         if (statfs(ep->ex_fsdir, &fs) < 0)
2111                                                 return (1);
2112                                         delete_export(iov, iovlen, &fs, errmsg);
2113                                         ret = do_export_mount(ep, &fs);
2114                                         if (ret != 0)
2115                                                 return (ret);
2116                                 }
2117                                 oep->ex_flag |= EX_DONE;
2118                                 LOGDEBUG("exdone");
2119                         } else {
2120                                 LOGDEBUG("not found so export");
2121                                 /* Not found, so do export. */
2122                                 if (statfs(ep->ex_fsdir, &fs) < 0)
2123                                         return (1);
2124                                 ret = do_export_mount(ep, &fs);
2125                                 if (ret != 0)
2126                                         return (ret);
2127                         }
2128                 }
2129
2130         /* Delete exports not done. */
2131         for (i = 0; i < exphashsize; i++)
2132                 SLIST_FOREACH(oep, &oldexphead[i], entries) {
2133                         if ((oep->ex_flag & EX_DONE) == 0) {
2134                                 LOGDEBUG("not done delete=%s", oep->ex_fsdir);
2135                                 if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
2136                                     fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) {
2137                                         LOGDEBUG("do delete");
2138                                         /*
2139                                          * Clear has_publicfh if if was set
2140                                          * in the old exports, but only if it
2141                                          * has not been set during processing of
2142                                          * the exports for this pass, as
2143                                          * indicated by has_set_publicfh.
2144                                          */
2145                                         if (has_set_publicfh == 0 &&
2146                                             (oep->ex_flag & EX_PUBLICFH) != 0)
2147                                                 has_publicfh = 0;
2148
2149                                         delete_export(iov, iovlen, &ofs,
2150                                             errmsg);
2151                                 }
2152                         }
2153                 }
2154
2155         /* Do the V4 root exports, as required. */
2156         grp = NULL;
2157         if (v4root_ep != NULL)
2158                 grp = v4root_ep->ex_grphead;
2159         v4root_phase = 2;
2160         while (v4root_ep != NULL && grp != NULL) {
2161                 LOGDEBUG("v4root expath=%s", v4root_dirpath);
2162                 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
2163                     v4root_dirpath, strlen(v4root_dirpath), &fs,
2164                     grp->gr_numsecflavors, grp->gr_secflavors);
2165                 if (ret != 0) {
2166                         v4root_phase = 0;
2167                         return (ret);
2168                 }
2169                 grp = grp->gr_next;
2170         }
2171         v4root_phase = 0;
2172         free_v4rootexp();
2173         return (0);
2174 }
2175
2176 /*
2177  * Compare old and current exportlist entries for the fsid and return 0
2178  * if they are the same, 1 otherwise.
2179  */
2180 static int
2181 compare_export(struct exportlist *ep, struct exportlist *oep)
2182 {
2183         struct grouplist *grp, *ogrp;
2184
2185         if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2186                 return (1);
2187         if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET))
2188                 return (1);
2189         if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
2190             (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
2191                 return (1);
2192         if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) !=
2193             (oep->ex_defdir->dp_flag & DP_DEFSET))
2194                 return (1);
2195         if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors !=
2196             oep->ex_defnumsecflavors || ep->ex_defexflags !=
2197             oep->ex_defexflags || compare_cred(&ep->ex_defanon,
2198             &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors,
2199             oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0))
2200                 return (1);
2201
2202         /* Now, check all the groups. */
2203         for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2204                 ogrp->gr_flag = 0;
2205         for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
2206                 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
2207                     ogrp->gr_next)
2208                         if ((ogrp->gr_flag & GR_FND) == 0 &&
2209                             grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
2210                             grp->gr_exflags == ogrp->gr_exflags &&
2211                             compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
2212                             compare_secflavor(grp->gr_secflavors,
2213                             ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
2214                                 break;
2215                 if (ogrp != NULL)
2216                         ogrp->gr_flag |= GR_FND;
2217                 else
2218                         return (1);
2219         }
2220         for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2221                 if ((ogrp->gr_flag & GR_FND) == 0)
2222                         return (1);
2223         return (0);
2224 }
2225
2226 /*
2227  * This algorithm compares two arrays of "n" items. It returns 0 if they are
2228  * the "same" and 1 otherwise.  Although suboptimal, it is always safe to
2229  * return 1, which makes compare_nmount_export() reload the exports entry.
2230  * "same" refers to having the same set of values in the two arrays.
2231  * The arrays are in no particular order and duplicates (multiple entries
2232  * in an array with the same value) is allowed.
2233  * The algorithm is inefficient, but the common case of indentical arrays is
2234  * handled first and "n" is normally fairly small.
2235  * Since the two functions need the same algorithm but for arrays of
2236  * different types (gid_t vs int), this is done as a macro.
2237  */
2238 #define COMPARE_ARRAYS(a1, a2, n)                                       \
2239         do {                                                            \
2240                 int fnd, fndarray[(n)], i, j;                           \
2241                 /* Handle common case of identical arrays. */           \
2242                 for (i = 0; i < (n); i++)                               \
2243                         if ((a1)[i] != (a2)[i])                         \
2244                                 break;                                  \
2245                 if (i == (n))                                           \
2246                         return (0);                                     \
2247                 for (i = 0; i < (n); i++)                               \
2248                         fndarray[i] = 0;                                \
2249                 for (i = 0; i < (n); i++) {                             \
2250                         fnd = 0;                                        \
2251                         for (j = 0; j < (n); j++) {                     \
2252                                 if ((a1)[i] == (a2)[j]) {               \
2253                                         fndarray[j] = 1;                \
2254                                         fnd = 1;                        \
2255                                 }                                       \
2256                         }                                               \
2257                         if (fnd == 0)                                   \
2258                                 return (1);                             \
2259                 }                                                       \
2260                 for (i = 0; i < (n); i++)                               \
2261                         if (fndarray[i] == 0)                           \
2262                                 return (1);                             \
2263                 return (0);                                             \
2264         } while (0)
2265
2266 /*
2267  * Compare two struct expcred's.  Return 0 if the same and 1 otherwise.
2268  */
2269 static int
2270 compare_cred(struct expcred *cr0, struct expcred *cr1)
2271 {
2272
2273         if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
2274                 return (1);
2275
2276         COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups);
2277 }
2278
2279 /*
2280  * Compare two lists of security flavors.  Return 0 if the same and 1 otherwise.
2281  */
2282 static int
2283 compare_secflavor(int *sec1, int *sec2, int nsec)
2284 {
2285
2286         COMPARE_ARRAYS(sec1, sec2, nsec);
2287 }
2288
2289 /*
2290  * Delete an exports entry.
2291  */
2292 static void
2293 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
2294 {
2295         struct xvfsconf vfc;
2296
2297         if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
2298                 syslog(LOG_ERR, "getvfsbyname() failed for %s",
2299                     fsp->f_fstypename);
2300                 return;
2301         }
2302         
2303         /*
2304          * We do not need to delete "export" flag from
2305          * filesystems that do not have it set.
2306          */
2307         if (!(fsp->f_flags & MNT_EXPORTED))
2308                 return;
2309         /*
2310          * Do not delete export for network filesystem by
2311          * passing "export" arg to nmount().
2312          * It only makes sense to do this for local filesystems.
2313          */
2314         if (vfc.vfc_flags & VFCF_NETWORK)
2315                 return;
2316         
2317         iov[1].iov_base = fsp->f_fstypename;
2318         iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
2319         iov[3].iov_base = fsp->f_mntonname;
2320         iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
2321         iov[5].iov_base = fsp->f_mntfromname;
2322         iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
2323         errmsg[0] = '\0';
2324         
2325         /*
2326          * EXDEV is returned when path exists but is not a
2327          * mount point.  May happens if raced with unmount.
2328          */
2329         if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
2330             errno != ENOTSUP && errno != EXDEV) {
2331                 syslog(LOG_ERR,
2332                     "can't delete exports for %s: %m %s",
2333                     fsp->f_mntonname, errmsg);
2334         }
2335 }
2336
2337 /*
2338  * Allocate an export list element
2339  */
2340 static struct exportlist *
2341 get_exp(void)
2342 {
2343         struct exportlist *ep;
2344
2345         ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
2346         if (ep == (struct exportlist *)NULL)
2347                 out_of_mem();
2348         return (ep);
2349 }
2350
2351 /*
2352  * Allocate a group list element
2353  */
2354 static struct grouplist *
2355 get_grp(void)
2356 {
2357         struct grouplist *gp;
2358
2359         gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
2360         if (gp == (struct grouplist *)NULL)
2361                 out_of_mem();
2362         return (gp);
2363 }
2364
2365 /*
2366  * Clean up upon an error in get_exportlist().
2367  */
2368 static void
2369 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
2370 {
2371         struct grouplist *tgrp;
2372
2373         if (!(opt_flags & OP_QUIET)) {
2374                 if (reason != NULL)
2375                         syslog(LOG_ERR, "bad exports list line '%s': %s", line,
2376                             reason);
2377                 else
2378                         syslog(LOG_ERR, "bad exports list line '%s'", line);
2379         }
2380         if (ep && (ep->ex_flag & EX_LINKED) == 0)
2381                 free_exp(ep);
2382         while (grp) {
2383                 tgrp = grp;
2384                 grp = grp->gr_next;
2385                 free_grp(tgrp);
2386         }
2387 }
2388
2389 /*
2390  * Search the export list for a matching fs.
2391  */
2392 static struct exportlist *
2393 ex_search(fsid_t *fsid, struct exportlisthead *exhp)
2394 {
2395         struct exportlist *ep;
2396         uint32_t i;
2397
2398         i = EXPHASH(fsid);
2399         SLIST_FOREACH(ep, &exhp[i], entries) {
2400                 if (fsidcmp(&ep->ex_fs, fsid) == 0)
2401                         return (ep);
2402         }
2403
2404         return (ep);
2405 }
2406
2407 /*
2408  * Add a directory path to the list.
2409  */
2410 static char *
2411 add_expdir(struct dirlist **dpp, char *cp, int len)
2412 {
2413         struct dirlist *dp;
2414
2415         dp = malloc(sizeof (struct dirlist));
2416         if (dp == (struct dirlist *)NULL)
2417                 out_of_mem();
2418         dp->dp_left = *dpp;
2419         dp->dp_right = (struct dirlist *)NULL;
2420         dp->dp_flag = 0;
2421         dp->dp_hosts = (struct hostlist *)NULL;
2422         dp->dp_dirp = strndup(cp, len);
2423         if (dp->dp_dirp == NULL)
2424                 out_of_mem();
2425         *dpp = dp;
2426         return (dp->dp_dirp);
2427 }
2428
2429 /*
2430  * Hang the dir list element off the dirpath binary tree as required
2431  * and update the entry for host.
2432  */
2433 static void
2434 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2435         int flags, struct expcred *anoncrp, uint64_t exflags)
2436 {
2437         struct hostlist *hp;
2438         struct dirlist *dp2;
2439
2440         if (flags & OP_ALLDIRS) {
2441                 if (ep->ex_defdir)
2442                         free((caddr_t)dp);
2443                 else
2444                         ep->ex_defdir = dp;
2445                 if (grp == (struct grouplist *)NULL) {
2446                         ep->ex_flag |= EX_DEFSET;
2447                         ep->ex_defdir->dp_flag |= DP_DEFSET;
2448                         /* Save the default security flavors list. */
2449                         ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2450                         if (ep->ex_numsecflavors > 0)
2451                                 memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2452                                     sizeof(ep->ex_secflavors));
2453                         cp_cred(&ep->ex_defanon, anoncrp);
2454                         ep->ex_defexflags = exflags;
2455                 } else while (grp) {
2456                         hp = get_ht();
2457                         hp->ht_grp = grp;
2458                         hp->ht_next = ep->ex_defdir->dp_hosts;
2459                         ep->ex_defdir->dp_hosts = hp;
2460                         /* Save the security flavors list for this host set. */
2461                         grp->gr_numsecflavors = ep->ex_numsecflavors;
2462                         if (ep->ex_numsecflavors > 0)
2463                                 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2464                                     sizeof(ep->ex_secflavors));
2465                         grp = grp->gr_next;
2466                 }
2467         } else {
2468
2469                 /*
2470                  * Loop through the directories adding them to the tree.
2471                  */
2472                 while (dp) {
2473                         dp2 = dp->dp_left;
2474                         add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp,
2475                             exflags);
2476                         dp = dp2;
2477                 }
2478         }
2479 }
2480
2481 /*
2482  * Traverse the binary tree either updating a node that is already there
2483  * for the new directory or adding the new node.
2484  */
2485 static void
2486 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2487         int flags, struct exportlist *ep, struct expcred *anoncrp,
2488         uint64_t exflags)
2489 {
2490         struct dirlist *dp;
2491         struct hostlist *hp;
2492         int cmp;
2493
2494         dp = *dpp;
2495         if (dp) {
2496                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2497                 if (cmp > 0) {
2498                         add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp,
2499                             exflags);
2500                         return;
2501                 } else if (cmp < 0) {
2502                         add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp,
2503                             exflags);
2504                         return;
2505                 } else
2506                         free((caddr_t)newdp);
2507         } else {
2508                 dp = newdp;
2509                 dp->dp_left = (struct dirlist *)NULL;
2510                 *dpp = dp;
2511         }
2512         if (grp) {
2513
2514                 /*
2515                  * Hang all of the host(s) off of the directory point.
2516                  */
2517                 do {
2518                         hp = get_ht();
2519                         hp->ht_grp = grp;
2520                         hp->ht_next = dp->dp_hosts;
2521                         dp->dp_hosts = hp;
2522                         /* Save the security flavors list for this host set. */
2523                         grp->gr_numsecflavors = ep->ex_numsecflavors;
2524                         if (ep->ex_numsecflavors > 0)
2525                                 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2526                                     sizeof(ep->ex_secflavors));
2527                         grp = grp->gr_next;
2528                 } while (grp);
2529         } else {
2530                 ep->ex_flag |= EX_DEFSET;
2531                 dp->dp_flag |= DP_DEFSET;
2532                 /* Save the default security flavors list. */
2533                 ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2534                 if (ep->ex_numsecflavors > 0)
2535                         memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2536                             sizeof(ep->ex_secflavors));
2537                 cp_cred(&ep->ex_defanon, anoncrp);
2538                 ep->ex_defexflags = exflags;
2539         }
2540 }
2541
2542 /*
2543  * Search for a dirpath on the export point.
2544  */
2545 static struct dirlist *
2546 dirp_search(struct dirlist *dp, char *dirp)
2547 {
2548         int cmp;
2549
2550         if (dp) {
2551                 cmp = strcmp(dp->dp_dirp, dirp);
2552                 if (cmp > 0)
2553                         return (dirp_search(dp->dp_left, dirp));
2554                 else if (cmp < 0)
2555                         return (dirp_search(dp->dp_right, dirp));
2556                 else
2557                         return (dp);
2558         }
2559         return (dp);
2560 }
2561
2562 /*
2563  * Scan for a host match in a directory tree.
2564  */
2565 static int
2566 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2567         int *hostsetp, int *numsecflavors, int **secflavorsp)
2568 {
2569         struct hostlist *hp;
2570         struct grouplist *grp;
2571         struct addrinfo *ai;
2572
2573         if (dp) {
2574                 if (dp->dp_flag & DP_DEFSET)
2575                         *defsetp = dp->dp_flag;
2576                 hp = dp->dp_hosts;
2577                 while (hp) {
2578                         grp = hp->ht_grp;
2579                         switch (grp->gr_type) {
2580                         case GT_HOST:
2581                                 ai = grp->gr_ptr.gt_addrinfo;
2582                                 for (; ai; ai = ai->ai_next) {
2583                                         if (!sacmp(ai->ai_addr, saddr, NULL)) {
2584                                                 *hostsetp =
2585                                                     (hp->ht_flag | DP_HOSTSET);
2586                                                 if (numsecflavors != NULL) {
2587                                                         *numsecflavors =
2588                                                             grp->gr_numsecflavors;
2589                                                         *secflavorsp =
2590                                                             grp->gr_secflavors;
2591                                                 }
2592                                                 return (1);
2593                                         }
2594                                 }
2595                                 break;
2596                         case GT_NET:
2597                                 if (!sacmp(saddr, (struct sockaddr *)
2598                                     &grp->gr_ptr.gt_net.nt_net,
2599                                     (struct sockaddr *)
2600                                     &grp->gr_ptr.gt_net.nt_mask)) {
2601                                         *hostsetp = (hp->ht_flag | DP_HOSTSET);
2602                                         if (numsecflavors != NULL) {
2603                                                 *numsecflavors =
2604                                                     grp->gr_numsecflavors;
2605                                                 *secflavorsp =
2606                                                     grp->gr_secflavors;
2607                                         }
2608                                         return (1);
2609                                 }
2610                                 break;
2611                         }
2612                         hp = hp->ht_next;
2613                 }
2614         }
2615         return (0);
2616 }
2617
2618 /*
2619  * Scan tree for a host that matches the address.
2620  */
2621 static int
2622 scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2623 {
2624         int defset, hostset;
2625
2626         if (dp) {
2627                 if (scan_tree(dp->dp_left, saddr))
2628                         return (1);
2629                 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2630                         return (1);
2631                 if (scan_tree(dp->dp_right, saddr))
2632                         return (1);
2633         }
2634         return (0);
2635 }
2636
2637 /*
2638  * Traverse the dirlist tree and free it up.
2639  */
2640 static void
2641 free_dir(struct dirlist *dp)
2642 {
2643
2644         if (dp) {
2645                 free_dir(dp->dp_left);
2646                 free_dir(dp->dp_right);
2647                 free_host(dp->dp_hosts);
2648                 free(dp->dp_dirp);
2649                 free(dp);
2650         }
2651 }
2652
2653 /*
2654  * Parse a colon separated list of security flavors
2655  */
2656 static int
2657 parsesec(char *seclist, struct exportlist *ep)
2658 {
2659         char *cp, savedc;
2660         int flavor;
2661
2662         ep->ex_numsecflavors = 0;
2663         for (;;) {
2664                 cp = strchr(seclist, ':');
2665                 if (cp) {
2666                         savedc = *cp;
2667                         *cp = '\0';
2668                 }
2669
2670                 if (!strcmp(seclist, "sys"))
2671                         flavor = AUTH_SYS;
2672                 else if (!strcmp(seclist, "krb5"))
2673                         flavor = RPCSEC_GSS_KRB5;
2674                 else if (!strcmp(seclist, "krb5i"))
2675                         flavor = RPCSEC_GSS_KRB5I;
2676                 else if (!strcmp(seclist, "krb5p"))
2677                         flavor = RPCSEC_GSS_KRB5P;
2678                 else {
2679                         if (cp)
2680                                 *cp = savedc;
2681                         syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2682                         return (1);
2683                 }
2684                 if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2685                         if (cp)
2686                                 *cp = savedc;
2687                         syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2688                         return (1);
2689                 }
2690                 ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2691                 ep->ex_numsecflavors++;
2692                 if (cp) {
2693                         *cp = savedc;
2694                         seclist = cp + 1;
2695                 } else {
2696                         break;
2697                 }
2698         }
2699         return (0);
2700 }
2701
2702 /*
2703  * Parse the option string and update fields.
2704  * Option arguments may either be -<option>=<value> or
2705  * -<option> <value>
2706  */
2707 static int
2708 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2709         int *has_hostp, uint64_t *exflagsp, struct expcred *cr)
2710 {
2711         char *cpoptarg, *cpoptend;
2712         char *cp, *endcp, *cpopt, savedc, savedc2;
2713         int allflag, usedarg;
2714
2715         savedc2 = '\0';
2716         cpopt = *cpp;
2717         cpopt++;
2718         cp = *endcpp;
2719         savedc = *cp;
2720         *cp = '\0';
2721         while (cpopt && *cpopt) {
2722                 allflag = 1;
2723                 usedarg = -2;
2724                 if ((cpoptend = strchr(cpopt, ','))) {
2725                         *cpoptend++ = '\0';
2726                         if ((cpoptarg = strchr(cpopt, '=')))
2727                                 *cpoptarg++ = '\0';
2728                 } else {
2729                         if ((cpoptarg = strchr(cpopt, '=')))
2730                                 *cpoptarg++ = '\0';
2731                         else {
2732                                 *cp = savedc;
2733                                 nextfield(&cp, &endcp);
2734                                 **endcpp = '\0';
2735                                 if (endcp > cp && *cp != '-') {
2736                                         cpoptarg = cp;
2737                                         savedc2 = *endcp;
2738                                         *endcp = '\0';
2739                                         usedarg = 0;
2740                                 }
2741                         }
2742                 }
2743                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2744                         *exflagsp |= MNT_EXRDONLY;
2745                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2746                     !(allflag = strcmp(cpopt, "mapall")) ||
2747                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2748                         usedarg++;
2749                         parsecred(cpoptarg, cr);
2750                         if (allflag == 0) {
2751                                 *exflagsp |= MNT_EXPORTANON;
2752                                 opt_flags |= OP_MAPALL;
2753                         } else
2754                                 opt_flags |= OP_MAPROOT;
2755                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2756                     !strcmp(cpopt, "m"))) {
2757                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2758                                 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2759                                 return (1);
2760                         }
2761                         usedarg++;
2762                         opt_flags |= OP_MASK;
2763                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
2764                         !strcmp(cpopt, "n"))) {
2765                         if (strchr(cpoptarg, '/') != NULL) {
2766                                 if (debug)
2767                                         fprintf(stderr, "setting OP_MASKLEN\n");
2768                                 opt_flags |= OP_MASKLEN;
2769                         }
2770                         if (grp->gr_type != GT_NULL) {
2771                                 syslog(LOG_ERR, "network/host conflict");
2772                                 return (1);
2773                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2774                                 syslog(LOG_ERR, "bad net: %s", cpoptarg);
2775                                 return (1);
2776                         }
2777                         grp->gr_type = GT_NET;
2778                         *has_hostp = 1;
2779                         usedarg++;
2780                         opt_flags |= OP_NET;
2781                 } else if (!strcmp(cpopt, "alldirs")) {
2782                         opt_flags |= OP_ALLDIRS;
2783                 } else if (!strcmp(cpopt, "public")) {
2784                         *exflagsp |= MNT_EXPUBLIC;
2785                 } else if (!strcmp(cpopt, "webnfs")) {
2786                         *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2787                         opt_flags |= OP_MAPALL;
2788                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
2789                         ep->ex_indexfile = strdup(cpoptarg);
2790                 } else if (!strcmp(cpopt, "quiet")) {
2791                         opt_flags |= OP_QUIET;
2792                 } else if (cpoptarg && !strcmp(cpopt, "sec")) {
2793                         if (parsesec(cpoptarg, ep))
2794                                 return (1);
2795                         opt_flags |= OP_SEC;
2796                         usedarg++;
2797                 } else {
2798                         syslog(LOG_ERR, "bad opt %s", cpopt);
2799                         return (1);
2800                 }
2801                 if (usedarg >= 0) {
2802                         *endcp = savedc2;
2803                         **endcpp = savedc;
2804                         if (usedarg > 0) {
2805                                 *cpp = cp;
2806                                 *endcpp = endcp;
2807                         }
2808                         return (0);
2809                 }
2810                 cpopt = cpoptend;
2811         }
2812         **endcpp = savedc;
2813         return (0);
2814 }
2815
2816 /*
2817  * Translate a character string to the corresponding list of network
2818  * addresses for a hostname.
2819  */
2820 static int
2821 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2822 {
2823         struct grouplist *checkgrp;
2824         struct addrinfo *ai, *tai, hints;
2825         int ecode;
2826         char host[NI_MAXHOST];
2827
2828         if (grp->gr_type != GT_NULL) {
2829                 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2830                 return (1);
2831         }
2832         memset(&hints, 0, sizeof hints);
2833         hints.ai_flags = AI_CANONNAME;
2834         hints.ai_protocol = IPPROTO_UDP;
2835         ecode = getaddrinfo(cp, NULL, &hints, &ai);
2836         if (ecode != 0) {
2837                 syslog(LOG_ERR,"can't get address info for host %s", cp);
2838                 return 1;
2839         }
2840         grp->gr_ptr.gt_addrinfo = ai;
2841         while (ai != NULL) {
2842                 if (ai->ai_canonname == NULL) {
2843                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2844                             sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2845                                 strlcpy(host, "?", sizeof(host));
2846                         ai->ai_canonname = strdup(host);
2847                         ai->ai_flags |= AI_CANONNAME;
2848                 }
2849                 if (debug)
2850                         fprintf(stderr, "got host %s\n", ai->ai_canonname);
2851                 /*
2852                  * Sanity check: make sure we don't already have an entry
2853                  * for this host in the grouplist.
2854                  */
2855                 for (checkgrp = tgrp; checkgrp != NULL;
2856                     checkgrp = checkgrp->gr_next) {
2857                         if (checkgrp->gr_type != GT_HOST)
2858                                 continue;
2859                         for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2860                             tai = tai->ai_next) {
2861                                 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2862                                         continue;
2863                                 if (debug)
2864                                         fprintf(stderr,
2865                                             "ignoring duplicate host %s\n",
2866                                             ai->ai_canonname);
2867                                 grp->gr_type = GT_IGNORE;
2868                                 return (0);
2869                         }
2870                 }
2871                 ai = ai->ai_next;
2872         }
2873         grp->gr_type = GT_HOST;
2874         return (0);
2875 }
2876
2877 /*
2878  * Free up an exports list component
2879  */
2880 static void
2881 free_exp(struct exportlist *ep)
2882 {
2883         struct grouplist *grp, *tgrp;
2884
2885         if (ep->ex_defdir) {
2886                 free_host(ep->ex_defdir->dp_hosts);
2887                 free((caddr_t)ep->ex_defdir);
2888         }
2889         if (ep->ex_fsdir)
2890                 free(ep->ex_fsdir);
2891         if (ep->ex_indexfile)
2892                 free(ep->ex_indexfile);
2893         free_dir(ep->ex_dirl);
2894         grp = ep->ex_grphead;
2895         while (grp) {
2896                 tgrp = grp;
2897                 grp = grp->gr_next;
2898                 free_grp(tgrp);
2899         }
2900         free((caddr_t)ep);
2901 }
2902
2903 /*
2904  * Free up the v4root exports.
2905  */
2906 static void
2907 free_v4rootexp(void)
2908 {
2909
2910         if (v4root_ep != NULL) {
2911                 free_exp(v4root_ep);
2912                 v4root_ep = NULL;
2913         }
2914 }
2915
2916 /*
2917  * Free hosts.
2918  */
2919 static void
2920 free_host(struct hostlist *hp)
2921 {
2922         struct hostlist *hp2;
2923
2924         while (hp) {
2925                 hp2 = hp;
2926                 hp = hp->ht_next;
2927                 free((caddr_t)hp2);
2928         }
2929 }
2930
2931 static struct hostlist *
2932 get_ht(void)
2933 {
2934         struct hostlist *hp;
2935
2936         hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2937         if (hp == (struct hostlist *)NULL)
2938                 out_of_mem();
2939         hp->ht_next = (struct hostlist *)NULL;
2940         hp->ht_flag = 0;
2941         return (hp);
2942 }
2943
2944 /*
2945  * Out of memory, fatal
2946  */
2947 static void
2948 out_of_mem(void)
2949 {
2950
2951         syslog(LOG_ERR, "out of memory");
2952         exit(2);
2953 }
2954
2955 /*
2956  * Call do_mount() from the struct exportlist, for each case needed.
2957  */
2958 static int
2959 do_export_mount(struct exportlist *ep, struct statfs *fsp)
2960 {
2961         struct grouplist *grp, defgrp;
2962         int ret;
2963         size_t dirlen;
2964
2965         LOGDEBUG("do_export_mount=%s", ep->ex_fsdir);
2966         dirlen = strlen(ep->ex_fsdir);
2967         if ((ep->ex_flag & EX_DEFSET) != 0) {
2968                 defgrp.gr_type = GT_DEFAULT;
2969                 defgrp.gr_next = NULL;
2970                 /* We have an entry for all other hosts/nets. */
2971                 LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags);
2972                 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
2973                     ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors,
2974                     ep->ex_defsecflavors);
2975                 if (ret != 0)
2976                         return (ret);
2977         }
2978
2979         /* Do a mount for each group. */
2980         grp = ep->ex_grphead;
2981         while (grp != NULL) {
2982                 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx",
2983                     grp->gr_type, (uintmax_t)grp->gr_exflags);
2984                 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
2985                     ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors,
2986                     grp->gr_secflavors);
2987                 if (ret != 0)
2988                         return (ret);
2989                 grp = grp->gr_next;
2990         }
2991         return (0);
2992 }
2993
2994 /*
2995  * Do the nmount() syscall with the update flag to push the export info into
2996  * the kernel.
2997  */
2998 static int
2999 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
3000     struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb,
3001     int numsecflavors, int *secflavors)
3002 {
3003         struct statfs fsb1;
3004         struct addrinfo *ai;
3005         struct export_args *eap;
3006         char errmsg[255];
3007         char *cp;
3008         int done;
3009         char savedc;
3010         struct iovec *iov;
3011         int i, iovlen;
3012         int ret;
3013         struct nfsex_args nfsea;
3014
3015         eap = &nfsea.export;
3016
3017         cp = NULL;
3018         savedc = '\0';
3019         iov = NULL;
3020         iovlen = 0;
3021         ret = 0;
3022
3023         bzero(eap, sizeof (struct export_args));
3024         bzero(errmsg, sizeof(errmsg));
3025         eap->ex_flags = exflags;
3026         eap->ex_uid = anoncrp->cr_uid;
3027         eap->ex_ngroups = anoncrp->cr_ngroups;
3028         if (eap->ex_ngroups > 0) {
3029                 eap->ex_groups = malloc(eap->ex_ngroups * sizeof(gid_t));
3030                 memcpy(eap->ex_groups, anoncrp->cr_groups, eap->ex_ngroups *
3031                     sizeof(gid_t));
3032         }
3033         LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags);
3034         eap->ex_indexfile = ep->ex_indexfile;
3035         if (grp->gr_type == GT_HOST)
3036                 ai = grp->gr_ptr.gt_addrinfo;
3037         else
3038                 ai = NULL;
3039         eap->ex_numsecflavors = numsecflavors;
3040         LOGDEBUG("do_mount numsec=%d", numsecflavors);
3041         for (i = 0; i < eap->ex_numsecflavors; i++)
3042                 eap->ex_secflavors[i] = secflavors[i];
3043         if (eap->ex_numsecflavors == 0) {
3044                 eap->ex_numsecflavors = 1;
3045                 eap->ex_secflavors[0] = AUTH_SYS;
3046         }
3047         done = FALSE;
3048
3049         if (v4root_phase == 0) {
3050                 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
3051                 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
3052                 build_iovec(&iov, &iovlen, "from", NULL, 0);
3053                 build_iovec(&iov, &iovlen, "update", NULL, 0);
3054                 build_iovec(&iov, &iovlen, "export", eap,
3055                     sizeof (struct export_args));
3056                 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
3057         }
3058
3059         while (!done) {
3060                 switch (grp->gr_type) {
3061                 case GT_HOST:
3062                         if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
3063                                 goto skip;
3064                         eap->ex_addr = ai->ai_addr;
3065                         eap->ex_addrlen = ai->ai_addrlen;
3066                         eap->ex_masklen = 0;
3067                         break;
3068                 case GT_NET:
3069                         if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
3070                             have_v6 == 0)
3071                                 goto skip;
3072                         eap->ex_addr =
3073                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
3074                         eap->ex_addrlen =
3075                             ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
3076                         eap->ex_mask =
3077                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
3078                         eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
3079                         break;
3080                 case GT_DEFAULT:
3081                         eap->ex_addr = NULL;
3082                         eap->ex_addrlen = 0;
3083                         eap->ex_mask = NULL;
3084                         eap->ex_masklen = 0;
3085                         break;
3086                 case GT_IGNORE:
3087                         ret = 0;
3088                         goto error_exit;
3089                         break;
3090                 default:
3091                         syslog(LOG_ERR, "bad grouptype");
3092                         if (cp)
3093                                 *cp = savedc;
3094                         ret = 1;
3095                         goto error_exit;
3096                 }
3097
3098                 /*
3099                  * For V4:, use the nfssvc() syscall, instead of mount().
3100                  */
3101                 if (v4root_phase == 2) {
3102                         nfsea.fspec = v4root_dirpath;
3103                         if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT,
3104                             (caddr_t)&nfsea) < 0) {
3105                                 syslog(LOG_ERR, "Exporting V4: failed");
3106                                 ret = 2;
3107                                 goto error_exit;
3108                         }
3109                 } else {
3110                         /*
3111                          * XXX:
3112                          * Maybe I should just use the fsb->f_mntonname path
3113                          * instead of looping back up the dirp to the mount
3114                          * point??
3115                          * Also, needs to know how to export all types of local
3116                          * exportable filesystems and not just "ufs".
3117                          */
3118                         iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
3119                         iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
3120                         iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
3121                         iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
3122                         iov[5].iov_base = fsb->f_mntfromname; /* "from" */
3123                         iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
3124                         errmsg[0] = '\0';
3125         
3126                         while (nmount(iov, iovlen, fsb->f_flags) < 0) {
3127                                 if (cp)
3128                                         *cp-- = savedc;
3129                                 else
3130                                         cp = dirp + dirplen - 1;
3131                                 if (opt_flags & OP_QUIET) {
3132                                         ret = 1;
3133                                         goto error_exit;
3134                                 }
3135                                 if (errno == EPERM) {
3136                                         if (debug)
3137                                                 warnx("can't change attributes for %s: %s",
3138                                                     dirp, errmsg);
3139                                         syslog(LOG_ERR,
3140                                            "can't change attributes for %s: %s",
3141                                             dirp, errmsg);
3142                                         ret = 1;
3143                                         goto error_exit;
3144                                 }
3145                                 if (opt_flags & OP_ALLDIRS) {
3146                                         if (errno == EINVAL)
3147                                                 syslog(LOG_ERR,
3148                 "-alldirs requested but %s is not a filesystem mountpoint",
3149                                                     dirp);
3150                                         else
3151                                                 syslog(LOG_ERR,
3152                                                     "could not remount %s: %m",
3153                                                     dirp);
3154                                         ret = 1;
3155                                         goto error_exit;
3156                                 }
3157                                 /* back up over the last component */
3158                                 while (*cp == '/' && cp > dirp)
3159                                         cp--;
3160                                 while (*(cp - 1) != '/' && cp > dirp)
3161                                         cp--;
3162                                 if (cp == dirp) {
3163                                         if (debug)
3164                                                 warnx("mnt unsucc");
3165                                         syslog(LOG_ERR, "can't export %s %s",
3166                                             dirp, errmsg);
3167                                         ret = 1;
3168                                         goto error_exit;
3169                                 }
3170                                 savedc = *cp;
3171                                 *cp = '\0';
3172                                 /*
3173                                  * Check that we're still on the same
3174                                  * filesystem.
3175                                  */
3176                                 if (statfs(dirp, &fsb1) != 0 ||
3177                                     fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) {
3178                                         *cp = savedc;
3179                                         syslog(LOG_ERR,
3180                                             "can't export %s %s", dirp,
3181                                             errmsg);
3182                                         ret = 1;
3183                                         goto error_exit;
3184                                 }
3185                         }
3186                 }
3187
3188                 /*
3189                  * For the experimental server:
3190                  * If this is the public directory, get the file handle
3191                  * and load it into the kernel via the nfssvc() syscall.
3192                  */
3193                 if ((exflags & MNT_EXPUBLIC) != 0) {
3194                         fhandle_t fh;
3195                         char *public_name;
3196
3197                         if (eap->ex_indexfile != NULL)
3198                                 public_name = eap->ex_indexfile;
3199                         else
3200                                 public_name = dirp;
3201                         if (getfh(public_name, &fh) < 0)
3202                                 syslog(LOG_ERR,
3203                                     "Can't get public fh for %s", public_name);
3204                         else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
3205                                 syslog(LOG_ERR,
3206                                     "Can't set public fh for %s", public_name);
3207                         else {
3208                                 has_publicfh = 1;
3209                                 has_set_publicfh = 1;
3210                                 ep->ex_flag |= EX_PUBLICFH;
3211                         }
3212                 }
3213 skip:
3214                 if (ai != NULL)
3215                         ai = ai->ai_next;
3216                 if (ai == NULL)
3217                         done = TRUE;
3218         }
3219         if (cp)
3220                 *cp = savedc;
3221 error_exit:
3222         free(eap->ex_groups);
3223         /* free strings allocated by strdup() in getmntopts.c */
3224         if (iov != NULL) {
3225                 free(iov[0].iov_base); /* fstype */
3226                 free(iov[2].iov_base); /* fspath */
3227                 free(iov[4].iov_base); /* from */
3228                 free(iov[6].iov_base); /* update */
3229                 free(iov[8].iov_base); /* export */
3230                 free(iov[10].iov_base); /* errmsg */
3231
3232                 /* free iov, allocated by realloc() */
3233                 free(iov);
3234         }
3235         return (ret);
3236 }
3237
3238 /*
3239  * Translate a net address.
3240  *
3241  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
3242  */
3243 static int
3244 get_net(char *cp, struct netmsk *net, int maskflg)
3245 {
3246         struct netent *np = NULL;
3247         char *name, *p, *prefp;
3248         struct sockaddr_in sin;
3249         struct sockaddr *sa = NULL;
3250         struct addrinfo hints, *ai = NULL;
3251         char netname[NI_MAXHOST];
3252         long preflen;
3253
3254         p = prefp = NULL;
3255         if ((opt_flags & OP_MASKLEN) && !maskflg) {
3256                 p = strchr(cp, '/');
3257                 *p = '\0';
3258                 prefp = p + 1;
3259         }
3260
3261         /*
3262          * Check for a numeric address first. We wish to avoid
3263          * possible DNS lookups in getnetbyname().
3264          */
3265         if (isxdigit(*cp) || *cp == ':') {
3266                 memset(&hints, 0, sizeof hints);
3267                 /* Ensure the mask and the network have the same family. */
3268                 if (maskflg && (opt_flags & OP_NET))
3269                         hints.ai_family = net->nt_net.ss_family;
3270                 else if (!maskflg && (opt_flags & OP_HAVEMASK))
3271                         hints.ai_family = net->nt_mask.ss_family;
3272                 else
3273                         hints.ai_family = AF_UNSPEC;
3274                 hints.ai_flags = AI_NUMERICHOST;
3275                 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
3276                         sa = ai->ai_addr;
3277                 if (sa != NULL && ai->ai_family == AF_INET) {
3278                         /*
3279                          * The address in `cp' is really a network address, so
3280                          * use inet_network() to re-interpret this correctly.
3281                          * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
3282                          */
3283                         bzero(&sin, sizeof sin);
3284                         sin.sin_family = AF_INET;
3285                         sin.sin_len = sizeof sin;
3286                         sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
3287                         if (debug)
3288                                 fprintf(stderr, "get_net: v4 addr %s\n",
3289                                     inet_ntoa(sin.sin_addr));
3290                         sa = (struct sockaddr *)&sin;
3291                 }
3292         }
3293         if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
3294                 bzero(&sin, sizeof sin);
3295                 sin.sin_family = AF_INET;
3296                 sin.sin_len = sizeof sin;
3297                 sin.sin_addr = inet_makeaddr(np->n_net, 0);
3298                 sa = (struct sockaddr *)&sin;
3299         }
3300         if (sa == NULL)
3301                 goto fail;
3302
3303         if (maskflg) {
3304                 /* The specified sockaddr is a mask. */
3305                 if (checkmask(sa) != 0)
3306                         goto fail;
3307                 bcopy(sa, &net->nt_mask, sa->sa_len);
3308                 opt_flags |= OP_HAVEMASK;
3309         } else {
3310                 /* The specified sockaddr is a network address. */
3311                 bcopy(sa, &net->nt_net, sa->sa_len);
3312
3313                 /* Get a network name for the export list. */
3314                 if (np) {
3315                         name = np->n_name;
3316                 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
3317                    NULL, 0, NI_NUMERICHOST) == 0) {
3318                         name = netname;
3319                 } else {
3320                         goto fail;
3321                 }
3322                 if ((net->nt_name = strdup(name)) == NULL)
3323                         out_of_mem();
3324
3325                 /*
3326                  * Extract a mask from either a "/<masklen>" suffix, or
3327                  * from the class of an IPv4 address.
3328                  */
3329                 if (opt_flags & OP_MASKLEN) {
3330                         preflen = strtol(prefp, NULL, 10);
3331                         if (preflen < 0L || preflen == LONG_MAX)
3332                                 goto fail;
3333                         bcopy(sa, &net->nt_mask, sa->sa_len);
3334                         if (makemask(&net->nt_mask, (int)preflen) != 0)
3335                                 goto fail;
3336                         opt_flags |= OP_HAVEMASK;
3337                         *p = '/';
3338                 } else if (sa->sa_family == AF_INET &&
3339                     (opt_flags & OP_MASK) == 0) {
3340                         in_addr_t addr;
3341
3342                         addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
3343                         if (IN_CLASSA(addr))
3344                                 preflen = 8;
3345                         else if (IN_CLASSB(addr))
3346                                 preflen = 16;
3347                         else if (IN_CLASSC(addr))
3348                                 preflen = 24;
3349                         else if (IN_CLASSD(addr))
3350                                 preflen = 28;
3351                         else
3352                                 preflen = 32;   /* XXX */
3353
3354                         bcopy(sa, &net->nt_mask, sa->sa_len);
3355                         makemask(&net->nt_mask, (int)preflen);
3356                         opt_flags |= OP_HAVEMASK;
3357                 }
3358         }
3359
3360         if (ai)
3361                 freeaddrinfo(ai);
3362         return 0;
3363
3364 fail:
3365         if (ai)
3366                 freeaddrinfo(ai);
3367         return 1;
3368 }
3369
3370 /*
3371  * Parse out the next white space separated field
3372  */
3373 static void
3374 nextfield(char **cp, char **endcp)
3375 {
3376         char *p;
3377         char quot = 0;
3378
3379         p = *cp;
3380         while (*p == ' ' || *p == '\t')
3381                 p++;
3382         *cp = p;
3383         while (*p != '\0') {
3384                 if (quot) {
3385                         if (*p == quot)
3386                                 quot = 0;
3387                 } else {
3388                         if (*p == '\\' && *(p + 1) != '\0')
3389                                 p++;
3390                         else if (*p == '\'' || *p == '"')
3391                                 quot = *p;
3392                         else if (*p == ' ' || *p == '\t')
3393                                 break;
3394                 }
3395                 p++;
3396         };
3397         *endcp = p;
3398 }
3399
3400 /*
3401  * Get an exports file line. Skip over blank lines and handle line
3402  * continuations.
3403  */
3404 static int
3405 get_line(void)
3406 {
3407         char *p, *cp;
3408         size_t len;
3409         int totlen, cont_line;
3410
3411         /*
3412          * Loop around ignoring blank lines and getting all continuation lines.
3413          */
3414         p = line;
3415         totlen = 0;
3416         do {
3417                 if ((p = fgetln(exp_file, &len)) == NULL)
3418                         return (0);
3419                 cp = p + len - 1;
3420                 cont_line = 0;
3421                 while (cp >= p &&
3422                     (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
3423                         if (*cp == '\\')
3424                                 cont_line = 1;
3425                         cp--;
3426                         len--;
3427                 }
3428                 if (cont_line) {
3429                         *++cp = ' ';
3430                         len++;
3431                 }
3432                 if (linesize < len + totlen + 1) {
3433                         linesize = len + totlen + 1;
3434                         line = realloc(line, linesize);
3435                         if (line == NULL)
3436                                 out_of_mem();
3437                 }
3438                 memcpy(line + totlen, p, len);
3439                 totlen += len;
3440                 line[totlen] = '\0';
3441         } while (totlen == 0 || cont_line);
3442         return (1);
3443 }
3444
3445 /*
3446  * Parse a description of a credential.
3447  */
3448 static void
3449 parsecred(char *namelist, struct expcred *cr)
3450 {
3451         char *name;
3452         int cnt;
3453         char *names;
3454         struct passwd *pw;
3455         struct group *gr;
3456
3457         /*
3458          * Set up the unprivileged user.
3459          */
3460         cr->cr_uid = UID_NOBODY;
3461         cr->cr_groups[0] = GID_NOGROUP;
3462         cr->cr_ngroups = 1;
3463         /*
3464          * Get the user's password table entry.
3465          */
3466         names = namelist;
3467         name = strsep_quote(&names, ":");
3468         /* Bug?  name could be NULL here */
3469         if (isdigit(*name) || *name == '-')
3470                 pw = getpwuid(atoi(name));
3471         else
3472                 pw = getpwnam(name);
3473         /*
3474          * Credentials specified as those of a user.
3475          */
3476         if (names == NULL) {
3477                 if (pw == NULL) {
3478                         syslog(LOG_ERR, "unknown user: %s", name);
3479                         return;
3480                 }
3481                 cr->cr_uid = pw->pw_uid;
3482                 cr->cr_ngroups = NGROUPS_MAX + 1;
3483                 if (getgrouplist(pw->pw_name, pw->pw_gid, cr->cr_groups,
3484                     &cr->cr_ngroups)) {
3485                         syslog(LOG_ERR, "too many groups");
3486                         cr->cr_ngroups = NGROUPS_MAX + 1;
3487                 }
3488
3489                 /*
3490                  * Compress out duplicate.
3491                  */
3492                 if (cr->cr_ngroups > 1 && cr->cr_groups[0] ==
3493                     cr->cr_groups[1]) {
3494                         for (cnt = 2; cnt < cr->cr_ngroups; cnt++)
3495                                 cr->cr_groups[cnt - 1] = cr->cr_groups[cnt];
3496                         cr->cr_ngroups--;
3497                 }
3498                 if (cr->cr_ngroups > NGROUPS_MAX)
3499                         cr->cr_ngroups = NGROUPS_MAX;
3500                 return;
3501         }
3502         /*
3503          * Explicit credential specified as a colon separated list:
3504          *      uid:gid:gid:...
3505          */
3506         if (pw != NULL)
3507                 cr->cr_uid = pw->pw_uid;
3508         else if (isdigit(*name) || *name == '-')
3509                 cr->cr_uid = atoi(name);
3510         else {
3511                 syslog(LOG_ERR, "unknown user: %s", name);
3512                 return;
3513         }
3514         cr->cr_ngroups = 0;
3515         while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS_MAX) {
3516                 name = strsep_quote(&names, ":");
3517                 if (isdigit(*name) || *name == '-') {
3518                         cr->cr_groups[cr->cr_ngroups++] = atoi(name);
3519                 } else {
3520                         if ((gr = getgrnam(name)) == NULL) {
3521                                 syslog(LOG_ERR, "unknown group: %s", name);
3522                                 continue;
3523                         }
3524                         cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
3525                 }
3526         }
3527         if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS_MAX)
3528                 syslog(LOG_ERR, "too many groups");
3529 }
3530
3531 #define STRSIZ  (MNTNAMLEN+MNTPATHLEN+50)
3532 /*
3533  * Routines that maintain the remote mounttab
3534  */
3535 static void
3536 get_mountlist(void)
3537 {
3538         struct mountlist *mlp;
3539         char *host, *dirp, *cp;
3540         char str[STRSIZ];
3541         FILE *mlfile;
3542
3543         if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3544                 if (errno == ENOENT)
3545                         return;
3546                 else {
3547                         syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3548                         return;
3549                 }
3550         }
3551         while (fgets(str, STRSIZ, mlfile) != NULL) {
3552                 cp = str;
3553                 host = strsep(&cp, " \t\n");
3554                 dirp = strsep(&cp, " \t\n");
3555                 if (host == NULL || dirp == NULL)
3556                         continue;
3557                 mlp = (struct mountlist *)malloc(sizeof (*mlp));
3558                 if (mlp == (struct mountlist *)NULL)
3559                         out_of_mem();
3560                 strncpy(mlp->ml_host, host, MNTNAMLEN);
3561                 mlp->ml_host[MNTNAMLEN] = '\0';
3562                 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3563                 mlp->ml_dirp[MNTPATHLEN] = '\0';
3564
3565                 SLIST_INSERT_HEAD(&mlhead, mlp, next);
3566         }
3567         fclose(mlfile);
3568 }
3569
3570 static void
3571 del_mlist(char *hostp, char *dirp)
3572 {
3573         struct mountlist *mlp, *mlp2;
3574         FILE *mlfile;
3575         int fnd = 0;
3576
3577         SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3578                 if (!strcmp(mlp->ml_host, hostp) &&
3579                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3580                         fnd = 1;
3581                         SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3582                         free((caddr_t)mlp);
3583                 }
3584         }
3585         if (fnd) {
3586                 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3587                         syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3588                         return;
3589                 }
3590                 SLIST_FOREACH(mlp, &mlhead, next) {
3591                         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3592                 }
3593                 fclose(mlfile);
3594         }
3595 }
3596
3597 static void
3598 add_mlist(char *hostp, char *dirp)
3599 {
3600         struct mountlist *mlp;
3601         FILE *mlfile;
3602
3603         SLIST_FOREACH(mlp, &mlhead, next) {
3604                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3605                         return;
3606         }
3607
3608         mlp = (struct mountlist *)malloc(sizeof (*mlp));
3609         if (mlp == (struct mountlist *)NULL)
3610                 out_of_mem();
3611         strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3612         mlp->ml_host[MNTNAMLEN] = '\0';
3613         strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3614         mlp->ml_dirp[MNTPATHLEN] = '\0';
3615         SLIST_INSERT_HEAD(&mlhead, mlp, next);
3616         if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3617                 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3618                 return;
3619         }
3620         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3621         fclose(mlfile);
3622 }
3623
3624 /*
3625  * Free up a group list.
3626  */
3627 static void
3628 free_grp(struct grouplist *grp)
3629 {
3630         if (grp->gr_type == GT_HOST) {
3631                 if (grp->gr_ptr.gt_addrinfo != NULL)
3632                         freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3633         } else if (grp->gr_type == GT_NET) {
3634                 if (grp->gr_ptr.gt_net.nt_name)
3635                         free(grp->gr_ptr.gt_net.nt_name);
3636         }
3637         free((caddr_t)grp);
3638 }
3639
3640 #ifdef DEBUG
3641 static void
3642 SYSLOG(int pri, const char *fmt, ...)
3643 {
3644         va_list ap;
3645
3646         va_start(ap, fmt);
3647         vfprintf(stderr, fmt, ap);
3648         va_end(ap);
3649 }
3650 #endif /* DEBUG */
3651
3652 /*
3653  * Check options for consistency.
3654  */
3655 static int
3656 check_options(struct dirlist *dp)
3657 {
3658
3659         if (v4root_phase == 0 && dp == NULL)
3660             return (1);
3661         if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3662             syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3663             return (1);
3664         }
3665         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3666                 syslog(LOG_ERR, "-mask requires -network");
3667                 return (1);
3668         }
3669         if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3670                 syslog(LOG_ERR, "-network requires mask specification");
3671                 return (1);
3672         }
3673         if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3674                 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3675                 return (1);
3676         }
3677         if (v4root_phase > 0 &&
3678             (opt_flags &
3679              ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3680             syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3681             return (1);
3682         }
3683         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3684             syslog(LOG_ERR, "-alldirs has multiple directories");
3685             return (1);
3686         }
3687         return (0);
3688 }
3689
3690 /*
3691  * Check an absolute directory path for any symbolic links. Return true
3692  */
3693 static int
3694 check_dirpath(char *dirp)
3695 {
3696         char *cp;
3697         int ret = 1;
3698         struct stat sb;
3699
3700         cp = dirp + 1;
3701         while (*cp && ret) {
3702                 if (*cp == '/') {
3703                         *cp = '\0';
3704                         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3705                                 ret = 0;
3706                         *cp = '/';
3707                 }
3708                 cp++;
3709         }
3710         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3711                 ret = 0;
3712         return (ret);
3713 }
3714
3715 /*
3716  * Make a netmask according to the specified prefix length. The ss_family
3717  * and other non-address fields must be initialised before calling this.
3718  */
3719 static int
3720 makemask(struct sockaddr_storage *ssp, int bitlen)
3721 {
3722         u_char *p;
3723         int bits, i, len;
3724
3725         if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3726                 return (-1);
3727         if (bitlen > len * CHAR_BIT)
3728                 return (-1);
3729
3730         for (i = 0; i < len; i++) {
3731                 bits = MIN(CHAR_BIT, bitlen);
3732                 *p++ = (u_char)~0 << (CHAR_BIT - bits);
3733                 bitlen -= bits;
3734         }
3735         return 0;
3736 }
3737
3738 /*
3739  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3740  * is acceptable (i.e. of the form 1...10....0).
3741  */
3742 static int
3743 checkmask(struct sockaddr *sa)
3744 {
3745         u_char *mask;
3746         int i, len;
3747
3748         if ((mask = sa_rawaddr(sa, &len)) == NULL)
3749                 return (-1);
3750
3751         for (i = 0; i < len; i++)
3752                 if (mask[i] != 0xff)
3753                         break;
3754         if (i < len) {
3755                 if (~mask[i] & (u_char)(~mask[i] + 1))
3756                         return (-1);
3757                 i++;
3758         }
3759         for (; i < len; i++)
3760                 if (mask[i] != 0)
3761                         return (-1);
3762         return (0);
3763 }
3764
3765 /*
3766  * Compare two sockaddrs according to a specified mask. Return zero if
3767  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3768  * If samask is NULL, perform a full comparison.
3769  */
3770 static int
3771 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3772 {
3773         unsigned char *p1, *p2, *mask;
3774         int len, i;
3775
3776         if (sa1->sa_family != sa2->sa_family ||
3777             (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3778             (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3779                 return (1);
3780
3781         switch (sa1->sa_family) {
3782         case AF_INET6:
3783                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3784                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3785                         return (1);
3786                 break;
3787         }
3788
3789         /* Simple binary comparison if no mask specified. */
3790         if (samask == NULL)
3791                 return (memcmp(p1, p2, len));
3792
3793         /* Set up the mask, and do a mask-based comparison. */
3794         if (sa1->sa_family != samask->sa_family ||
3795             (mask = sa_rawaddr(samask, NULL)) == NULL)
3796                 return (1);
3797
3798         for (i = 0; i < len; i++)
3799                 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3800                         return (1);
3801         return (0);
3802 }
3803
3804 /*
3805  * Return a pointer to the part of the sockaddr that contains the
3806  * raw address, and set *nbytes to its length in bytes. Returns
3807  * NULL if the address family is unknown.
3808  */
3809 static void *
3810 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3811         void *p;
3812         int len;
3813
3814         switch (sa->sa_family) {
3815         case AF_INET:
3816                 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3817                 p = &((struct sockaddr_in *)sa)->sin_addr;
3818                 break;
3819         case AF_INET6:
3820                 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3821                 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3822                 break;
3823         default:
3824                 p = NULL;
3825                 len = 0;
3826         }
3827
3828         if (nbytes != NULL)
3829                 *nbytes = len;
3830         return (p);
3831 }
3832
3833 static void
3834 huphandler(int sig __unused)
3835 {
3836
3837         got_sighup = 1;
3838 }
3839
3840 static void
3841 terminate(int sig __unused)
3842 {
3843         pidfile_remove(pfh);
3844         rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3845         rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3846         exit (0);
3847 }
3848
3849 static void
3850 cp_cred(struct expcred *outcr, struct expcred *incr)
3851 {
3852
3853         outcr->cr_uid = incr->cr_uid;
3854         outcr->cr_ngroups = incr->cr_ngroups;
3855         memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups *
3856             sizeof(gid_t));
3857 }