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