]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mountd/mountd.c
Add UPDATING entries and bump version.
[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 error, i, nfs_maxvers, num;
1875         int iovlen;
1876         struct nfsex_args eargs;
1877         FILE *debug_file;
1878         size_t nfs_maxvers_size;
1879
1880         if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) {
1881                 fclose(debug_file);
1882                 logdebug = 1;
1883         } else
1884                 logdebug = 0;
1885         LOGDEBUG("passno=%d", passno);
1886         v4root_dirpath[0] = '\0';
1887         free_v4rootexp();
1888         if (passno == 1) {
1889                 /*
1890                  * Save the current lists as old ones, so that the new lists
1891                  * can be compared with the old ones in the 2nd pass.
1892                  */
1893                 for (i = 0; i < exphashsize; i++) {
1894                         SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]);
1895                         SLIST_INIT(&exphead[i]);
1896                 }
1897
1898                 /* Note that the public fh has not yet been set. */
1899                 has_set_publicfh = 0;
1900
1901                 /* Read the export file(s) and process them */
1902                 read_exportfile(passno);
1903         } else {
1904                 /*
1905                  * Just make the old lists empty.
1906                  * exphashsize == 0 for the first call, before oldexphead
1907                  * has been initialized-->loop won't be executed.
1908                  */
1909                 for (i = 0; i < exphashsize; i++)
1910                         SLIST_INIT(&oldexphead[i]);
1911         }
1912
1913         bzero(&export, sizeof(export));
1914         export.ex_flags = MNT_DELEXPORT;
1915         iov = NULL;
1916         iovlen = 0;
1917         bzero(errmsg, sizeof(errmsg));
1918
1919         if (suspend_nfsd != 0)
1920                 (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1921         /*
1922          * Delete the old V4 root dir.
1923          */
1924         bzero(&eargs, sizeof (eargs));
1925         eargs.export.ex_flags = MNT_DELEXPORT;
1926         if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1927             errno != ENOENT)
1928                 syslog(LOG_ERR, "Can't delete exports for V4:");
1929
1930         build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1931         build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1932         build_iovec(&iov, &iovlen, "from", NULL, 0);
1933         build_iovec(&iov, &iovlen, "update", NULL, 0);
1934         build_iovec(&iov, &iovlen, "export", &export,
1935             sizeof(export));
1936         build_iovec(&iov, &iovlen, "errmsg", errmsg,
1937             sizeof(errmsg));
1938
1939         /*
1940          * For passno == 1, compare the old and new lists updating the kernel
1941          * exports for any cases that have changed.
1942          * This call is doing the second pass through the lists.
1943          * If it fails, fall back on the bulk reload.
1944          */
1945         if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) ==
1946             0) {
1947                 LOGDEBUG("compareok");
1948                 /* Free up the old lists. */
1949                 free_exports(oldexphead);
1950         } else {
1951                 LOGDEBUG("doing passno=0");
1952                 /*
1953                  * Clear flag that notes if a public fh has been exported.
1954                  * It is set by do_mount() if MNT_EXPUBLIC is set for the entry.
1955                  */
1956                 has_publicfh = 0;
1957
1958                 /* exphead == NULL if not yet allocated (first call). */
1959                 if (exphead != NULL) {
1960                         /*
1961                          * First, get rid of the old lists.
1962                          */
1963                         free_exports(exphead);
1964                         free_exports(oldexphead);
1965                 }
1966
1967                 /*
1968                  * And delete exports that are in the kernel for all local
1969                  * filesystems.
1970                  * XXX: Should know how to handle all local exportable
1971                  * filesystems.
1972                  */
1973                 num = getmntinfo(&mntbufp, MNT_NOWAIT);
1974
1975                 /* Allocate hash tables, for first call. */
1976                 if (exphead == NULL) {
1977                         /* Target an average linked list length of 10. */
1978                         exphashsize = num / 10;
1979                         if (exphashsize < 1)
1980                                 exphashsize = 1;
1981                         else if (exphashsize > 100000)
1982                                 exphashsize = 100000;
1983                         exphead = malloc(exphashsize * sizeof(*exphead));
1984                         oldexphead = malloc(exphashsize * sizeof(*oldexphead));
1985                         if (exphead == NULL || oldexphead == NULL)
1986                                 errx(1, "Can't malloc hash tables");
1987
1988                         for (i = 0; i < exphashsize; i++) {
1989                                 SLIST_INIT(&exphead[i]);
1990                                 SLIST_INIT(&oldexphead[i]);
1991                         }
1992                 }
1993         
1994                 for (i = 0; i < num; i++)
1995                         delete_export(iov, iovlen, &mntbufp[i], errmsg);
1996
1997
1998                 /* Read the export file(s) and process them */
1999                 read_exportfile(0);
2000         }
2001
2002         if (strlen(v4root_dirpath) == 0) {
2003                 /* Check to see if a V4: line is needed. */
2004                 nfs_maxvers_size = sizeof(nfs_maxvers);
2005                 error = sysctlbyname("vfs.nfsd.server_max_nfsvers",
2006                     &nfs_maxvers, &nfs_maxvers_size, NULL, 0);
2007                 if (error != 0 || nfs_maxvers < NFS_VER2 || nfs_maxvers >
2008                     NFS_VER4) {
2009                         syslog(LOG_ERR, "sysctlbyname(vfs.nfsd."
2010                             "server_max_nfsvers) failed, defaulting to NFSv3");
2011                         nfs_maxvers = NFS_VER3;
2012                 }
2013                 if (nfs_maxvers == NFS_VER4)
2014                         syslog(LOG_ERR, "NFSv4 requires at least one V4: line");
2015         }
2016
2017         if (iov != NULL) {
2018                 /* Free strings allocated by strdup() in getmntopts.c */
2019                 free(iov[0].iov_base); /* fstype */
2020                 free(iov[2].iov_base); /* fspath */
2021                 free(iov[4].iov_base); /* from */
2022                 free(iov[6].iov_base); /* update */
2023                 free(iov[8].iov_base); /* export */
2024                 free(iov[10].iov_base); /* errmsg */
2025
2026                 /* free iov, allocated by realloc() */
2027                 free(iov);
2028                 iovlen = 0;
2029         }
2030
2031         /*
2032          * If there was no public fh, clear any previous one set.
2033          */
2034         if (has_publicfh == 0) {
2035                 LOGDEBUG("clear public fh");
2036                 (void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
2037         }
2038
2039         /* Resume the nfsd. If they weren't suspended, this is harmless. */
2040         (void)nfssvc(NFSSVC_RESUMENFSD, NULL);
2041         LOGDEBUG("eo get_exportlist");
2042 }
2043
2044 /*
2045  * Insert an export entry in the appropriate list.
2046  */
2047 static void
2048 insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
2049 {
2050         uint32_t i;
2051
2052         i = EXPHASH(&ep->ex_fs);
2053         LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i);
2054         SLIST_INSERT_HEAD(&exhp[i], ep, entries);
2055 }
2056
2057 /*
2058  * Free up the exports lists passed in as arguments.
2059  */
2060 static void
2061 free_exports(struct exportlisthead *exhp)
2062 {
2063         struct exportlist *ep, *ep2;
2064         int i;
2065
2066         for (i = 0; i < exphashsize; i++) {
2067                 SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
2068                         SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
2069                         free_exp(ep);
2070                 }
2071                 SLIST_INIT(&exhp[i]);
2072         }
2073 }
2074
2075 /*
2076  * Read the exports file(s) and call get_exportlist_one() for each line.
2077  */
2078 static void
2079 read_exportfile(int passno)
2080 {
2081         int done, i;
2082
2083         /*
2084          * Read in the exports file and build the list, calling
2085          * nmount() as we go along to push the export rules into the kernel.
2086          */
2087         done = 0;
2088         for (i = 0; exnames[i] != NULL; i++) {
2089                 if (debug)
2090                         warnx("reading exports from %s", exnames[i]);
2091                 if ((exp_file = fopen(exnames[i], "r")) == NULL) {
2092                         syslog(LOG_WARNING, "can't open %s", exnames[i]);
2093                         continue;
2094                 }
2095                 get_exportlist_one(passno);
2096                 fclose(exp_file);
2097                 done++;
2098         }
2099         if (done == 0) {
2100                 syslog(LOG_ERR, "can't open any exports file");
2101                 exit(2);
2102         }
2103 }
2104
2105 /*
2106  * Compare the export lists against the old ones and do nmount() operations
2107  * for any cases that have changed.  This avoids doing nmount() for entries
2108  * that have not changed.
2109  * Return 0 upon success, 1 otherwise.
2110  */
2111 static int
2112 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
2113 {
2114         struct exportlist *ep, *oep;
2115         struct grouplist *grp;
2116         struct statfs fs, ofs;
2117         int i, ret;
2118
2119         /*
2120          * Loop through the current list and look for an entry in the old
2121          * list.
2122          * If found, check to see if it the same.
2123          *        If it is not the same, delete and re-export.
2124          *        Then mark it done on the old list.
2125          * else (not found)
2126          *        export it.
2127          * Any entries left in the old list after processing must have their
2128          * exports deleted.
2129          */
2130         for (i = 0; i < exphashsize; i++)
2131                 SLIST_FOREACH(ep, &exphead[i], entries) {
2132                         LOGDEBUG("foreach ep=%s", ep->ex_fsdir);
2133                         oep = ex_search(&ep->ex_fs, oldexphead);
2134                         if (oep != NULL) {
2135                                 /*
2136                                  * Check the mount paths are the same.
2137                                  * If not, return 1 so that the reload of the
2138                                  * exports will be done in bulk, the
2139                                  * passno == 0 way.
2140                                  */
2141                                 LOGDEBUG("found old exp");
2142                                 if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2143                                         return (1);
2144                                 LOGDEBUG("same fsdir");
2145                                 /*
2146                                  * Test to see if the entry is the same.
2147                                  * If not the same delete exports and
2148                                  * re-export.
2149                                  */
2150                                 if (compare_export(ep, oep) != 0) {
2151                                         /*
2152                                          * Clear has_publicfh if if was set
2153                                          * in the old exports, but only if it
2154                                          * has not been set during processing of
2155                                          * the exports for this pass, as
2156                                          * indicated by has_set_publicfh.
2157                                          */
2158                                         if (has_set_publicfh == 0 &&
2159                                             (oep->ex_flag & EX_PUBLICFH) != 0)
2160                                                 has_publicfh = 0;
2161
2162                                         /* Delete and re-export. */
2163                                         if (statfs(ep->ex_fsdir, &fs) < 0)
2164                                                 return (1);
2165                                         delete_export(iov, iovlen, &fs, errmsg);
2166                                         ret = do_export_mount(ep, &fs);
2167                                         if (ret != 0)
2168                                                 return (ret);
2169                                 }
2170                                 oep->ex_flag |= EX_DONE;
2171                                 LOGDEBUG("exdone");
2172                         } else {
2173                                 LOGDEBUG("not found so export");
2174                                 /* Not found, so do export. */
2175                                 if (statfs(ep->ex_fsdir, &fs) < 0)
2176                                         return (1);
2177                                 ret = do_export_mount(ep, &fs);
2178                                 if (ret != 0)
2179                                         return (ret);
2180                         }
2181                 }
2182
2183         /* Delete exports not done. */
2184         for (i = 0; i < exphashsize; i++)
2185                 SLIST_FOREACH(oep, &oldexphead[i], entries) {
2186                         if ((oep->ex_flag & EX_DONE) == 0) {
2187                                 LOGDEBUG("not done delete=%s", oep->ex_fsdir);
2188                                 if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
2189                                     fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) {
2190                                         LOGDEBUG("do delete");
2191                                         /*
2192                                          * Clear has_publicfh if if was set
2193                                          * in the old exports, but only if it
2194                                          * has not been set during processing of
2195                                          * the exports for this pass, as
2196                                          * indicated by has_set_publicfh.
2197                                          */
2198                                         if (has_set_publicfh == 0 &&
2199                                             (oep->ex_flag & EX_PUBLICFH) != 0)
2200                                                 has_publicfh = 0;
2201
2202                                         delete_export(iov, iovlen, &ofs,
2203                                             errmsg);
2204                                 }
2205                         }
2206                 }
2207
2208         /* Do the V4 root exports, as required. */
2209         grp = NULL;
2210         if (v4root_ep != NULL)
2211                 grp = v4root_ep->ex_grphead;
2212         v4root_phase = 2;
2213         while (v4root_ep != NULL && grp != NULL) {
2214                 LOGDEBUG("v4root expath=%s", v4root_dirpath);
2215                 ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
2216                     v4root_dirpath, strlen(v4root_dirpath), &fs,
2217                     grp->gr_numsecflavors, grp->gr_secflavors);
2218                 if (ret != 0) {
2219                         v4root_phase = 0;
2220                         return (ret);
2221                 }
2222                 grp = grp->gr_next;
2223         }
2224         v4root_phase = 0;
2225         free_v4rootexp();
2226         return (0);
2227 }
2228
2229 /*
2230  * Compare old and current exportlist entries for the fsid and return 0
2231  * if they are the same, 1 otherwise.
2232  */
2233 static int
2234 compare_export(struct exportlist *ep, struct exportlist *oep)
2235 {
2236         struct grouplist *grp, *ogrp;
2237
2238         if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2239                 return (1);
2240         if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET))
2241                 return (1);
2242         if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
2243             (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
2244                 return (1);
2245         if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) !=
2246             (oep->ex_defdir->dp_flag & DP_DEFSET))
2247                 return (1);
2248         if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors !=
2249             oep->ex_defnumsecflavors || ep->ex_defexflags !=
2250             oep->ex_defexflags || compare_cred(&ep->ex_defanon,
2251             &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors,
2252             oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0))
2253                 return (1);
2254
2255         /* Now, check all the groups. */
2256         for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2257                 ogrp->gr_flag = 0;
2258         for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
2259                 for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
2260                     ogrp->gr_next)
2261                         if ((ogrp->gr_flag & GR_FND) == 0 &&
2262                             grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
2263                             grp->gr_exflags == ogrp->gr_exflags &&
2264                             compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
2265                             compare_secflavor(grp->gr_secflavors,
2266                             ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
2267                                 break;
2268                 if (ogrp != NULL)
2269                         ogrp->gr_flag |= GR_FND;
2270                 else
2271                         return (1);
2272         }
2273         for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2274                 if ((ogrp->gr_flag & GR_FND) == 0)
2275                         return (1);
2276         return (0);
2277 }
2278
2279 /*
2280  * This algorithm compares two arrays of "n" items. It returns 0 if they are
2281  * the "same" and 1 otherwise.  Although suboptimal, it is always safe to
2282  * return 1, which makes compare_nmount_export() reload the exports entry.
2283  * "same" refers to having the same set of values in the two arrays.
2284  * The arrays are in no particular order and duplicates (multiple entries
2285  * in an array with the same value) is allowed.
2286  * The algorithm is inefficient, but the common case of indentical arrays is
2287  * handled first and "n" is normally fairly small.
2288  * Since the two functions need the same algorithm but for arrays of
2289  * different types (gid_t vs int), this is done as a macro.
2290  */
2291 #define COMPARE_ARRAYS(a1, a2, n)                                       \
2292         do {                                                            \
2293                 int fnd, fndarray[(n)], i, j;                           \
2294                 /* Handle common case of identical arrays. */           \
2295                 for (i = 0; i < (n); i++)                               \
2296                         if ((a1)[i] != (a2)[i])                         \
2297                                 break;                                  \
2298                 if (i == (n))                                           \
2299                         return (0);                                     \
2300                 for (i = 0; i < (n); i++)                               \
2301                         fndarray[i] = 0;                                \
2302                 for (i = 0; i < (n); i++) {                             \
2303                         fnd = 0;                                        \
2304                         for (j = 0; j < (n); j++) {                     \
2305                                 if ((a1)[i] == (a2)[j]) {               \
2306                                         fndarray[j] = 1;                \
2307                                         fnd = 1;                        \
2308                                 }                                       \
2309                         }                                               \
2310                         if (fnd == 0)                                   \
2311                                 return (1);                             \
2312                 }                                                       \
2313                 for (i = 0; i < (n); i++)                               \
2314                         if (fndarray[i] == 0)                           \
2315                                 return (1);                             \
2316                 return (0);                                             \
2317         } while (0)
2318
2319 /*
2320  * Compare to struct xucred's.  Return 0 if the same and 1 otherwise.
2321  */
2322 static int
2323 compare_cred(struct xucred *cr0, struct xucred *cr1)
2324 {
2325
2326         if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
2327                 return (1);
2328
2329         COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups);
2330 }
2331
2332 /*
2333  * Compare two lists of security flavors.  Return 0 if the same and 1 otherwise.
2334  */
2335 static int
2336 compare_secflavor(int *sec1, int *sec2, int nsec)
2337 {
2338
2339         COMPARE_ARRAYS(sec1, sec2, nsec);
2340 }
2341
2342 /*
2343  * Delete an exports entry.
2344  */
2345 static void
2346 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
2347 {
2348         struct xvfsconf vfc;
2349
2350         if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
2351                 syslog(LOG_ERR, "getvfsbyname() failed for %s",
2352                     fsp->f_fstypename);
2353                 return;
2354         }
2355         
2356         /*
2357          * We do not need to delete "export" flag from
2358          * filesystems that do not have it set.
2359          */
2360         if (!(fsp->f_flags & MNT_EXPORTED))
2361                 return;
2362         /*
2363          * Do not delete export for network filesystem by
2364          * passing "export" arg to nmount().
2365          * It only makes sense to do this for local filesystems.
2366          */
2367         if (vfc.vfc_flags & VFCF_NETWORK)
2368                 return;
2369         
2370         iov[1].iov_base = fsp->f_fstypename;
2371         iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
2372         iov[3].iov_base = fsp->f_mntonname;
2373         iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
2374         iov[5].iov_base = fsp->f_mntfromname;
2375         iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
2376         errmsg[0] = '\0';
2377         
2378         /*
2379          * EXDEV is returned when path exists but is not a
2380          * mount point.  May happens if raced with unmount.
2381          */
2382         if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
2383             errno != ENOTSUP && errno != EXDEV) {
2384                 syslog(LOG_ERR,
2385                     "can't delete exports for %s: %m %s",
2386                     fsp->f_mntonname, errmsg);
2387         }
2388 }
2389
2390 /*
2391  * Allocate an export list element
2392  */
2393 static struct exportlist *
2394 get_exp(void)
2395 {
2396         struct exportlist *ep;
2397
2398         ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
2399         if (ep == (struct exportlist *)NULL)
2400                 out_of_mem();
2401         return (ep);
2402 }
2403
2404 /*
2405  * Allocate a group list element
2406  */
2407 static struct grouplist *
2408 get_grp(void)
2409 {
2410         struct grouplist *gp;
2411
2412         gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
2413         if (gp == (struct grouplist *)NULL)
2414                 out_of_mem();
2415         return (gp);
2416 }
2417
2418 /*
2419  * Clean up upon an error in get_exportlist().
2420  */
2421 static void
2422 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
2423 {
2424         struct grouplist *tgrp;
2425
2426         if (!(opt_flags & OP_QUIET)) {
2427                 if (reason != NULL)
2428                         syslog(LOG_ERR, "bad exports list line '%s': %s", line,
2429                             reason);
2430                 else
2431                         syslog(LOG_ERR, "bad exports list line '%s'", line);
2432         }
2433         if (ep && (ep->ex_flag & EX_LINKED) == 0)
2434                 free_exp(ep);
2435         while (grp) {
2436                 tgrp = grp;
2437                 grp = grp->gr_next;
2438                 free_grp(tgrp);
2439         }
2440 }
2441
2442 /*
2443  * Search the export list for a matching fs.
2444  */
2445 static struct exportlist *
2446 ex_search(fsid_t *fsid, struct exportlisthead *exhp)
2447 {
2448         struct exportlist *ep;
2449         uint32_t i;
2450
2451         i = EXPHASH(fsid);
2452         SLIST_FOREACH(ep, &exhp[i], entries) {
2453                 if (fsidcmp(&ep->ex_fs, fsid) == 0)
2454                         return (ep);
2455         }
2456
2457         return (ep);
2458 }
2459
2460 /*
2461  * Add a directory path to the list.
2462  */
2463 static char *
2464 add_expdir(struct dirlist **dpp, char *cp, int len)
2465 {
2466         struct dirlist *dp;
2467
2468         dp = malloc(sizeof (struct dirlist));
2469         if (dp == (struct dirlist *)NULL)
2470                 out_of_mem();
2471         dp->dp_left = *dpp;
2472         dp->dp_right = (struct dirlist *)NULL;
2473         dp->dp_flag = 0;
2474         dp->dp_hosts = (struct hostlist *)NULL;
2475         dp->dp_dirp = strndup(cp, len);
2476         if (dp->dp_dirp == NULL)
2477                 out_of_mem();
2478         *dpp = dp;
2479         return (dp->dp_dirp);
2480 }
2481
2482 /*
2483  * Hang the dir list element off the dirpath binary tree as required
2484  * and update the entry for host.
2485  */
2486 static void
2487 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2488         int flags, struct xucred *anoncrp, int exflags)
2489 {
2490         struct hostlist *hp;
2491         struct dirlist *dp2;
2492
2493         if (flags & OP_ALLDIRS) {
2494                 if (ep->ex_defdir)
2495                         free((caddr_t)dp);
2496                 else
2497                         ep->ex_defdir = dp;
2498                 if (grp == (struct grouplist *)NULL) {
2499                         ep->ex_flag |= EX_DEFSET;
2500                         ep->ex_defdir->dp_flag |= DP_DEFSET;
2501                         /* Save the default security flavors list. */
2502                         ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2503                         if (ep->ex_numsecflavors > 0)
2504                                 memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2505                                     sizeof(ep->ex_secflavors));
2506                         ep->ex_defanon = *anoncrp;
2507                         ep->ex_defexflags = exflags;
2508                 } else while (grp) {
2509                         hp = get_ht();
2510                         hp->ht_grp = grp;
2511                         hp->ht_next = ep->ex_defdir->dp_hosts;
2512                         ep->ex_defdir->dp_hosts = hp;
2513                         /* Save the security flavors list for this host set. */
2514                         grp->gr_numsecflavors = ep->ex_numsecflavors;
2515                         if (ep->ex_numsecflavors > 0)
2516                                 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2517                                     sizeof(ep->ex_secflavors));
2518                         grp = grp->gr_next;
2519                 }
2520         } else {
2521
2522                 /*
2523                  * Loop through the directories adding them to the tree.
2524                  */
2525                 while (dp) {
2526                         dp2 = dp->dp_left;
2527                         add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp,
2528                             exflags);
2529                         dp = dp2;
2530                 }
2531         }
2532 }
2533
2534 /*
2535  * Traverse the binary tree either updating a node that is already there
2536  * for the new directory or adding the new node.
2537  */
2538 static void
2539 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2540         int flags, struct exportlist *ep, struct xucred *anoncrp, int exflags)
2541 {
2542         struct dirlist *dp;
2543         struct hostlist *hp;
2544         int cmp;
2545
2546         dp = *dpp;
2547         if (dp) {
2548                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2549                 if (cmp > 0) {
2550                         add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp,
2551                             exflags);
2552                         return;
2553                 } else if (cmp < 0) {
2554                         add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp,
2555                             exflags);
2556                         return;
2557                 } else
2558                         free((caddr_t)newdp);
2559         } else {
2560                 dp = newdp;
2561                 dp->dp_left = (struct dirlist *)NULL;
2562                 *dpp = dp;
2563         }
2564         if (grp) {
2565
2566                 /*
2567                  * Hang all of the host(s) off of the directory point.
2568                  */
2569                 do {
2570                         hp = get_ht();
2571                         hp->ht_grp = grp;
2572                         hp->ht_next = dp->dp_hosts;
2573                         dp->dp_hosts = hp;
2574                         /* Save the security flavors list for this host set. */
2575                         grp->gr_numsecflavors = ep->ex_numsecflavors;
2576                         if (ep->ex_numsecflavors > 0)
2577                                 memcpy(grp->gr_secflavors, ep->ex_secflavors,
2578                                     sizeof(ep->ex_secflavors));
2579                         grp = grp->gr_next;
2580                 } while (grp);
2581         } else {
2582                 ep->ex_flag |= EX_DEFSET;
2583                 dp->dp_flag |= DP_DEFSET;
2584                 /* Save the default security flavors list. */
2585                 ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2586                 if (ep->ex_numsecflavors > 0)
2587                         memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2588                             sizeof(ep->ex_secflavors));
2589                 ep->ex_defanon = *anoncrp;
2590                 ep->ex_defexflags = exflags;
2591         }
2592 }
2593
2594 /*
2595  * Search for a dirpath on the export point.
2596  */
2597 static struct dirlist *
2598 dirp_search(struct dirlist *dp, char *dirp)
2599 {
2600         int cmp;
2601
2602         if (dp) {
2603                 cmp = strcmp(dp->dp_dirp, dirp);
2604                 if (cmp > 0)
2605                         return (dirp_search(dp->dp_left, dirp));
2606                 else if (cmp < 0)
2607                         return (dirp_search(dp->dp_right, dirp));
2608                 else
2609                         return (dp);
2610         }
2611         return (dp);
2612 }
2613
2614 /*
2615  * Scan for a host match in a directory tree.
2616  */
2617 static int
2618 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2619         int *hostsetp, int *numsecflavors, int **secflavorsp)
2620 {
2621         struct hostlist *hp;
2622         struct grouplist *grp;
2623         struct addrinfo *ai;
2624
2625         if (dp) {
2626                 if (dp->dp_flag & DP_DEFSET)
2627                         *defsetp = dp->dp_flag;
2628                 hp = dp->dp_hosts;
2629                 while (hp) {
2630                         grp = hp->ht_grp;
2631                         switch (grp->gr_type) {
2632                         case GT_HOST:
2633                                 ai = grp->gr_ptr.gt_addrinfo;
2634                                 for (; ai; ai = ai->ai_next) {
2635                                         if (!sacmp(ai->ai_addr, saddr, NULL)) {
2636                                                 *hostsetp =
2637                                                     (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                                 }
2647                                 break;
2648                         case GT_NET:
2649                                 if (!sacmp(saddr, (struct sockaddr *)
2650                                     &grp->gr_ptr.gt_net.nt_net,
2651                                     (struct sockaddr *)
2652                                     &grp->gr_ptr.gt_net.nt_mask)) {
2653                                         *hostsetp = (hp->ht_flag | DP_HOSTSET);
2654                                         if (numsecflavors != NULL) {
2655                                                 *numsecflavors =
2656                                                     grp->gr_numsecflavors;
2657                                                 *secflavorsp =
2658                                                     grp->gr_secflavors;
2659                                         }
2660                                         return (1);
2661                                 }
2662                                 break;
2663                         }
2664                         hp = hp->ht_next;
2665                 }
2666         }
2667         return (0);
2668 }
2669
2670 /*
2671  * Scan tree for a host that matches the address.
2672  */
2673 static int
2674 scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2675 {
2676         int defset, hostset;
2677
2678         if (dp) {
2679                 if (scan_tree(dp->dp_left, saddr))
2680                         return (1);
2681                 if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2682                         return (1);
2683                 if (scan_tree(dp->dp_right, saddr))
2684                         return (1);
2685         }
2686         return (0);
2687 }
2688
2689 /*
2690  * Traverse the dirlist tree and free it up.
2691  */
2692 static void
2693 free_dir(struct dirlist *dp)
2694 {
2695
2696         if (dp) {
2697                 free_dir(dp->dp_left);
2698                 free_dir(dp->dp_right);
2699                 free_host(dp->dp_hosts);
2700                 free(dp->dp_dirp);
2701                 free(dp);
2702         }
2703 }
2704
2705 /*
2706  * Parse a colon separated list of security flavors
2707  */
2708 static int
2709 parsesec(char *seclist, struct exportlist *ep)
2710 {
2711         char *cp, savedc;
2712         int flavor;
2713
2714         ep->ex_numsecflavors = 0;
2715         for (;;) {
2716                 cp = strchr(seclist, ':');
2717                 if (cp) {
2718                         savedc = *cp;
2719                         *cp = '\0';
2720                 }
2721
2722                 if (!strcmp(seclist, "sys"))
2723                         flavor = AUTH_SYS;
2724                 else if (!strcmp(seclist, "krb5"))
2725                         flavor = RPCSEC_GSS_KRB5;
2726                 else if (!strcmp(seclist, "krb5i"))
2727                         flavor = RPCSEC_GSS_KRB5I;
2728                 else if (!strcmp(seclist, "krb5p"))
2729                         flavor = RPCSEC_GSS_KRB5P;
2730                 else {
2731                         if (cp)
2732                                 *cp = savedc;
2733                         syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2734                         return (1);
2735                 }
2736                 if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2737                         if (cp)
2738                                 *cp = savedc;
2739                         syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2740                         return (1);
2741                 }
2742                 ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2743                 ep->ex_numsecflavors++;
2744                 if (cp) {
2745                         *cp = savedc;
2746                         seclist = cp + 1;
2747                 } else {
2748                         break;
2749                 }
2750         }
2751         return (0);
2752 }
2753
2754 /*
2755  * Parse the option string and update fields.
2756  * Option arguments may either be -<option>=<value> or
2757  * -<option> <value>
2758  */
2759 static int
2760 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2761         int *has_hostp, int *exflagsp, struct xucred *cr)
2762 {
2763         char *cpoptarg, *cpoptend;
2764         char *cp, *endcp, *cpopt, savedc, savedc2;
2765         int allflag, usedarg;
2766
2767         savedc2 = '\0';
2768         cpopt = *cpp;
2769         cpopt++;
2770         cp = *endcpp;
2771         savedc = *cp;
2772         *cp = '\0';
2773         while (cpopt && *cpopt) {
2774                 allflag = 1;
2775                 usedarg = -2;
2776                 if ((cpoptend = strchr(cpopt, ','))) {
2777                         *cpoptend++ = '\0';
2778                         if ((cpoptarg = strchr(cpopt, '=')))
2779                                 *cpoptarg++ = '\0';
2780                 } else {
2781                         if ((cpoptarg = strchr(cpopt, '=')))
2782                                 *cpoptarg++ = '\0';
2783                         else {
2784                                 *cp = savedc;
2785                                 nextfield(&cp, &endcp);
2786                                 **endcpp = '\0';
2787                                 if (endcp > cp && *cp != '-') {
2788                                         cpoptarg = cp;
2789                                         savedc2 = *endcp;
2790                                         *endcp = '\0';
2791                                         usedarg = 0;
2792                                 }
2793                         }
2794                 }
2795                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2796                         *exflagsp |= MNT_EXRDONLY;
2797                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2798                     !(allflag = strcmp(cpopt, "mapall")) ||
2799                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2800                         usedarg++;
2801                         parsecred(cpoptarg, cr);
2802                         if (allflag == 0) {
2803                                 *exflagsp |= MNT_EXPORTANON;
2804                                 opt_flags |= OP_MAPALL;
2805                         } else
2806                                 opt_flags |= OP_MAPROOT;
2807                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2808                     !strcmp(cpopt, "m"))) {
2809                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2810                                 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2811                                 return (1);
2812                         }
2813                         usedarg++;
2814                         opt_flags |= OP_MASK;
2815                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
2816                         !strcmp(cpopt, "n"))) {
2817                         if (strchr(cpoptarg, '/') != NULL) {
2818                                 if (debug)
2819                                         fprintf(stderr, "setting OP_MASKLEN\n");
2820                                 opt_flags |= OP_MASKLEN;
2821                         }
2822                         if (grp->gr_type != GT_NULL) {
2823                                 syslog(LOG_ERR, "network/host conflict");
2824                                 return (1);
2825                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2826                                 syslog(LOG_ERR, "bad net: %s", cpoptarg);
2827                                 return (1);
2828                         }
2829                         grp->gr_type = GT_NET;
2830                         *has_hostp = 1;
2831                         usedarg++;
2832                         opt_flags |= OP_NET;
2833                 } else if (!strcmp(cpopt, "alldirs")) {
2834                         opt_flags |= OP_ALLDIRS;
2835                 } else if (!strcmp(cpopt, "public")) {
2836                         *exflagsp |= MNT_EXPUBLIC;
2837                 } else if (!strcmp(cpopt, "webnfs")) {
2838                         *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2839                         opt_flags |= OP_MAPALL;
2840                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
2841                         ep->ex_indexfile = strdup(cpoptarg);
2842                 } else if (!strcmp(cpopt, "quiet")) {
2843                         opt_flags |= OP_QUIET;
2844                 } else if (cpoptarg && !strcmp(cpopt, "sec")) {
2845                         if (parsesec(cpoptarg, ep))
2846                                 return (1);
2847                         opt_flags |= OP_SEC;
2848                         usedarg++;
2849                 } else {
2850                         syslog(LOG_ERR, "bad opt %s", cpopt);
2851                         return (1);
2852                 }
2853                 if (usedarg >= 0) {
2854                         *endcp = savedc2;
2855                         **endcpp = savedc;
2856                         if (usedarg > 0) {
2857                                 *cpp = cp;
2858                                 *endcpp = endcp;
2859                         }
2860                         return (0);
2861                 }
2862                 cpopt = cpoptend;
2863         }
2864         **endcpp = savedc;
2865         return (0);
2866 }
2867
2868 /*
2869  * Translate a character string to the corresponding list of network
2870  * addresses for a hostname.
2871  */
2872 static int
2873 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2874 {
2875         struct grouplist *checkgrp;
2876         struct addrinfo *ai, *tai, hints;
2877         int ecode;
2878         char host[NI_MAXHOST];
2879
2880         if (grp->gr_type != GT_NULL) {
2881                 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2882                 return (1);
2883         }
2884         memset(&hints, 0, sizeof hints);
2885         hints.ai_flags = AI_CANONNAME;
2886         hints.ai_protocol = IPPROTO_UDP;
2887         ecode = getaddrinfo(cp, NULL, &hints, &ai);
2888         if (ecode != 0) {
2889                 syslog(LOG_ERR,"can't get address info for host %s", cp);
2890                 return 1;
2891         }
2892         grp->gr_ptr.gt_addrinfo = ai;
2893         while (ai != NULL) {
2894                 if (ai->ai_canonname == NULL) {
2895                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2896                             sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2897                                 strlcpy(host, "?", sizeof(host));
2898                         ai->ai_canonname = strdup(host);
2899                         ai->ai_flags |= AI_CANONNAME;
2900                 }
2901                 if (debug)
2902                         fprintf(stderr, "got host %s\n", ai->ai_canonname);
2903                 /*
2904                  * Sanity check: make sure we don't already have an entry
2905                  * for this host in the grouplist.
2906                  */
2907                 for (checkgrp = tgrp; checkgrp != NULL;
2908                     checkgrp = checkgrp->gr_next) {
2909                         if (checkgrp->gr_type != GT_HOST)
2910                                 continue;
2911                         for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2912                             tai = tai->ai_next) {
2913                                 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2914                                         continue;
2915                                 if (debug)
2916                                         fprintf(stderr,
2917                                             "ignoring duplicate host %s\n",
2918                                             ai->ai_canonname);
2919                                 grp->gr_type = GT_IGNORE;
2920                                 return (0);
2921                         }
2922                 }
2923                 ai = ai->ai_next;
2924         }
2925         grp->gr_type = GT_HOST;
2926         return (0);
2927 }
2928
2929 /*
2930  * Free up an exports list component
2931  */
2932 static void
2933 free_exp(struct exportlist *ep)
2934 {
2935         struct grouplist *grp, *tgrp;
2936
2937         if (ep->ex_defdir) {
2938                 free_host(ep->ex_defdir->dp_hosts);
2939                 free((caddr_t)ep->ex_defdir);
2940         }
2941         if (ep->ex_fsdir)
2942                 free(ep->ex_fsdir);
2943         if (ep->ex_indexfile)
2944                 free(ep->ex_indexfile);
2945         free_dir(ep->ex_dirl);
2946         grp = ep->ex_grphead;
2947         while (grp) {
2948                 tgrp = grp;
2949                 grp = grp->gr_next;
2950                 free_grp(tgrp);
2951         }
2952         free((caddr_t)ep);
2953 }
2954
2955 /*
2956  * Free up the v4root exports.
2957  */
2958 static void
2959 free_v4rootexp(void)
2960 {
2961
2962         if (v4root_ep != NULL) {
2963                 free_exp(v4root_ep);
2964                 v4root_ep = NULL;
2965         }
2966 }
2967
2968 /*
2969  * Free hosts.
2970  */
2971 static void
2972 free_host(struct hostlist *hp)
2973 {
2974         struct hostlist *hp2;
2975
2976         while (hp) {
2977                 hp2 = hp;
2978                 hp = hp->ht_next;
2979                 free((caddr_t)hp2);
2980         }
2981 }
2982
2983 static struct hostlist *
2984 get_ht(void)
2985 {
2986         struct hostlist *hp;
2987
2988         hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2989         if (hp == (struct hostlist *)NULL)
2990                 out_of_mem();
2991         hp->ht_next = (struct hostlist *)NULL;
2992         hp->ht_flag = 0;
2993         return (hp);
2994 }
2995
2996 /*
2997  * Out of memory, fatal
2998  */
2999 static void
3000 out_of_mem(void)
3001 {
3002
3003         syslog(LOG_ERR, "out of memory");
3004         exit(2);
3005 }
3006
3007 /*
3008  * Call do_mount() from the struct exportlist, for each case needed.
3009  */
3010 static int
3011 do_export_mount(struct exportlist *ep, struct statfs *fsp)
3012 {
3013         struct grouplist *grp, defgrp;
3014         int ret;
3015         size_t dirlen;
3016
3017         LOGDEBUG("do_export_mount=%s", ep->ex_fsdir);
3018         dirlen = strlen(ep->ex_fsdir);
3019         if ((ep->ex_flag & EX_DEFSET) != 0) {
3020                 defgrp.gr_type = GT_DEFAULT;
3021                 defgrp.gr_next = NULL;
3022                 /* We have an entry for all other hosts/nets. */
3023                 LOGDEBUG("ex_defexflags=0x%x", ep->ex_defexflags);
3024                 ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
3025                     ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors,
3026                     ep->ex_defsecflavors);
3027                 if (ret != 0)
3028                         return (ret);
3029         }
3030
3031         /* Do a mount for each group. */
3032         grp = ep->ex_grphead;
3033         while (grp != NULL) {
3034                 LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%x",
3035                     grp->gr_type, grp->gr_exflags);
3036                 ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
3037                     ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors,
3038                     grp->gr_secflavors);
3039                 if (ret != 0)
3040                         return (ret);
3041                 grp = grp->gr_next;
3042         }
3043         return (0);
3044 }
3045
3046 /*
3047  * Do the nmount() syscall with the update flag to push the export info into
3048  * the kernel.
3049  */
3050 static int
3051 do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
3052     struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb,
3053     int numsecflavors, int *secflavors)
3054 {
3055         struct statfs fsb1;
3056         struct addrinfo *ai;
3057         struct export_args *eap;
3058         char errmsg[255];
3059         char *cp;
3060         int done;
3061         char savedc;
3062         struct iovec *iov;
3063         int i, iovlen;
3064         int ret;
3065         struct nfsex_args nfsea;
3066
3067         eap = &nfsea.export;
3068
3069         cp = NULL;
3070         savedc = '\0';
3071         iov = NULL;
3072         iovlen = 0;
3073         ret = 0;
3074
3075         bzero(eap, sizeof (struct export_args));
3076         bzero(errmsg, sizeof(errmsg));
3077         eap->ex_flags = exflags;
3078         eap->ex_anon = *anoncrp;
3079         LOGDEBUG("do_mount exflags=0x%x", exflags);
3080         eap->ex_indexfile = ep->ex_indexfile;
3081         if (grp->gr_type == GT_HOST)
3082                 ai = grp->gr_ptr.gt_addrinfo;
3083         else
3084                 ai = NULL;
3085         eap->ex_numsecflavors = numsecflavors;
3086         LOGDEBUG("do_mount numsec=%d", numsecflavors);
3087         for (i = 0; i < eap->ex_numsecflavors; i++)
3088                 eap->ex_secflavors[i] = secflavors[i];
3089         if (eap->ex_numsecflavors == 0) {
3090                 eap->ex_numsecflavors = 1;
3091                 eap->ex_secflavors[0] = AUTH_SYS;
3092         }
3093         done = FALSE;
3094
3095         if (v4root_phase == 0) {
3096                 build_iovec(&iov, &iovlen, "fstype", NULL, 0);
3097                 build_iovec(&iov, &iovlen, "fspath", NULL, 0);
3098                 build_iovec(&iov, &iovlen, "from", NULL, 0);
3099                 build_iovec(&iov, &iovlen, "update", NULL, 0);
3100                 build_iovec(&iov, &iovlen, "export", eap,
3101                     sizeof (struct export_args));
3102                 build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
3103         }
3104
3105         while (!done) {
3106                 switch (grp->gr_type) {
3107                 case GT_HOST:
3108                         if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
3109                                 goto skip;
3110                         eap->ex_addr = ai->ai_addr;
3111                         eap->ex_addrlen = ai->ai_addrlen;
3112                         eap->ex_masklen = 0;
3113                         break;
3114                 case GT_NET:
3115                         if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
3116                             have_v6 == 0)
3117                                 goto skip;
3118                         eap->ex_addr =
3119                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
3120                         eap->ex_addrlen =
3121                             ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
3122                         eap->ex_mask =
3123                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
3124                         eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
3125                         break;
3126                 case GT_DEFAULT:
3127                         eap->ex_addr = NULL;
3128                         eap->ex_addrlen = 0;
3129                         eap->ex_mask = NULL;
3130                         eap->ex_masklen = 0;
3131                         break;
3132                 case GT_IGNORE:
3133                         ret = 0;
3134                         goto error_exit;
3135                         break;
3136                 default:
3137                         syslog(LOG_ERR, "bad grouptype");
3138                         if (cp)
3139                                 *cp = savedc;
3140                         ret = 1;
3141                         goto error_exit;
3142                 }
3143
3144                 /*
3145                  * For V4:, use the nfssvc() syscall, instead of mount().
3146                  */
3147                 if (v4root_phase == 2) {
3148                         nfsea.fspec = v4root_dirpath;
3149                         if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
3150                                 syslog(LOG_ERR, "Exporting V4: failed");
3151                                 return (2);
3152                         }
3153                 } else {
3154                         /*
3155                          * XXX:
3156                          * Maybe I should just use the fsb->f_mntonname path
3157                          * instead of looping back up the dirp to the mount
3158                          * point??
3159                          * Also, needs to know how to export all types of local
3160                          * exportable filesystems and not just "ufs".
3161                          */
3162                         iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
3163                         iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
3164                         iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
3165                         iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
3166                         iov[5].iov_base = fsb->f_mntfromname; /* "from" */
3167                         iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
3168                         errmsg[0] = '\0';
3169         
3170                         while (nmount(iov, iovlen, fsb->f_flags) < 0) {
3171                                 if (cp)
3172                                         *cp-- = savedc;
3173                                 else
3174                                         cp = dirp + dirplen - 1;
3175                                 if (opt_flags & OP_QUIET) {
3176                                         ret = 1;
3177                                         goto error_exit;
3178                                 }
3179                                 if (errno == EPERM) {
3180                                         if (debug)
3181                                                 warnx("can't change attributes for %s: %s",
3182                                                     dirp, errmsg);
3183                                         syslog(LOG_ERR,
3184                                            "can't change attributes for %s: %s",
3185                                             dirp, errmsg);
3186                                         ret = 1;
3187                                         goto error_exit;
3188                                 }
3189                                 if (opt_flags & OP_ALLDIRS) {
3190                                         if (errno == EINVAL)
3191                                                 syslog(LOG_ERR,
3192                 "-alldirs requested but %s is not a filesystem mountpoint",
3193                                                     dirp);
3194                                         else
3195                                                 syslog(LOG_ERR,
3196                                                     "could not remount %s: %m",
3197                                                     dirp);
3198                                         ret = 1;
3199                                         goto error_exit;
3200                                 }
3201                                 /* back up over the last component */
3202                                 while (cp > dirp && *cp == '/')
3203                                         cp--;
3204                                 while (cp > dirp && *(cp - 1) != '/')
3205                                         cp--;
3206                                 if (cp == dirp) {
3207                                         if (debug)
3208                                                 warnx("mnt unsucc");
3209                                         syslog(LOG_ERR, "can't export %s %s",
3210                                             dirp, errmsg);
3211                                         ret = 1;
3212                                         goto error_exit;
3213                                 }
3214                                 savedc = *cp;
3215                                 *cp = '\0';
3216                                 /*
3217                                  * Check that we're still on the same
3218                                  * filesystem.
3219                                  */
3220                                 if (statfs(dirp, &fsb1) != 0 ||
3221                                     fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) {
3222                                         *cp = savedc;
3223                                         syslog(LOG_ERR,
3224                                             "can't export %s %s", dirp,
3225                                             errmsg);
3226                                         ret = 1;
3227                                         goto error_exit;
3228                                 }
3229                         }
3230                 }
3231
3232                 /*
3233                  * For the experimental server:
3234                  * If this is the public directory, get the file handle
3235                  * and load it into the kernel via the nfssvc() syscall.
3236                  */
3237                 if ((exflags & MNT_EXPUBLIC) != 0) {
3238                         fhandle_t fh;
3239                         char *public_name;
3240
3241                         if (eap->ex_indexfile != NULL)
3242                                 public_name = eap->ex_indexfile;
3243                         else
3244                                 public_name = dirp;
3245                         if (getfh(public_name, &fh) < 0)
3246                                 syslog(LOG_ERR,
3247                                     "Can't get public fh for %s", public_name);
3248                         else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
3249                                 syslog(LOG_ERR,
3250                                     "Can't set public fh for %s", public_name);
3251                         else {
3252                                 has_publicfh = 1;
3253                                 has_set_publicfh = 1;
3254                                 ep->ex_flag |= EX_PUBLICFH;
3255                         }
3256                 }
3257 skip:
3258                 if (ai != NULL)
3259                         ai = ai->ai_next;
3260                 if (ai == NULL)
3261                         done = TRUE;
3262         }
3263         if (cp)
3264                 *cp = savedc;
3265 error_exit:
3266         /* free strings allocated by strdup() in getmntopts.c */
3267         if (iov != NULL) {
3268                 free(iov[0].iov_base); /* fstype */
3269                 free(iov[2].iov_base); /* fspath */
3270                 free(iov[4].iov_base); /* from */
3271                 free(iov[6].iov_base); /* update */
3272                 free(iov[8].iov_base); /* export */
3273                 free(iov[10].iov_base); /* errmsg */
3274
3275                 /* free iov, allocated by realloc() */
3276                 free(iov);
3277         }
3278         return (ret);
3279 }
3280
3281 /*
3282  * Translate a net address.
3283  *
3284  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
3285  */
3286 static int
3287 get_net(char *cp, struct netmsk *net, int maskflg)
3288 {
3289         struct netent *np = NULL;
3290         char *name, *p, *prefp;
3291         struct sockaddr_in sin;
3292         struct sockaddr *sa = NULL;
3293         struct addrinfo hints, *ai = NULL;
3294         char netname[NI_MAXHOST];
3295         long preflen;
3296
3297         p = prefp = NULL;
3298         if ((opt_flags & OP_MASKLEN) && !maskflg) {
3299                 p = strchr(cp, '/');
3300                 *p = '\0';
3301                 prefp = p + 1;
3302         }
3303
3304         /*
3305          * Check for a numeric address first. We wish to avoid
3306          * possible DNS lookups in getnetbyname().
3307          */
3308         if (isxdigit(*cp) || *cp == ':') {
3309                 memset(&hints, 0, sizeof hints);
3310                 /* Ensure the mask and the network have the same family. */
3311                 if (maskflg && (opt_flags & OP_NET))
3312                         hints.ai_family = net->nt_net.ss_family;
3313                 else if (!maskflg && (opt_flags & OP_HAVEMASK))
3314                         hints.ai_family = net->nt_mask.ss_family;
3315                 else
3316                         hints.ai_family = AF_UNSPEC;
3317                 hints.ai_flags = AI_NUMERICHOST;
3318                 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
3319                         sa = ai->ai_addr;
3320                 if (sa != NULL && ai->ai_family == AF_INET) {
3321                         /*
3322                          * The address in `cp' is really a network address, so
3323                          * use inet_network() to re-interpret this correctly.
3324                          * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
3325                          */
3326                         bzero(&sin, sizeof sin);
3327                         sin.sin_family = AF_INET;
3328                         sin.sin_len = sizeof sin;
3329                         sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
3330                         if (debug)
3331                                 fprintf(stderr, "get_net: v4 addr %s\n",
3332                                     inet_ntoa(sin.sin_addr));
3333                         sa = (struct sockaddr *)&sin;
3334                 }
3335         }
3336         if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
3337                 bzero(&sin, sizeof sin);
3338                 sin.sin_family = AF_INET;
3339                 sin.sin_len = sizeof sin;
3340                 sin.sin_addr = inet_makeaddr(np->n_net, 0);
3341                 sa = (struct sockaddr *)&sin;
3342         }
3343         if (sa == NULL)
3344                 goto fail;
3345
3346         if (maskflg) {
3347                 /* The specified sockaddr is a mask. */
3348                 if (checkmask(sa) != 0)
3349                         goto fail;
3350                 bcopy(sa, &net->nt_mask, sa->sa_len);
3351                 opt_flags |= OP_HAVEMASK;
3352         } else {
3353                 /* The specified sockaddr is a network address. */
3354                 bcopy(sa, &net->nt_net, sa->sa_len);
3355
3356                 /* Get a network name for the export list. */
3357                 if (np) {
3358                         name = np->n_name;
3359                 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
3360                    NULL, 0, NI_NUMERICHOST) == 0) {
3361                         name = netname;
3362                 } else {
3363                         goto fail;
3364                 }
3365                 if ((net->nt_name = strdup(name)) == NULL)
3366                         out_of_mem();
3367
3368                 /*
3369                  * Extract a mask from either a "/<masklen>" suffix, or
3370                  * from the class of an IPv4 address.
3371                  */
3372                 if (opt_flags & OP_MASKLEN) {
3373                         preflen = strtol(prefp, NULL, 10);
3374                         if (preflen < 0L || preflen == LONG_MAX)
3375                                 goto fail;
3376                         bcopy(sa, &net->nt_mask, sa->sa_len);
3377                         if (makemask(&net->nt_mask, (int)preflen) != 0)
3378                                 goto fail;
3379                         opt_flags |= OP_HAVEMASK;
3380                         *p = '/';
3381                 } else if (sa->sa_family == AF_INET &&
3382                     (opt_flags & OP_MASK) == 0) {
3383                         in_addr_t addr;
3384
3385                         addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
3386                         if (IN_CLASSA(addr))
3387                                 preflen = 8;
3388                         else if (IN_CLASSB(addr))
3389                                 preflen = 16;
3390                         else if (IN_CLASSC(addr))
3391                                 preflen = 24;
3392                         else if (IN_CLASSD(addr))
3393                                 preflen = 28;
3394                         else
3395                                 preflen = 32;   /* XXX */
3396
3397                         bcopy(sa, &net->nt_mask, sa->sa_len);
3398                         makemask(&net->nt_mask, (int)preflen);
3399                         opt_flags |= OP_HAVEMASK;
3400                 }
3401         }
3402
3403         if (ai)
3404                 freeaddrinfo(ai);
3405         return 0;
3406
3407 fail:
3408         if (ai)
3409                 freeaddrinfo(ai);
3410         return 1;
3411 }
3412
3413 /*
3414  * Parse out the next white space separated field
3415  */
3416 static void
3417 nextfield(char **cp, char **endcp)
3418 {
3419         char *p;
3420         char quot = 0;
3421
3422         p = *cp;
3423         while (*p == ' ' || *p == '\t')
3424                 p++;
3425         *cp = p;
3426         while (*p != '\0') {
3427                 if (quot) {
3428                         if (*p == quot)
3429                                 quot = 0;
3430                 } else {
3431                         if (*p == '\\' && *(p + 1) != '\0')
3432                                 p++;
3433                         else if (*p == '\'' || *p == '"')
3434                                 quot = *p;
3435                         else if (*p == ' ' || *p == '\t')
3436                                 break;
3437                 }
3438                 p++;
3439         };
3440         *endcp = p;
3441 }
3442
3443 /*
3444  * Get an exports file line. Skip over blank lines and handle line
3445  * continuations.
3446  */
3447 static int
3448 get_line(void)
3449 {
3450         char *p, *cp;
3451         size_t len;
3452         int totlen, cont_line;
3453
3454         /*
3455          * Loop around ignoring blank lines and getting all continuation lines.
3456          */
3457         p = line;
3458         totlen = 0;
3459         do {
3460                 if ((p = fgetln(exp_file, &len)) == NULL)
3461                         return (0);
3462                 cp = p + len - 1;
3463                 cont_line = 0;
3464                 while (cp >= p &&
3465                     (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
3466                         if (*cp == '\\')
3467                                 cont_line = 1;
3468                         cp--;
3469                         len--;
3470                 }
3471                 if (cont_line) {
3472                         *++cp = ' ';
3473                         len++;
3474                 }
3475                 if (linesize < len + totlen + 1) {
3476                         linesize = len + totlen + 1;
3477                         line = realloc(line, linesize);
3478                         if (line == NULL)
3479                                 out_of_mem();
3480                 }
3481                 memcpy(line + totlen, p, len);
3482                 totlen += len;
3483                 line[totlen] = '\0';
3484         } while (totlen == 0 || cont_line);
3485         return (1);
3486 }
3487
3488 /*
3489  * Parse a description of a credential.
3490  */
3491 static void
3492 parsecred(char *namelist, struct xucred *cr)
3493 {
3494         char *name;
3495         int cnt;
3496         char *names;
3497         struct passwd *pw;
3498         struct group *gr;
3499         gid_t groups[XU_NGROUPS + 1];
3500         int ngroups;
3501
3502         cr->cr_version = XUCRED_VERSION;
3503         /*
3504          * Set up the unprivileged user.
3505          */
3506         cr->cr_uid = 65534;
3507         cr->cr_groups[0] = 65533;
3508         cr->cr_ngroups = 1;
3509         /*
3510          * Get the user's password table entry.
3511          */
3512         names = namelist;
3513         name = strsep_quote(&names, ":");
3514         /* Bug?  name could be NULL here */
3515         if (isdigit(*name) || *name == '-')
3516                 pw = getpwuid(atoi(name));
3517         else
3518                 pw = getpwnam(name);
3519         /*
3520          * Credentials specified as those of a user.
3521          */
3522         if (names == NULL) {
3523                 if (pw == NULL) {
3524                         syslog(LOG_ERR, "unknown user: %s", name);
3525                         return;
3526                 }
3527                 cr->cr_uid = pw->pw_uid;
3528                 ngroups = XU_NGROUPS + 1;
3529                 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
3530                         syslog(LOG_ERR, "too many groups");
3531                         ngroups = XU_NGROUPS + 1;
3532                 }
3533
3534                 /*
3535                  * Compress out duplicate.
3536                  */
3537                 cr->cr_groups[0] = groups[0];
3538                 if (ngroups > 1 && groups[0] == groups[1]) {
3539                         cr->cr_ngroups = ngroups - 1;
3540                         for (cnt = 2; cnt < ngroups; cnt++)
3541                                 cr->cr_groups[cnt - 1] = groups[cnt];
3542                 } else {
3543                         cr->cr_ngroups = ngroups;
3544                         if (cr->cr_ngroups > XU_NGROUPS)
3545                                 cr->cr_ngroups = XU_NGROUPS;
3546                         for (cnt = 1; cnt < cr->cr_ngroups; cnt++)
3547                                 cr->cr_groups[cnt] = groups[cnt];
3548                 }
3549                 return;
3550         }
3551         /*
3552          * Explicit credential specified as a colon separated list:
3553          *      uid:gid:gid:...
3554          */
3555         if (pw != NULL)
3556                 cr->cr_uid = pw->pw_uid;
3557         else if (isdigit(*name) || *name == '-')
3558                 cr->cr_uid = atoi(name);
3559         else {
3560                 syslog(LOG_ERR, "unknown user: %s", name);
3561                 return;
3562         }
3563         cr->cr_ngroups = 0;
3564         while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
3565                 name = strsep_quote(&names, ":");
3566                 if (isdigit(*name) || *name == '-') {
3567                         cr->cr_groups[cr->cr_ngroups++] = atoi(name);
3568                 } else {
3569                         if ((gr = getgrnam(name)) == NULL) {
3570                                 syslog(LOG_ERR, "unknown group: %s", name);
3571                                 continue;
3572                         }
3573                         cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
3574                 }
3575         }
3576         if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
3577                 syslog(LOG_ERR, "too many groups");
3578 }
3579
3580 #define STRSIZ  (MNTNAMLEN+MNTPATHLEN+50)
3581 /*
3582  * Routines that maintain the remote mounttab
3583  */
3584 static void
3585 get_mountlist(void)
3586 {
3587         struct mountlist *mlp;
3588         char *host, *dirp, *cp;
3589         char str[STRSIZ];
3590         FILE *mlfile;
3591
3592         if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3593                 if (errno == ENOENT)
3594                         return;
3595                 else {
3596                         syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3597                         return;
3598                 }
3599         }
3600         while (fgets(str, STRSIZ, mlfile) != NULL) {
3601                 cp = str;
3602                 host = strsep(&cp, " \t\n");
3603                 dirp = strsep(&cp, " \t\n");
3604                 if (host == NULL || dirp == NULL)
3605                         continue;
3606                 mlp = (struct mountlist *)malloc(sizeof (*mlp));
3607                 if (mlp == (struct mountlist *)NULL)
3608                         out_of_mem();
3609                 strncpy(mlp->ml_host, host, MNTNAMLEN);
3610                 mlp->ml_host[MNTNAMLEN] = '\0';
3611                 strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3612                 mlp->ml_dirp[MNTPATHLEN] = '\0';
3613
3614                 SLIST_INSERT_HEAD(&mlhead, mlp, next);
3615         }
3616         fclose(mlfile);
3617 }
3618
3619 static void
3620 del_mlist(char *hostp, char *dirp)
3621 {
3622         struct mountlist *mlp, *mlp2;
3623         FILE *mlfile;
3624         int fnd = 0;
3625
3626         SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3627                 if (!strcmp(mlp->ml_host, hostp) &&
3628                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3629                         fnd = 1;
3630                         SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3631                         free((caddr_t)mlp);
3632                 }
3633         }
3634         if (fnd) {
3635                 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3636                         syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3637                         return;
3638                 }
3639                 SLIST_FOREACH(mlp, &mlhead, next) {
3640                         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3641                 }
3642                 fclose(mlfile);
3643         }
3644 }
3645
3646 static void
3647 add_mlist(char *hostp, char *dirp)
3648 {
3649         struct mountlist *mlp;
3650         FILE *mlfile;
3651
3652         SLIST_FOREACH(mlp, &mlhead, next) {
3653                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3654                         return;
3655         }
3656
3657         mlp = (struct mountlist *)malloc(sizeof (*mlp));
3658         if (mlp == (struct mountlist *)NULL)
3659                 out_of_mem();
3660         strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3661         mlp->ml_host[MNTNAMLEN] = '\0';
3662         strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3663         mlp->ml_dirp[MNTPATHLEN] = '\0';
3664         SLIST_INSERT_HEAD(&mlhead, mlp, next);
3665         if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3666                 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3667                 return;
3668         }
3669         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3670         fclose(mlfile);
3671 }
3672
3673 /*
3674  * Free up a group list.
3675  */
3676 static void
3677 free_grp(struct grouplist *grp)
3678 {
3679         if (grp->gr_type == GT_HOST) {
3680                 if (grp->gr_ptr.gt_addrinfo != NULL)
3681                         freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3682         } else if (grp->gr_type == GT_NET) {
3683                 if (grp->gr_ptr.gt_net.nt_name)
3684                         free(grp->gr_ptr.gt_net.nt_name);
3685         }
3686         free((caddr_t)grp);
3687 }
3688
3689 #ifdef DEBUG
3690 static void
3691 SYSLOG(int pri, const char *fmt, ...)
3692 {
3693         va_list ap;
3694
3695         va_start(ap, fmt);
3696         vfprintf(stderr, fmt, ap);
3697         va_end(ap);
3698 }
3699 #endif /* DEBUG */
3700
3701 /*
3702  * Check options for consistency.
3703  */
3704 static int
3705 check_options(struct dirlist *dp)
3706 {
3707
3708         if (v4root_phase == 0 && dp == NULL)
3709             return (1);
3710         if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3711             syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3712             return (1);
3713         }
3714         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3715                 syslog(LOG_ERR, "-mask requires -network");
3716                 return (1);
3717         }
3718         if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3719                 syslog(LOG_ERR, "-network requires mask specification");
3720                 return (1);
3721         }
3722         if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3723                 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3724                 return (1);
3725         }
3726         if (v4root_phase > 0 &&
3727             (opt_flags &
3728              ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3729             syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3730             return (1);
3731         }
3732         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3733             syslog(LOG_ERR, "-alldirs has multiple directories");
3734             return (1);
3735         }
3736         return (0);
3737 }
3738
3739 /*
3740  * Check an absolute directory path for any symbolic links. Return true
3741  */
3742 static int
3743 check_dirpath(char *dirp)
3744 {
3745         char *cp;
3746         int ret = 1;
3747         struct stat sb;
3748
3749         cp = dirp + 1;
3750         while (*cp && ret) {
3751                 if (*cp == '/') {
3752                         *cp = '\0';
3753                         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3754                                 ret = 0;
3755                         *cp = '/';
3756                 }
3757                 cp++;
3758         }
3759         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3760                 ret = 0;
3761         return (ret);
3762 }
3763
3764 /*
3765  * Make a netmask according to the specified prefix length. The ss_family
3766  * and other non-address fields must be initialised before calling this.
3767  */
3768 static int
3769 makemask(struct sockaddr_storage *ssp, int bitlen)
3770 {
3771         u_char *p;
3772         int bits, i, len;
3773
3774         if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3775                 return (-1);
3776         if (bitlen > len * CHAR_BIT)
3777                 return (-1);
3778
3779         for (i = 0; i < len; i++) {
3780                 bits = MIN(CHAR_BIT, bitlen);
3781                 *p++ = (u_char)~0 << (CHAR_BIT - bits);
3782                 bitlen -= bits;
3783         }
3784         return 0;
3785 }
3786
3787 /*
3788  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3789  * is acceptable (i.e. of the form 1...10....0).
3790  */
3791 static int
3792 checkmask(struct sockaddr *sa)
3793 {
3794         u_char *mask;
3795         int i, len;
3796
3797         if ((mask = sa_rawaddr(sa, &len)) == NULL)
3798                 return (-1);
3799
3800         for (i = 0; i < len; i++)
3801                 if (mask[i] != 0xff)
3802                         break;
3803         if (i < len) {
3804                 if (~mask[i] & (u_char)(~mask[i] + 1))
3805                         return (-1);
3806                 i++;
3807         }
3808         for (; i < len; i++)
3809                 if (mask[i] != 0)
3810                         return (-1);
3811         return (0);
3812 }
3813
3814 /*
3815  * Compare two sockaddrs according to a specified mask. Return zero if
3816  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3817  * If samask is NULL, perform a full comparison.
3818  */
3819 static int
3820 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3821 {
3822         unsigned char *p1, *p2, *mask;
3823         int len, i;
3824
3825         if (sa1->sa_family != sa2->sa_family ||
3826             (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3827             (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3828                 return (1);
3829
3830         switch (sa1->sa_family) {
3831         case AF_INET6:
3832                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3833                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3834                         return (1);
3835                 break;
3836         }
3837
3838         /* Simple binary comparison if no mask specified. */
3839         if (samask == NULL)
3840                 return (memcmp(p1, p2, len));
3841
3842         /* Set up the mask, and do a mask-based comparison. */
3843         if (sa1->sa_family != samask->sa_family ||
3844             (mask = sa_rawaddr(samask, NULL)) == NULL)
3845                 return (1);
3846
3847         for (i = 0; i < len; i++)
3848                 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3849                         return (1);
3850         return (0);
3851 }
3852
3853 /*
3854  * Return a pointer to the part of the sockaddr that contains the
3855  * raw address, and set *nbytes to its length in bytes. Returns
3856  * NULL if the address family is unknown.
3857  */
3858 static void *
3859 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3860         void *p;
3861         int len;
3862
3863         switch (sa->sa_family) {
3864         case AF_INET:
3865                 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3866                 p = &((struct sockaddr_in *)sa)->sin_addr;
3867                 break;
3868         case AF_INET6:
3869                 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3870                 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3871                 break;
3872         default:
3873                 p = NULL;
3874                 len = 0;
3875         }
3876
3877         if (nbytes != NULL)
3878                 *nbytes = len;
3879         return (p);
3880 }
3881
3882 static void
3883 huphandler(int sig __unused)
3884 {
3885
3886         got_sighup = 1;
3887 }
3888
3889 static void
3890 terminate(int sig __unused)
3891 {
3892         pidfile_remove(pfh);
3893         rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3894         rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3895         exit (0);
3896 }