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