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