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