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