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