]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/netbsd-tests/fs/nfs/nfsservice/mountd.c
MFC r296586:
[FreeBSD/stable/10.git] / contrib / netbsd-tests / fs / nfs / nfsservice / mountd.c
1 /*      $NetBSD: mountd.c,v 1.8 2013/10/19 17:45:00 christos Exp $       */
2
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 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
38  The Regents of the University of California.  All rights reserved.");
39 #endif                          /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char     sccsid[] = "@(#)mountd.c  8.15 (Berkeley) 5/1/95";
44 #else
45 __RCSID("$NetBSD: mountd.c,v 1.8 2013/10/19 17:45:00 christos Exp $");
46 #endif
47 #endif                          /* not lint */
48
49 #include <sys/param.h>
50 #include <sys/file.h>
51 #include <sys/ioctl.h>
52 #include <sys/mount.h>
53 #include <sys/socket.h>
54 #include <sys/stat.h>
55 #include <syslog.h>
56 #include <sys/ucred.h>
57
58 #include <rpc/rpc.h>
59 #include <rpc/pmap_clnt.h>
60 #include <rpc/pmap_prot.h>
61 #include <rpcsvc/mount.h>
62 #include <nfs/rpcv2.h>
63 #include <nfs/nfsproto.h>
64 #include <nfs/nfs.h>
65 #include <nfs/nfsmount.h>
66
67 #include <arpa/inet.h>
68
69 #include <rump/rump.h>
70 #include <rump/rump_syscalls.h>
71
72 #include <ctype.h>
73 #include <errno.h>
74 #include <grp.h>
75 #include <netdb.h>
76 #include <pwd.h>
77 #include <netgroup.h>
78 #include <pthread.h>
79 #include <semaphore.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <unistd.h>
85 #include <err.h>
86 #include <util.h>
87 #include "pathnames.h"
88
89 #ifdef IPSEC
90 #include <netinet6/ipsec.h>
91 #ifndef IPSEC_POLICY_IPSEC      /* no ipsec support on old ipsec */
92 #undef IPSEC
93 #endif
94 #include "ipsec.h"
95 #endif
96
97 #include "svc_fdset.h"
98
99 #include <stdarg.h>
100
101 /*
102  * Structures for keeping the mount list and export list
103  */
104 struct mountlist {
105         struct mountlist *ml_next;
106         char ml_host[RPCMNT_NAMELEN + 1];
107         char ml_dirp[RPCMNT_PATHLEN + 1];
108         int ml_flag;/* XXX more flags (same as dp_flag) */
109 };
110
111 struct dirlist {
112         struct dirlist *dp_left;
113         struct dirlist *dp_right;
114         int dp_flag;
115         struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
116         char dp_dirp[1];                /* Actually malloc'd to size of dir */
117 };
118 /* dp_flag bits */
119 #define DP_DEFSET       0x1
120 #define DP_HOSTSET      0x2
121 #define DP_KERB         0x4
122 #define DP_NORESMNT     0x8
123
124 struct exportlist {
125         struct exportlist *ex_next;
126         struct dirlist *ex_dirl;
127         struct dirlist *ex_defdir;
128         int             ex_flag;
129         fsid_t          ex_fs;
130         char           *ex_fsdir;
131         char           *ex_indexfile;
132 };
133 /* ex_flag bits */
134 #define EX_LINKED       0x1
135
136 struct netmsk {
137         struct sockaddr_storage nt_net;
138         int             nt_len;
139         char           *nt_name;
140 };
141
142 union grouptypes {
143         struct addrinfo *gt_addrinfo;
144         struct netmsk   gt_net;
145 };
146
147 struct grouplist {
148         int             gr_type;
149         union grouptypes gr_ptr;
150         struct grouplist *gr_next;
151 };
152 /* Group types */
153 #define GT_NULL         0x0
154 #define GT_HOST         0x1
155 #define GT_NET          0x2
156
157 struct hostlist {
158         int             ht_flag;/* Uses DP_xx bits */
159         struct grouplist *ht_grp;
160         struct hostlist *ht_next;
161 };
162
163 struct fhreturn {
164         int             fhr_flag;
165         int             fhr_vers;
166         size_t          fhr_fhsize;
167         union {
168                 uint8_t v2[NFSX_V2FH];
169                 uint8_t v3[NFSX_V3FHMAX];
170         } fhr_fh;
171 };
172
173 /* Global defs */
174 static char    *add_expdir __P((struct dirlist **, char *, int));
175 static void add_dlist __P((struct dirlist **, struct dirlist *,
176     struct grouplist *, int));
177 static void add_mlist __P((char *, char *, int));
178 static int check_dirpath __P((const char *, size_t, char *));
179 static int check_options __P((const char *, size_t, struct dirlist *));
180 static int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *));
181 static int del_mlist __P((char *, char *, struct sockaddr *));
182 static struct dirlist *dirp_search __P((struct dirlist *, char *));
183 static int do_nfssvc __P((const char *, size_t, struct exportlist *,
184     struct grouplist *, int, struct uucred *, char *, int, struct statvfs *));
185 static int do_opt __P((const char *, size_t, char **, char **,
186     struct exportlist *, struct grouplist *, int *, int *, struct uucred *));
187 static struct exportlist *ex_search __P((fsid_t *));
188 static int parse_directory __P((const char *, size_t, struct grouplist *,
189     int, char *, struct exportlist **, struct statvfs *));
190 static int parse_host_netgroup __P((const char *, size_t, struct exportlist *,
191     struct grouplist *, char *, int *, struct grouplist **));
192 static struct exportlist *get_exp __P((void));
193 static void free_dir __P((struct dirlist *));
194 static void free_exp __P((struct exportlist *));
195 static void free_grp __P((struct grouplist *));
196 static void free_host __P((struct hostlist *));
197 void get_exportlist __P((int));
198 static int get_host __P((const char *, size_t, const char *,
199     struct grouplist *));
200 static struct hostlist *get_ht __P((void));
201 static void get_mountlist __P((void));
202 static int get_net __P((char *, struct netmsk *, int));
203 static void free_exp_grp __P((struct exportlist *, struct grouplist *));
204 static struct grouplist *get_grp __P((void));
205 static void hang_dirp __P((struct dirlist *, struct grouplist *,
206     struct exportlist *, int));
207 static void mntsrv __P((struct svc_req *, SVCXPRT *));
208 static void nextfield __P((char **, char **));
209 static void parsecred __P((char *, struct uucred *));
210 static int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
211 static int scan_tree __P((struct dirlist *, struct sockaddr *));
212 static void send_umntall __P((int));
213 #if 0
214 static int umntall_each __P((caddr_t, struct sockaddr_in *));
215 #endif
216 static int xdr_dir __P((XDR *, char *));
217 static int xdr_explist __P((XDR *, caddr_t));
218 static int xdr_fhs __P((XDR *, caddr_t));
219 static int xdr_mlist __P((XDR *, caddr_t));
220 static int bitcmp __P((void *, void *, int));
221 static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int));
222 static int sacmp __P((struct sockaddr *, struct sockaddr *));
223 static int allones __P((struct sockaddr_storage *, int));
224 static int countones __P((struct sockaddr *));
225 static void bind_resv_port __P((int, sa_family_t, in_port_t));
226 static void no_nfs(int);
227 static struct exportlist *exphead;
228 static struct mountlist *mlhead;
229 static struct grouplist *grphead;
230 static char    *exname;
231 static struct uucred def_anon = {
232         1,
233         (uid_t) -2,
234         (gid_t) -2,
235         0,
236         { 0 }
237 };
238
239 static int      opt_flags;
240 static int      have_v6 = 1;
241 static const int ninumeric = NI_NUMERICHOST;
242
243 /* Bits for above */
244 #define OP_MAPROOT      0x001
245 #define OP_MAPALL       0x002
246 #define OP_KERB         0x004
247 #define OP_MASK         0x008
248 #define OP_NET          0x010
249 #define OP_ALLDIRS      0x040
250 #define OP_NORESPORT    0x080
251 #define OP_NORESMNT     0x100
252 #define OP_MASKLEN      0x200
253
254 static int      debug = 1;
255 #if 0
256 static void SYSLOG __P((int, const char *,...));
257 #endif
258 int main __P((int, char *[]));
259
260 /*
261  * If this is non-zero, -noresvport and -noresvmnt are implied for
262  * each export.
263  */
264 static int noprivports;
265
266 #define C2FD(_c_) ((int)(uintptr_t)(_c_))
267 static int
268 rumpread(void *cookie, char *buf, int count)
269 {
270
271         return rump_sys_read(C2FD(cookie), buf, count);
272 }
273
274 static int
275 rumpwrite(void *cookie, const char *buf, int count)
276 {
277
278         return rump_sys_write(C2FD(cookie), buf, count);
279 }
280
281 static off_t
282 rumpseek(void *cookie, off_t off, int whence)
283 {
284
285         return rump_sys_lseek(C2FD(cookie), off, whence);
286 }
287
288 static int
289 rumpclose(void *cookie)
290 {
291
292         return rump_sys_close(C2FD(cookie));
293 }
294
295 int __sflags(const char *, int *); /* XXX */
296 static FILE *
297 rumpfopen(const char *path, const char *opts)
298 {
299         int fd, oflags;
300
301         __sflags(opts, &oflags);
302         fd = rump_sys_open(path, oflags, 0777);
303         if (fd == -1)
304                 return NULL;
305
306         return funopen((void *)(uintptr_t)fd,
307             rumpread, rumpwrite, rumpseek, rumpclose);
308 }
309
310 /*
311  * Make sure mountd signal handler is executed from a thread context
312  * instead of the signal handler.  This avoids the signal handler
313  * ruining our kernel context.
314  */
315 static sem_t exportsem;
316 static void
317 signal_get_exportlist(int sig)
318 {
319
320         sem_post(&exportsem);
321 }
322
323 static void *
324 exportlist_thread(void *arg)
325 {
326
327         for (;;) {
328                 sem_wait(&exportsem);
329                 get_exportlist(0);
330         }
331
332         return NULL;
333 }
334
335 /*
336  * Mountd server for NFS mount protocol as described in:
337  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
338  * The optional arguments are the exports file name
339  * default: _PATH_EXPORTS
340  * "-d" to enable debugging
341  * and "-n" to allow nonroot mount.
342  */
343 void *mountd_main(void *);
344 void *
345 mountd_main(void *arg)
346 {
347         SVCXPRT *udptransp, *tcptransp;
348         struct netconfig *udpconf, *tcpconf;
349         int udpsock, tcpsock;
350         int xcreated = 0;
351         int maxrec = RPC_MAXDATASIZE;
352         in_port_t forcedport = 0;
353         extern sem_t gensem;
354         pthread_t ptdummy;
355
356         alloc_fdset();
357
358 #if 0
359         while ((c = getopt(argc, argv, "dNnrp:" ADDOPTS)) != -1)
360                 switch (c) {
361 #ifdef IPSEC
362                 case 'P':
363                         if (ipsecsetup_test(policy = optarg))
364                                 errx(1, "Invalid ipsec policy `%s'", policy);
365                         break;
366 #endif
367                 case 'p':
368                         /* A forced port "0" will dynamically allocate a port */
369                         forcedport = atoi(optarg);
370                         break;
371                 case 'd':
372                         debug = 1;
373                         break;
374                 case 'N':
375                         noprivports = 1;
376                         break;
377                         /* Compatibility */
378                 case 'n':
379                 case 'r':
380                         break;
381                 default:
382                         fprintf(stderr, "usage: %s [-dNn]"
383 #ifdef IPSEC
384                             " [-P policy]"
385 #endif
386                             " [-p port] [exportsfile]\n", getprogname());
387                         exit(1);
388                 };
389         argc -= optind;
390         argv += optind;
391 #endif
392
393         sem_init(&exportsem, 0, 0);
394         pthread_create(&ptdummy, NULL, exportlist_thread, NULL);
395
396         grphead = NULL;
397         exphead = NULL;
398         mlhead = NULL;
399         exname = _PATH_EXPORTS;
400         openlog("mountd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON);
401         (void)signal(SIGSYS, no_nfs);
402
403         if (debug)
404                 (void)fprintf(stderr, "Getting export list.\n");
405         get_exportlist(0);
406         if (debug)
407                 (void)fprintf(stderr, "Getting mount list.\n");
408         get_mountlist();
409         if (debug)
410                 (void)fprintf(stderr, "Here we go.\n");
411         if (debug == 0) {
412                 daemon(0, 0);
413                 (void)signal(SIGINT, SIG_IGN);
414                 (void)signal(SIGQUIT, SIG_IGN);
415         }
416         (void)signal(SIGHUP, signal_get_exportlist);
417         (void)signal(SIGTERM, send_umntall);
418         pidfile(NULL);
419
420         rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
421         rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
422
423         udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
424         tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
425
426         udpconf  = getnetconfigent("udp");
427         tcpconf  = getnetconfigent("tcp");
428
429         rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
430
431         if (udpsock != -1 && udpconf != NULL) {
432                 bind_resv_port(udpsock, AF_INET, forcedport);
433 #ifdef IPSEC
434                 if (policy)
435                         ipsecsetup(AF_INET, udpsock, policy);
436 #endif
437                 udptransp = svc_dg_create(udpsock, 0, 0);
438                 if (udptransp != NULL) {
439                         if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
440                                 mntsrv, udpconf) ||
441                             !svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
442                                 mntsrv, udpconf)) {
443                                 syslog(LOG_WARNING, "can't register UDP service");
444                         }
445                         else {
446                                 xcreated++;
447                         }
448                 } else {
449                         syslog(LOG_WARNING, "can't create UDP service");
450                 }
451                         
452         }
453
454         if (tcpsock != -1 && tcpconf != NULL) {
455                 bind_resv_port(tcpsock, AF_INET, forcedport);
456 #ifdef IPSEC
457                 if (policy)
458                         ipsecsetup(AF_INET, tcpsock, policy);
459 #endif
460                 listen(tcpsock, SOMAXCONN);
461                 tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE,
462                     RPC_MAXDATASIZE);
463                 if (tcptransp != NULL) {
464                         if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
465                                 mntsrv, tcpconf) ||
466                             !svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
467                                 mntsrv, tcpconf))
468                                 syslog(LOG_WARNING, "can't register TCP service");
469                         else
470                                 xcreated++;
471                 } else
472                         syslog(LOG_WARNING, "can't create TCP service");
473
474         }
475
476         if (xcreated == 0) {
477                 syslog(LOG_ERR, "could not create any services");
478                 exit(1);
479         }
480
481         sem_post(&gensem);
482         svc_run();
483         syslog(LOG_ERR, "Mountd died");
484         exit(1);
485 }
486
487 /*
488  * The mount rpc service
489  */
490 void
491 mntsrv(rqstp, transp)
492         struct svc_req *rqstp;
493         SVCXPRT *transp;
494 {
495         struct exportlist *ep;
496         struct dirlist *dp;
497         struct fhreturn fhr;
498         struct stat     stb;
499         struct statvfs   fsb;
500         char host[NI_MAXHOST], numerichost[NI_MAXHOST];
501         int lookup_failed = 1;
502         struct sockaddr *saddr;
503         u_short         sport;
504         char            rpcpath[RPCMNT_PATHLEN + 1], dpath[MAXPATHLEN];
505         long            bad = EACCES;
506         int             defset, hostset, ret;
507         sigset_t        sighup_mask;
508         struct sockaddr_in6 *sin6;
509         struct sockaddr_in *sin;
510         size_t fh_size;
511         int error = 0;
512
513         (void)sigemptyset(&sighup_mask);
514         (void)sigaddset(&sighup_mask, SIGHUP);
515         saddr = svc_getrpccaller(transp)->buf;
516         switch (saddr->sa_family) {
517         case AF_INET6:
518                 sin6 = (struct sockaddr_in6 *)saddr;
519                 sport = ntohs(sin6->sin6_port);
520                 break;
521         case AF_INET:
522                 sin = (struct sockaddr_in *)saddr;
523                 sport = ntohs(sin->sin_port);
524                 break;
525         default:
526                 syslog(LOG_ERR, "request from unknown address family");
527                 return;
528         }
529         lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 
530             NULL, 0, 0);
531         if (getnameinfo(saddr, saddr->sa_len, numerichost,
532             sizeof numerichost, NULL, 0, ninumeric) != 0)
533                 strlcpy(numerichost, "?", sizeof(numerichost));
534         ret = 0;
535         switch (rqstp->rq_proc) {
536         case NULLPROC:
537                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
538                         syslog(LOG_ERR, "Can't send reply");
539                 return;
540         case MOUNTPROC_MNT:
541                 if (debug)
542                         fprintf(stderr,
543                             "got mount request from %s\n", numerichost);
544                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
545                         if (debug)
546                                 fprintf(stderr, "-> garbage args\n");
547                         svcerr_decode(transp);
548                         return;
549                 }
550                 if (debug)
551                         fprintf(stderr,
552                             "-> rpcpath: %s\n", rpcpath);
553                 /*
554                  * Get the real pathname and make sure it is a file or
555                  * directory that exists.
556                  */
557 #if 0
558                 if (realpath(rpcpath, dpath) == 0 ||
559 #endif
560                 strcpy(dpath, rpcpath);
561                 if (rump_sys_stat(dpath, &stb) < 0 ||
562                     (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
563                     rump_sys_statvfs1(dpath, &fsb, ST_WAIT) < 0) {
564                         (void)chdir("/"); /* Just in case realpath doesn't */
565                         if (debug)
566                                 (void)fprintf(stderr, "-> stat failed on %s\n",
567                                     dpath);
568                         if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t) &bad))
569                                 syslog(LOG_ERR, "Can't send reply");
570                         return;
571                 }
572                 if (debug)
573                         fprintf(stderr,
574                             "-> dpath: %s\n", dpath);
575                 /* Check in the exports list */
576                 (void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
577                 ep = ex_search(&fsb.f_fsidx);
578                 hostset = defset = 0;
579                 if (ep && (chk_host(ep->ex_defdir, saddr, &defset,
580                    &hostset) || ((dp = dirp_search(ep->ex_dirl, dpath)) &&
581                    chk_host(dp, saddr, &defset, &hostset)) ||
582                    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
583                    scan_tree(ep->ex_dirl, saddr) == 0))) {
584                         if ((hostset & DP_HOSTSET) == 0) {
585                                 hostset = defset;
586                         }
587                         if (sport >= IPPORT_RESERVED &&
588                             !(hostset & DP_NORESMNT)) {
589                                 syslog(LOG_NOTICE,
590                                     "Refused mount RPC from host %s port %d",
591                                     numerichost, sport);
592                                 svcerr_weakauth(transp);
593                                 goto out;
594                         }
595                         fhr.fhr_flag = hostset;
596                         fhr.fhr_vers = rqstp->rq_vers;
597                         /* Get the file handle */
598                         memset(&fhr.fhr_fh, 0, sizeof(fhr.fhr_fh)); /* for v2 */
599                         fh_size = sizeof(fhr.fhr_fh);
600                         error = 0;
601                         if (rump_sys_getfh(dpath, &fhr.fhr_fh, &fh_size) < 0) {
602                                 bad = error;
603                                 //syslog(LOG_ERR, "Can't get fh for %s %d %d", dpath, error, fh_size);
604                                 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
605                                     (char *)&bad))
606                                         syslog(LOG_ERR, "Can't send reply");
607                                 goto out;
608                         }
609                         if ((fhr.fhr_vers == 1 && fh_size > NFSX_V2FH) ||
610                             fh_size > NFSX_V3FHMAX) {
611                                 bad = EINVAL; /* XXX */
612                                 if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
613                                     (char *)&bad))
614                                         syslog(LOG_ERR, "Can't send reply");
615                                 goto out;
616                         }
617                         fhr.fhr_fhsize = fh_size;
618                         if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, (char *) &fhr))
619                                 syslog(LOG_ERR, "Can't send reply");
620                         if (!lookup_failed)
621                                 add_mlist(host, dpath, hostset);
622                         else
623                                 add_mlist(numerichost, dpath, hostset);
624                         if (debug)
625                                 (void)fprintf(stderr, "Mount successful.\n");
626                 } else {
627                         if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t) &bad))
628                                 syslog(LOG_ERR, "Can't send reply");
629                 }
630 out:
631                 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
632                 return;
633         case MOUNTPROC_DUMP:
634                 if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, NULL))
635                         syslog(LOG_ERR, "Can't send reply");
636                 return;
637         case MOUNTPROC_UMNT:
638                 if (!svc_getargs(transp, xdr_dir, dpath)) {
639                         svcerr_decode(transp);
640                         return;
641                 }
642                 if (!lookup_failed)
643                         ret = del_mlist(host, dpath, saddr);
644                 ret |= del_mlist(numerichost, dpath, saddr);
645                 if (ret) {
646                         svcerr_weakauth(transp);
647                         return;
648                 }
649                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
650                         syslog(LOG_ERR, "Can't send reply");
651                 return;
652         case MOUNTPROC_UMNTALL:
653                 if (!lookup_failed)
654                         ret = del_mlist(host, NULL, saddr);
655                 ret |= del_mlist(numerichost, NULL, saddr);
656                 if (ret) {
657                         svcerr_weakauth(transp);
658                         return;
659                 }
660                 if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
661                         syslog(LOG_ERR, "Can't send reply");
662                 return;
663         case MOUNTPROC_EXPORT:
664         case MOUNTPROC_EXPORTALL:
665                 if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, NULL))
666                         syslog(LOG_ERR, "Can't send reply");
667                 return;
668
669
670         default:
671                 svcerr_noproc(transp);
672                 return;
673         }
674 }
675
676 /*
677  * Xdr conversion for a dpath string
678  */
679 static int
680 xdr_dir(xdrsp, dirp)
681         XDR *xdrsp;
682         char *dirp;
683 {
684
685         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
686 }
687
688 /*
689  * Xdr routine to generate file handle reply
690  */
691 static int
692 xdr_fhs(xdrsp, cp)
693         XDR *xdrsp;
694         caddr_t cp;
695 {
696         struct fhreturn *fhrp = (struct fhreturn *) cp;
697         long ok = 0, len, auth;
698
699         if (!xdr_long(xdrsp, &ok))
700                 return (0);
701         switch (fhrp->fhr_vers) {
702         case 1:
703                 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
704         case 3:
705                 len = fhrp->fhr_fhsize;
706                 if (!xdr_long(xdrsp, &len))
707                         return (0);
708                 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
709                         return (0);
710                 if (fhrp->fhr_flag & DP_KERB)
711                         auth = RPCAUTH_KERB4;
712                 else
713                         auth = RPCAUTH_UNIX;
714                 len = 1;
715                 if (!xdr_long(xdrsp, &len))
716                         return (0);
717                 return (xdr_long(xdrsp, &auth));
718         };
719         return (0);
720 }
721
722 int
723 xdr_mlist(xdrsp, cp)
724         XDR *xdrsp;
725         caddr_t cp;
726 {
727         struct mountlist *mlp;
728         int trueval = 1;
729         int falseval = 0;
730         char *strp;
731
732         mlp = mlhead;
733         while (mlp) {
734                 if (!xdr_bool(xdrsp, &trueval))
735                         return (0);
736                 strp = &mlp->ml_host[0];
737                 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
738                         return (0);
739                 strp = &mlp->ml_dirp[0];
740                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
741                         return (0);
742                 mlp = mlp->ml_next;
743         }
744         if (!xdr_bool(xdrsp, &falseval))
745                 return (0);
746         return (1);
747 }
748
749 /*
750  * Xdr conversion for export list
751  */
752 int
753 xdr_explist(xdrsp, cp)
754         XDR *xdrsp;
755         caddr_t cp;
756 {
757         struct exportlist *ep;
758         int falseval = 0;
759         int putdef;
760         sigset_t sighup_mask;
761
762         (void)sigemptyset(&sighup_mask);
763         (void)sigaddset(&sighup_mask, SIGHUP);
764         (void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
765         ep = exphead;
766         while (ep) {
767                 putdef = 0;
768                 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
769                         goto errout;
770                 if (ep->ex_defdir && putdef == 0 &&
771                     put_exlist(ep->ex_defdir, xdrsp, NULL, &putdef))
772                         goto errout;
773                 ep = ep->ex_next;
774         }
775         (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
776         if (!xdr_bool(xdrsp, &falseval))
777                 return (0);
778         return (1);
779 errout:
780         (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
781         return (0);
782 }
783
784 /*
785  * Called from xdr_explist() to traverse the tree and export the
786  * directory paths.  Assumes SIGHUP has already been masked.
787  */
788 int
789 put_exlist(dp, xdrsp, adp, putdefp)
790         struct dirlist *dp;
791         XDR *xdrsp;
792         struct dirlist *adp;
793         int *putdefp;
794 {
795         struct grouplist *grp;
796         struct hostlist *hp;
797         int trueval = 1;
798         int falseval = 0;
799         int gotalldir = 0;
800         char *strp;
801
802         if (dp) {
803                 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
804                         return (1);
805                 if (!xdr_bool(xdrsp, &trueval))
806                         return (1);
807                 strp = dp->dp_dirp;
808                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
809                         return (1);
810                 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
811                         gotalldir = 1;
812                         *putdefp = 1;
813                 }
814                 if ((dp->dp_flag & DP_DEFSET) == 0 &&
815                     (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
816                         hp = dp->dp_hosts;
817                         while (hp) {
818                                 grp = hp->ht_grp;
819                                 if (grp->gr_type == GT_HOST) {
820                                         if (!xdr_bool(xdrsp, &trueval))
821                                                 return (1);
822                                         strp =
823                                           grp->gr_ptr.gt_addrinfo->ai_canonname;
824                                         if (!xdr_string(xdrsp, &strp,
825                                                         RPCMNT_NAMELEN))
826                                                 return (1);
827                                 } else if (grp->gr_type == GT_NET) {
828                                         if (!xdr_bool(xdrsp, &trueval))
829                                                 return (1);
830                                         strp = grp->gr_ptr.gt_net.nt_name;
831                                         if (!xdr_string(xdrsp, &strp,
832                                                         RPCMNT_NAMELEN))
833                                                 return (1);
834                                 }
835                                 hp = hp->ht_next;
836                                 if (gotalldir && hp == NULL) {
837                                         hp = adp->dp_hosts;
838                                         gotalldir = 0;
839                                 }
840                         }
841                 }
842                 if (!xdr_bool(xdrsp, &falseval))
843                         return (1);
844                 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
845                         return (1);
846         }
847         return (0);
848 }
849
850 static int
851 parse_host_netgroup(line, lineno, ep, tgrp, cp, has_host, grp)
852         const char *line;
853         size_t lineno;
854         struct exportlist *ep;
855         struct grouplist *tgrp;
856         char *cp;
857         int *has_host;
858         struct grouplist **grp;
859 {
860         const char *hst, *usr, *dom;
861         int netgrp;
862
863         if (ep == NULL) {
864                 syslog(LOG_ERR, "\"%s\", line %ld: No current export",
865                     line, (unsigned long)lineno);
866                 return 0;
867         }
868         setnetgrent(cp);
869         netgrp = getnetgrent(&hst, &usr, &dom);
870         do {
871                 if (*has_host) {
872                         (*grp)->gr_next = get_grp();
873                         *grp = (*grp)->gr_next;
874                 }
875                 if (netgrp) {
876                         if (hst == NULL) {
877                                 syslog(LOG_ERR,
878                                     "\"%s\", line %ld: No host in netgroup %s",
879                                     line, (unsigned long)lineno, cp);
880                                 goto bad;
881                         }
882                         if (get_host(line, lineno, hst, *grp))
883                                 goto bad;
884                 } else if (get_host(line, lineno, cp, *grp))
885                         goto bad;
886                 *has_host = TRUE;
887         } while (netgrp && getnetgrent(&hst, &usr, &dom));
888
889         endnetgrent();
890         return 1;
891 bad:
892         endnetgrent();
893         return 0;
894         
895 }
896
897 static int
898 parse_directory(line, lineno, tgrp, got_nondir, cp, ep, fsp)
899         const char *line;
900         size_t lineno;
901         struct grouplist *tgrp;
902         int got_nondir;
903         char *cp;
904         struct exportlist **ep;
905         struct statvfs *fsp;
906 {
907         int error = 0;
908
909         if (!check_dirpath(line, lineno, cp))
910                 return 0;
911
912         if (rump_sys_statvfs1(cp, fsp, ST_WAIT) == -1) {
913                 syslog(LOG_ERR, "\"%s\", line %ld: statvfs for `%s' failed: %m %d",
914                     line, (unsigned long)lineno, cp, error);
915                 return 0;
916         }
917
918         if (got_nondir) {
919                 syslog(LOG_ERR,
920                     "\"%s\", line %ld: Directories must precede files",
921                     line, (unsigned long)lineno);
922                 return 0;
923         }
924         if (*ep) {
925                 if ((*ep)->ex_fs.__fsid_val[0] != fsp->f_fsidx.__fsid_val[0] ||
926                     (*ep)->ex_fs.__fsid_val[1] != fsp->f_fsidx.__fsid_val[1]) {
927                         syslog(LOG_ERR,
928                             "\"%s\", line %ld: filesystem ids disagree",
929                             line, (unsigned long)lineno);
930                         return 0;
931                 }
932         } else {
933                 /*
934                  * See if this directory is already
935                  * in the list.
936                  */
937                 *ep = ex_search(&fsp->f_fsidx);
938                 if (*ep == NULL) {
939                         *ep = get_exp();
940                         (*ep)->ex_fs = fsp->f_fsidx;
941                         (*ep)->ex_fsdir = estrdup(fsp->f_mntonname);
942                         if (debug)
943                                 (void)fprintf(stderr,
944                                     "Making new ep fs=0x%x,0x%x\n",
945                                     fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
946                 } else {
947                         if (debug)
948                                 (void)fprintf(stderr,
949                                     "Found ep fs=0x%x,0x%x\n",
950                                     fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
951                 }
952         }
953
954         return 1;
955 }
956
957
958 /*
959  * Get the export list
960  */
961 /* ARGSUSED */
962 void
963 get_exportlist(n)
964         int n;
965 {
966         struct exportlist *ep, *ep2;
967         struct grouplist *grp, *tgrp;
968         struct exportlist **epp;
969         struct dirlist *dirhead;
970         struct statvfs fsb, *fsp;
971         struct addrinfo *ai;
972         struct uucred anon;
973         char *cp, *endcp, *dirp, savedc;
974         int has_host, exflags, got_nondir, dirplen, num, i;
975         FILE *exp_file;
976         char *line;
977         size_t lineno = 0, len;
978
979
980         /*
981          * First, get rid of the old list
982          */
983         ep = exphead;
984         while (ep) {
985                 ep2 = ep;
986                 ep = ep->ex_next;
987                 free_exp(ep2);
988         }
989         exphead = NULL;
990
991         dirp = NULL;
992         dirplen = 0;
993         grp = grphead;
994         while (grp) {
995                 tgrp = grp;
996                 grp = grp->gr_next;
997                 free_grp(tgrp);
998         }
999         grphead = NULL;
1000
1001         /*
1002          * And delete exports that are in the kernel for all local
1003          * file systems.
1004          */
1005         num = getmntinfo(&fsp, MNT_NOWAIT);
1006         for (i = 0; i < num; i++) {
1007                 struct mountd_exports_list mel;
1008
1009                 /* Delete all entries from the export list. */
1010                 mel.mel_path = fsp->f_mntonname;
1011                 mel.mel_nexports = 0;
1012                 if (rump_sys_nfssvc(NFSSVC_SETEXPORTSLIST, &mel) == -1 &&
1013                     errno != EOPNOTSUPP)
1014                         syslog(LOG_ERR, "Can't delete exports for %s (%m)",
1015                             fsp->f_mntonname);
1016
1017                 fsp++;
1018         }
1019
1020         /*
1021          * Read in the exports file and build the list, calling
1022          * mount() as we go along to push the export rules into the kernel.
1023          */
1024         exname = _PATH_EXPORTS;
1025         if ((exp_file = rumpfopen(exname, "r")) == NULL) {
1026                 /*
1027                  * Don't exit here; we can still reload the config
1028                  * after a SIGHUP.
1029                  */
1030                 if (debug)
1031                         (void)fprintf(stderr, "Can't open %s: %s\n", exname,
1032                             strerror(errno));
1033                 return;
1034         }
1035         dirhead = NULL;
1036         while ((line = fparseln(exp_file, &len, &lineno, NULL, 0)) != NULL) {
1037                 if (debug)
1038                         (void)fprintf(stderr, "Got line %s\n", line);
1039                 cp = line;
1040                 nextfield(&cp, &endcp);
1041                 if (cp == endcp)
1042                         goto nextline;  /* skip empty line */
1043                 /*
1044                  * Set defaults.
1045                  */
1046                 has_host = FALSE;
1047                 anon = def_anon;
1048                 exflags = MNT_EXPORTED;
1049                 got_nondir = 0;
1050                 opt_flags = 0;
1051                 ep = NULL;
1052
1053                 if (noprivports) {
1054                         opt_flags |= OP_NORESMNT | OP_NORESPORT;
1055                         exflags |= MNT_EXNORESPORT;
1056                 }
1057
1058                 /*
1059                  * Create new exports list entry
1060                  */
1061                 len = endcp - cp;
1062                 tgrp = grp = get_grp();
1063                 while (len > 0) {
1064                         if (len > RPCMNT_NAMELEN) {
1065                                 *endcp = '\0';
1066                                 syslog(LOG_ERR,
1067                                     "\"%s\", line %ld: name `%s' is too long",
1068                                     line, (unsigned long)lineno, cp);
1069                                 goto badline;
1070                         }
1071                         switch (*cp) {
1072                         case '-':
1073                                 /*
1074                                  * Option
1075                                  */
1076                                 if (ep == NULL) {
1077                                         syslog(LOG_ERR,
1078                                 "\"%s\", line %ld: No current export list",
1079                                             line, (unsigned long)lineno);
1080                                         goto badline;
1081                                 }
1082                                 if (debug)
1083                                         (void)fprintf(stderr, "doing opt %s\n",
1084                                             cp);
1085                                 got_nondir = 1;
1086                                 if (do_opt(line, lineno, &cp, &endcp, ep, grp,
1087                                     &has_host, &exflags, &anon))
1088                                         goto badline;
1089                                 break;
1090
1091                         case '/':
1092                                 /*
1093                                  * Directory
1094                                  */
1095                                 savedc = *endcp;
1096                                 *endcp = '\0';
1097
1098                                 if (!parse_directory(line, lineno, tgrp,
1099                                     got_nondir, cp, &ep, &fsb))
1100                                         goto badline;
1101                                 /*
1102                                  * Add dirpath to export mount point.
1103                                  */
1104                                 dirp = add_expdir(&dirhead, cp, len);
1105                                 dirplen = len;
1106
1107                                 *endcp = savedc;
1108                                 break;
1109
1110                         default:
1111                                 /*
1112                                  * Host or netgroup.
1113                                  */
1114                                 savedc = *endcp;
1115                                 *endcp = '\0';
1116
1117                                 if (!parse_host_netgroup(line, lineno, ep,
1118                                     tgrp, cp, &has_host, &grp))
1119                                         goto badline;
1120
1121                                 got_nondir = 1;
1122
1123                                 *endcp = savedc;
1124                                 break;
1125                         }
1126
1127                         cp = endcp;
1128                         nextfield(&cp, &endcp);
1129                         len = endcp - cp;
1130                 }
1131                 if (check_options(line, lineno, dirhead))
1132                         goto badline;
1133
1134                 if (!has_host) {
1135                         grp->gr_type = GT_HOST;
1136                         if (debug)
1137                                 (void)fprintf(stderr,
1138                                     "Adding a default entry\n");
1139                         /* add a default group and make the grp list NULL */
1140                         ai = emalloc(sizeof(struct addrinfo));
1141                         ai->ai_flags = 0;
1142                         ai->ai_family = AF_INET;        /* XXXX */
1143                         ai->ai_socktype = SOCK_DGRAM;
1144                         /* setting the length to 0 will match anything */
1145                         ai->ai_addrlen = 0;
1146                         ai->ai_flags = AI_CANONNAME;
1147                         ai->ai_canonname = estrdup("Default");
1148                         ai->ai_addr = NULL;
1149                         ai->ai_next = NULL;
1150                         grp->gr_ptr.gt_addrinfo = ai;
1151
1152                 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1153                         /*
1154                          * Don't allow a network export coincide with a list of
1155                          * host(s) on the same line.
1156                          */
1157                         syslog(LOG_ERR,
1158                             "\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed",
1159                             line, (unsigned long)lineno);
1160                         goto badline;
1161                 }
1162                 /*
1163                  * Loop through hosts, pushing the exports into the kernel.
1164                  * After loop, tgrp points to the start of the list and
1165                  * grp points to the last entry in the list.
1166                  */
1167                 grp = tgrp;
1168                 do {
1169                         if (do_nfssvc(line, lineno, ep, grp, exflags, &anon,
1170                             dirp, dirplen, &fsb))
1171                                 goto badline;
1172                 } while (grp->gr_next && (grp = grp->gr_next));
1173
1174                 /*
1175                  * Success. Update the data structures.
1176                  */
1177                 if (has_host) {
1178                         hang_dirp(dirhead, tgrp, ep, opt_flags);
1179                         grp->gr_next = grphead;
1180                         grphead = tgrp;
1181                 } else {
1182                         hang_dirp(dirhead, NULL, ep, opt_flags);
1183                         free_grp(tgrp);
1184                 }
1185                 tgrp = NULL;
1186                 dirhead = NULL;
1187                 if ((ep->ex_flag & EX_LINKED) == 0) {
1188                         ep2 = exphead;
1189                         epp = &exphead;
1190
1191                         /*
1192                          * Insert in the list in alphabetical order.
1193                          */
1194                         while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1195                                 epp = &ep2->ex_next;
1196                                 ep2 = ep2->ex_next;
1197                         }
1198                         if (ep2)
1199                                 ep->ex_next = ep2;
1200                         *epp = ep;
1201                         ep->ex_flag |= EX_LINKED;
1202                 }
1203                 goto nextline;
1204 badline:
1205                 free_exp_grp(ep, grp);
1206 nextline:
1207                 if (dirhead) {
1208                         free_dir(dirhead);
1209                         dirhead = NULL;
1210                 }
1211                 free(line);
1212         }
1213         (void)fclose(exp_file);
1214 }
1215
1216 /*
1217  * Allocate an export list element
1218  */
1219 static struct exportlist *
1220 get_exp()
1221 {
1222         struct exportlist *ep;
1223
1224         ep = emalloc(sizeof(struct exportlist));
1225         (void)memset(ep, 0, sizeof(struct exportlist));
1226         return (ep);
1227 }
1228
1229 /*
1230  * Allocate a group list element
1231  */
1232 static struct grouplist *
1233 get_grp()
1234 {
1235         struct grouplist *gp;
1236
1237         gp = emalloc(sizeof(struct grouplist));
1238         (void)memset(gp, 0, sizeof(struct grouplist));
1239         return (gp);
1240 }
1241
1242 /*
1243  * Clean up upon an error in get_exportlist().
1244  */
1245 static void
1246 free_exp_grp(ep, grp)
1247         struct exportlist *ep;
1248         struct grouplist *grp;
1249 {
1250         struct grouplist *tgrp;
1251
1252         if (ep && (ep->ex_flag & EX_LINKED) == 0)
1253                 free_exp(ep);
1254         while (grp) {
1255                 tgrp = grp;
1256                 grp = grp->gr_next;
1257                 free_grp(tgrp);
1258         }
1259 }
1260
1261 /*
1262  * Search the export list for a matching fs.
1263  */
1264 static struct exportlist *
1265 ex_search(fsid)
1266         fsid_t *fsid;
1267 {
1268         struct exportlist *ep;
1269
1270         ep = exphead;
1271         return ep;
1272         while (ep) {
1273                 if (ep->ex_fs.__fsid_val[0] == fsid->__fsid_val[0] &&
1274                     ep->ex_fs.__fsid_val[1] == fsid->__fsid_val[1])
1275                         return (ep);
1276                 ep = ep->ex_next;
1277         }
1278         return (ep);
1279 }
1280
1281 /*
1282  * Add a directory path to the list.
1283  */
1284 static char *
1285 add_expdir(dpp, cp, len)
1286         struct dirlist **dpp;
1287         char *cp;
1288         int len;
1289 {
1290         struct dirlist *dp;
1291
1292         dp = emalloc(sizeof(struct dirlist) + len);
1293         dp->dp_left = *dpp;
1294         dp->dp_right = NULL;
1295         dp->dp_flag = 0;
1296         dp->dp_hosts = NULL;
1297         (void)strcpy(dp->dp_dirp, cp);
1298         *dpp = dp;
1299         return (dp->dp_dirp);
1300 }
1301
1302 /*
1303  * Hang the dir list element off the dirpath binary tree as required
1304  * and update the entry for host.
1305  */
1306 void
1307 hang_dirp(dp, grp, ep, flags)
1308         struct dirlist *dp;
1309         struct grouplist *grp;
1310         struct exportlist *ep;
1311         int flags;
1312 {
1313         struct hostlist *hp;
1314         struct dirlist *dp2;
1315
1316         if (flags & OP_ALLDIRS) {
1317                 if (ep->ex_defdir)
1318                         free(dp);
1319                 else
1320                         ep->ex_defdir = dp;
1321                 if (grp == NULL) {
1322                         ep->ex_defdir->dp_flag |= DP_DEFSET;
1323                         if (flags & OP_KERB)
1324                                 ep->ex_defdir->dp_flag |= DP_KERB;
1325                         if (flags & OP_NORESMNT)
1326                                 ep->ex_defdir->dp_flag |= DP_NORESMNT;
1327                 } else
1328                         while (grp) {
1329                                 hp = get_ht();
1330                                 if (flags & OP_KERB)
1331                                         hp->ht_flag |= DP_KERB;
1332                                 if (flags & OP_NORESMNT)
1333                                         hp->ht_flag |= DP_NORESMNT;
1334                                 hp->ht_grp = grp;
1335                                 hp->ht_next = ep->ex_defdir->dp_hosts;
1336                                 ep->ex_defdir->dp_hosts = hp;
1337                                 grp = grp->gr_next;
1338                         }
1339         } else {
1340
1341                 /*
1342                  * Loop through the directories adding them to the tree.
1343                  */
1344                 while (dp) {
1345                         dp2 = dp->dp_left;
1346                         add_dlist(&ep->ex_dirl, dp, grp, flags);
1347                         dp = dp2;
1348                 }
1349         }
1350 }
1351
1352 /*
1353  * Traverse the binary tree either updating a node that is already there
1354  * for the new directory or adding the new node.
1355  */
1356 static void
1357 add_dlist(dpp, newdp, grp, flags)
1358         struct dirlist **dpp;
1359         struct dirlist *newdp;
1360         struct grouplist *grp;
1361         int flags;
1362 {
1363         struct dirlist *dp;
1364         struct hostlist *hp;
1365         int cmp;
1366
1367         dp = *dpp;
1368         if (dp) {
1369                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1370                 if (cmp > 0) {
1371                         add_dlist(&dp->dp_left, newdp, grp, flags);
1372                         return;
1373                 } else if (cmp < 0) {
1374                         add_dlist(&dp->dp_right, newdp, grp, flags);
1375                         return;
1376                 } else
1377                         free(newdp);
1378         } else {
1379                 dp = newdp;
1380                 dp->dp_left = NULL;
1381                 *dpp = dp;
1382         }
1383         if (grp) {
1384
1385                 /*
1386                  * Hang all of the host(s) off of the directory point.
1387                  */
1388                 do {
1389                         hp = get_ht();
1390                         if (flags & OP_KERB)
1391                                 hp->ht_flag |= DP_KERB;
1392                         if (flags & OP_NORESMNT)
1393                                 hp->ht_flag |= DP_NORESMNT;
1394                         hp->ht_grp = grp;
1395                         hp->ht_next = dp->dp_hosts;
1396                         dp->dp_hosts = hp;
1397                         grp = grp->gr_next;
1398                 } while (grp);
1399         } else {
1400                 dp->dp_flag |= DP_DEFSET;
1401                 if (flags & OP_KERB)
1402                         dp->dp_flag |= DP_KERB;
1403                 if (flags & OP_NORESMNT)
1404                         dp->dp_flag |= DP_NORESMNT;
1405         }
1406 }
1407
1408 /*
1409  * Search for a dirpath on the export point.
1410  */
1411 static struct dirlist *
1412 dirp_search(dp, dirp)
1413         struct dirlist *dp;
1414         char *dirp;
1415 {
1416         int cmp;
1417
1418         if (dp) {
1419                 cmp = strcmp(dp->dp_dirp, dirp);
1420                 if (cmp > 0)
1421                         return (dirp_search(dp->dp_left, dirp));
1422                 else if (cmp < 0)
1423                         return (dirp_search(dp->dp_right, dirp));
1424                 else
1425                         return (dp);
1426         }
1427         return (dp);
1428 }
1429
1430 /*
1431  * Some helper functions for netmasks. They all assume masks in network
1432  * order (big endian).
1433  */
1434 static int
1435 bitcmp(void *dst, void *src, int bitlen)
1436 {
1437         int i;
1438         u_int8_t *p1 = dst, *p2 = src;
1439         u_int8_t bitmask;
1440         int bytelen, bitsleft;
1441
1442         bytelen = bitlen / 8;
1443         bitsleft = bitlen % 8;
1444
1445         if (debug) {
1446                 printf("comparing:\n");
1447                 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
1448                         printf("%02x", p1[i]);
1449                 printf("\n");
1450                 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
1451                         printf("%02x", p2[i]);
1452                 printf("\n");
1453         }
1454
1455         for (i = 0; i < bytelen; i++) {
1456                 if (*p1 != *p2)
1457                         return 1;
1458                 p1++;
1459                 p2++;
1460         }
1461
1462         for (i = 0; i < bitsleft; i++) {
1463                 bitmask = 1 << (7 - i);
1464                 if ((*p1 & bitmask) != (*p2 & bitmask))
1465                         return 1;
1466         }
1467
1468         return 0;
1469 }
1470
1471 static int
1472 netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen)
1473 {
1474         void *src, *dst;
1475
1476         if (s1->sa_family != s2->sa_family)
1477                 return 1;
1478
1479         switch (s1->sa_family) {
1480         case AF_INET:
1481                 src = &((struct sockaddr_in *)s1)->sin_addr;
1482                 dst = &((struct sockaddr_in *)s2)->sin_addr;
1483                 if (bitlen > sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8)
1484                         return 1;
1485                 break;
1486         case AF_INET6:
1487                 src = &((struct sockaddr_in6 *)s1)->sin6_addr;
1488                 dst = &((struct sockaddr_in6 *)s2)->sin6_addr;
1489                 if (((struct sockaddr_in6 *)s1)->sin6_scope_id !=
1490                     ((struct sockaddr_in6 *)s2)->sin6_scope_id)
1491                         return 1;
1492                 if (bitlen > sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8)
1493                         return 1;
1494                 break;
1495         default:
1496                 return 1;
1497         }
1498
1499         return bitcmp(src, dst, bitlen);
1500 }
1501
1502 static int
1503 allones(struct sockaddr_storage *ssp, int bitlen)
1504 {
1505         u_int8_t *p;
1506         int bytelen, bitsleft, i;
1507         int zerolen;
1508
1509         switch (ssp->ss_family) {
1510         case AF_INET:
1511                 p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr;
1512                 zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr);
1513                 break;
1514         case AF_INET6:
1515                 p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr;
1516                 zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr);
1517                 break;
1518         default:
1519                 return -1;
1520         }
1521
1522         memset(p, 0, zerolen);
1523
1524         bytelen = bitlen / 8;
1525         bitsleft = bitlen % 8;
1526
1527         if (bytelen > zerolen)
1528                 return -1;
1529
1530         for (i = 0; i < bytelen; i++)
1531                 *p++ = 0xff;
1532
1533         for (i = 0; i < bitsleft; i++)
1534                 *p |= 1 << (7 - i);
1535         
1536         return 0;
1537 }
1538
1539 static int
1540 countones(struct sockaddr *sa)
1541 {
1542         void *mask;
1543         int i, bits = 0, bytelen;
1544         u_int8_t *p;
1545
1546         switch (sa->sa_family) {
1547         case AF_INET:
1548                 mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
1549                 bytelen = 4;
1550                 break;
1551         case AF_INET6:
1552                 mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr;
1553                 bytelen = 16;
1554                 break;
1555         default:
1556                 return 0;
1557         }
1558
1559         p = mask;
1560
1561         for (i = 0; i < bytelen; i++, p++) {
1562                 if (*p != 0xff) {
1563                         for (bits = 0; bits < 8; bits++) {
1564                                 if (!(*p & (1 << (7 - bits))))
1565                                         break;
1566                         }
1567                         break;
1568                 }
1569         }
1570
1571         return (i * 8 + bits);
1572 }
1573
1574 static int
1575 sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
1576 {
1577         void *p1, *p2;
1578         int len;
1579
1580         if (sa1->sa_family != sa2->sa_family)
1581                 return 1;
1582
1583         switch (sa1->sa_family) {
1584         case AF_INET:
1585                 p1 = &((struct sockaddr_in *)sa1)->sin_addr;
1586                 p2 = &((struct sockaddr_in *)sa2)->sin_addr;
1587                 len = 4;
1588                 break;
1589         case AF_INET6:
1590                 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
1591                 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
1592                 len = 16;
1593                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
1594                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
1595                         return 1;
1596                 break;
1597         default:
1598                 return 1;
1599         }
1600
1601         return memcmp(p1, p2, len);
1602 }
1603
1604 /*
1605  * Scan for a host match in a directory tree.
1606  */
1607 static int
1608 chk_host(dp, saddr, defsetp, hostsetp)
1609         struct dirlist *dp;
1610         struct sockaddr *saddr;
1611         int *defsetp;
1612         int *hostsetp;
1613 {
1614         struct hostlist *hp;
1615         struct grouplist *grp;
1616         struct addrinfo *ai;
1617
1618         if (dp) {
1619                 if (dp->dp_flag & DP_DEFSET)
1620                         *defsetp = dp->dp_flag;
1621                 hp = dp->dp_hosts;
1622                 while (hp) {
1623                         grp = hp->ht_grp;
1624                         switch (grp->gr_type) {
1625                         case GT_HOST:
1626                                 ai = grp->gr_ptr.gt_addrinfo;
1627                                 for (; ai; ai = ai->ai_next) {
1628                                         if (!sacmp(ai->ai_addr, saddr)) {
1629                                                 *hostsetp =
1630                                                     (hp->ht_flag | DP_HOSTSET);
1631                                                 return (1);
1632                                         }
1633                                 }
1634                                 break;
1635                         case GT_NET:
1636                                 if (!netpartcmp(saddr,
1637                                     (struct sockaddr *)
1638                                         &grp->gr_ptr.gt_net.nt_net,
1639                                     grp->gr_ptr.gt_net.nt_len)) {
1640                                         *hostsetp = (hp->ht_flag | DP_HOSTSET);
1641                                         return (1);
1642                                 }
1643                                 break;
1644                         };
1645                         hp = hp->ht_next;
1646                 }
1647         }
1648         return (0);
1649 }
1650
1651 /*
1652  * Scan tree for a host that matches the address.
1653  */
1654 static int
1655 scan_tree(dp, saddr)
1656         struct dirlist *dp;
1657         struct sockaddr *saddr;
1658 {
1659         int defset, hostset;
1660
1661         if (dp) {
1662                 if (scan_tree(dp->dp_left, saddr))
1663                         return (1);
1664                 if (chk_host(dp, saddr, &defset, &hostset))
1665                         return (1);
1666                 if (scan_tree(dp->dp_right, saddr))
1667                         return (1);
1668         }
1669         return (0);
1670 }
1671
1672 /*
1673  * Traverse the dirlist tree and free it up.
1674  */
1675 static void
1676 free_dir(dp)
1677         struct dirlist *dp;
1678 {
1679
1680         if (dp) {
1681                 free_dir(dp->dp_left);
1682                 free_dir(dp->dp_right);
1683                 free_host(dp->dp_hosts);
1684                 free(dp);
1685         }
1686 }
1687
1688 /*
1689  * Parse the option string and update fields.
1690  * Option arguments may either be -<option>=<value> or
1691  * -<option> <value>
1692  */
1693 static int
1694 do_opt(line, lineno, cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1695         const char *line;
1696         size_t lineno;
1697         char **cpp, **endcpp;
1698         struct exportlist *ep;
1699         struct grouplist *grp;
1700         int *has_hostp;
1701         int *exflagsp;
1702         struct uucred *cr;
1703 {
1704         char *cpoptarg, *cpoptend;
1705         char *cp, *cpopt, savedc, savedc2;
1706         char *endcp = NULL;     /* XXX: GCC */
1707         int allflag, usedarg;
1708
1709         cpopt = *cpp;
1710         cpopt++;
1711         cp = *endcpp;
1712         savedc = *cp;
1713         *cp = '\0';
1714         while (cpopt && *cpopt) {
1715                 allflag = 1;
1716                 usedarg = -2;
1717                 savedc2 = '\0';
1718                 if ((cpoptend = strchr(cpopt, ',')) != NULL) {
1719                         *cpoptend++ = '\0';
1720                         if ((cpoptarg = strchr(cpopt, '=')) != NULL)
1721                                 *cpoptarg++ = '\0';
1722                 } else {
1723                         if ((cpoptarg = strchr(cpopt, '=')) != NULL)
1724                                 *cpoptarg++ = '\0';
1725                         else {
1726                                 *cp = savedc;
1727                                 nextfield(&cp, &endcp);
1728                                 **endcpp = '\0';
1729                                 if (endcp > cp && *cp != '-') {
1730                                         cpoptarg = cp;
1731                                         savedc2 = *endcp;
1732                                         *endcp = '\0';
1733                                         usedarg = 0;
1734                                 }
1735                         }
1736                 }
1737                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1738                         *exflagsp |= MNT_EXRDONLY;
1739                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1740                     !(allflag = strcmp(cpopt, "mapall")) ||
1741                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1742                         usedarg++;
1743                         parsecred(cpoptarg, cr);
1744                         if (allflag == 0) {
1745                                 *exflagsp |= MNT_EXPORTANON;
1746                                 opt_flags |= OP_MAPALL;
1747                         } else
1748                                 opt_flags |= OP_MAPROOT;
1749                 } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1750                         *exflagsp |= MNT_EXKERB;
1751                         opt_flags |= OP_KERB;
1752                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1753                     !strcmp(cpopt, "m"))) {
1754                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1755                                 syslog(LOG_ERR,
1756                                     "\"%s\", line %ld: Bad mask: %s",
1757                                     line, (unsigned long)lineno, cpoptarg);
1758                                 return (1);
1759                         }
1760                         usedarg++;
1761                         opt_flags |= OP_MASK;
1762                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1763                     !strcmp(cpopt, "n"))) {
1764                         if (strchr(cpoptarg, '/') != NULL) {
1765                                 if (debug)
1766                                         fprintf(stderr, "setting OP_MASKLEN\n");
1767                                 opt_flags |= OP_MASKLEN;
1768                         }
1769                         if (grp->gr_type != GT_NULL) {
1770                                 syslog(LOG_ERR,
1771                                     "\"%s\", line %ld: Network/host conflict",
1772                                     line, (unsigned long)lineno);
1773                                 return (1);
1774                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1775                                 syslog(LOG_ERR,
1776                                     "\"%s\", line %ld: Bad net: %s",
1777                                     line, (unsigned long)lineno, cpoptarg);
1778                                 return (1);
1779                         }
1780                         grp->gr_type = GT_NET;
1781                         *has_hostp = 1;
1782                         usedarg++;
1783                         opt_flags |= OP_NET;
1784                 } else if (!strcmp(cpopt, "alldirs")) {
1785                         opt_flags |= OP_ALLDIRS;
1786                 } else if (!strcmp(cpopt, "noresvmnt")) {
1787                         opt_flags |= OP_NORESMNT;
1788                 } else if (!strcmp(cpopt, "noresvport")) {
1789                         opt_flags |= OP_NORESPORT;
1790                         *exflagsp |= MNT_EXNORESPORT;
1791                 } else if (!strcmp(cpopt, "public")) {
1792                         *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC);
1793                         opt_flags |= OP_NORESPORT;
1794                 } else if (!strcmp(cpopt, "webnfs")) {
1795                         *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC |
1796                             MNT_EXRDONLY | MNT_EXPORTANON);
1797                         opt_flags |= (OP_MAPALL | OP_NORESPORT);
1798                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1799                         ep->ex_indexfile = strdup(cpoptarg);
1800                 } else {
1801                         syslog(LOG_ERR, 
1802                             "\"%s\", line %ld: Bad opt %s",
1803                             line, (unsigned long)lineno, cpopt);
1804                         return (1);
1805                 }
1806                 if (usedarg >= 0) {
1807                         *endcp = savedc2;
1808                         **endcpp = savedc;
1809                         if (usedarg > 0) {
1810                                 *cpp = cp;
1811                                 *endcpp = endcp;
1812                         }
1813                         return (0);
1814                 }
1815                 cpopt = cpoptend;
1816         }
1817         **endcpp = savedc;
1818         return (0);
1819 }
1820
1821 /*
1822  * Translate a character string to the corresponding list of network
1823  * addresses for a hostname.
1824  */
1825 static int
1826 get_host(line, lineno, cp, grp)
1827         const char *line;
1828         size_t lineno;
1829         const char *cp;
1830         struct grouplist *grp;
1831 {
1832         struct addrinfo *ai, hints;
1833         int ecode;
1834         char host[NI_MAXHOST];
1835
1836         if (grp->gr_type != GT_NULL) {
1837                 syslog(LOG_ERR,
1838                     "\"%s\", line %ld: Bad netgroup type for ip host %s",
1839                     line, (unsigned long)lineno, cp);
1840                 return (1);
1841         }
1842         memset(&hints, 0, sizeof hints);
1843         hints.ai_flags = AI_CANONNAME;
1844         hints.ai_protocol = IPPROTO_UDP;
1845         ecode = getaddrinfo(cp, NULL, &hints, &ai);
1846         if (ecode != 0) {
1847                 syslog(LOG_ERR, "\"%s\", line %ld: can't get address info for "
1848                                 "host %s",
1849                     line, (long)lineno, cp);
1850                 return 1;
1851         }
1852         grp->gr_type = GT_HOST;
1853         grp->gr_ptr.gt_addrinfo = ai;
1854         while (ai != NULL) {
1855                 if (ai->ai_canonname == NULL) {
1856                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1857                             sizeof host, NULL, 0, ninumeric) != 0)
1858                                 strlcpy(host, "?", sizeof(host));
1859                         ai->ai_canonname = estrdup(host);
1860                         ai->ai_flags |= AI_CANONNAME;
1861                 } else
1862                         ai->ai_flags &= ~AI_CANONNAME;
1863                 if (debug)
1864                         (void)fprintf(stderr, "got host %s\n", ai->ai_canonname);
1865                 ai = ai->ai_next;
1866         }
1867         return (0);
1868 }
1869
1870 /*
1871  * Free up an exports list component
1872  */
1873 static void
1874 free_exp(ep)
1875         struct exportlist *ep;
1876 {
1877
1878         if (ep->ex_defdir) {
1879                 free_host(ep->ex_defdir->dp_hosts);
1880                 free(ep->ex_defdir);
1881         }
1882         if (ep->ex_fsdir)
1883                 free(ep->ex_fsdir);
1884         if (ep->ex_indexfile)
1885                 free(ep->ex_indexfile);
1886         free_dir(ep->ex_dirl);
1887         free(ep);
1888 }
1889
1890 /*
1891  * Free hosts.
1892  */
1893 static void
1894 free_host(hp)
1895         struct hostlist *hp;
1896 {
1897         struct hostlist *hp2;
1898
1899         while (hp) {
1900                 hp2 = hp;
1901                 hp = hp->ht_next;
1902                 free(hp2);
1903         }
1904 }
1905
1906 static struct hostlist *
1907 get_ht()
1908 {
1909         struct hostlist *hp;
1910
1911         hp = emalloc(sizeof(struct hostlist));
1912         hp->ht_next = NULL;
1913         hp->ht_flag = 0;
1914         return (hp);
1915 }
1916
1917 /*
1918  * Do the nfssvc syscall to push the export info into the kernel.
1919  */
1920 static int
1921 do_nfssvc(line, lineno, ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1922         const char *line;
1923         size_t lineno;
1924         struct exportlist *ep;
1925         struct grouplist *grp;
1926         int exflags;
1927         struct uucred *anoncrp;
1928         char *dirp;
1929         int dirplen;
1930         struct statvfs *fsb;
1931 {
1932         struct sockaddr *addrp;
1933         struct sockaddr_storage ss;
1934         struct addrinfo *ai;
1935         int addrlen;
1936         int done;
1937         struct export_args export;
1938
1939         export.ex_flags = exflags;
1940         export.ex_anon = *anoncrp;
1941         export.ex_indexfile = ep->ex_indexfile;
1942         if (grp->gr_type == GT_HOST) {
1943                 ai = grp->gr_ptr.gt_addrinfo;
1944                 addrp = ai->ai_addr;
1945                 addrlen = ai->ai_addrlen;
1946         } else {
1947                 addrp = NULL;
1948                 ai = NULL;      /* XXXGCC -Wuninitialized */
1949                 addrlen = 0;    /* XXXGCC -Wuninitialized */
1950         }
1951         done = FALSE;
1952         while (!done) {
1953                 struct mountd_exports_list mel;
1954
1955                 switch (grp->gr_type) {
1956                 case GT_HOST:
1957                         if (addrp != NULL && addrp->sa_family == AF_INET6 &&
1958                             have_v6 == 0)
1959                                 goto skip;
1960                         export.ex_addr = addrp;
1961                         export.ex_addrlen = addrlen;
1962                         export.ex_masklen = 0;
1963                         break;
1964                 case GT_NET:
1965                         export.ex_addr = (struct sockaddr *)
1966                             &grp->gr_ptr.gt_net.nt_net;
1967                         if (export.ex_addr->sa_family == AF_INET6 &&
1968                             have_v6 == 0)
1969                                 goto skip;
1970                         export.ex_addrlen = export.ex_addr->sa_len;
1971                         memset(&ss, 0, sizeof ss);
1972                         ss.ss_family = export.ex_addr->sa_family;
1973                         ss.ss_len = export.ex_addr->sa_len;
1974                         if (allones(&ss, grp->gr_ptr.gt_net.nt_len) != 0) {
1975                                 syslog(LOG_ERR,
1976                                     "\"%s\", line %ld: Bad network flag",
1977                                     line, (unsigned long)lineno);
1978                                 return (1);
1979                         }
1980                         export.ex_mask = (struct sockaddr *)&ss;
1981                         export.ex_masklen = ss.ss_len;
1982                         break;
1983                 default:
1984                         syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type",
1985                             line, (unsigned long)lineno);
1986                         return (1);
1987                 };
1988
1989                 /*
1990                  * XXX:
1991                  * Maybe I should just use the fsb->f_mntonname path?
1992                  */
1993
1994                 mel.mel_path = dirp;
1995                 mel.mel_nexports = 1;
1996                 mel.mel_exports = &export;
1997
1998                 if (rump_sys_nfssvc(NFSSVC_SETEXPORTSLIST, &mel) != 0) {
1999                         syslog(LOG_ERR,
2000             "\"%s\", line %ld: Can't change attributes for %s to %s: %m %d",
2001                             line, (unsigned long)lineno,
2002                             dirp, (grp->gr_type == GT_HOST) ?
2003                             grp->gr_ptr.gt_addrinfo->ai_canonname :
2004                             (grp->gr_type == GT_NET) ?
2005                             grp->gr_ptr.gt_net.nt_name :
2006                             "Unknown", errno);
2007                         return (1);
2008                 }
2009 skip:
2010                 if (addrp) {
2011                         ai = ai->ai_next;
2012                         if (ai == NULL)
2013                                 done = TRUE;
2014                         else {
2015                                 addrp = ai->ai_addr;
2016                                 addrlen = ai->ai_addrlen;
2017                         }
2018                 } else
2019                         done = TRUE;
2020         }
2021         return (0);
2022 }
2023
2024 /*
2025  * Translate a net address.
2026  */
2027 static int
2028 get_net(cp, net, maskflg)
2029         char *cp;
2030         struct netmsk *net;
2031         int maskflg;
2032 {
2033         struct netent *np;
2034         char *thename, *p, *prefp;
2035         struct sockaddr_in sin, *sinp;
2036         struct sockaddr *sa;
2037         struct addrinfo hints, *ai = NULL;
2038         char netname[NI_MAXHOST];
2039         long preflen;
2040         int ecode;
2041
2042         (void)memset(&sin, 0, sizeof(sin));
2043         if ((opt_flags & OP_MASKLEN) && !maskflg) {
2044                 p = strchr(cp, '/');
2045                 *p = '\0';
2046                 prefp = p + 1;
2047         } else {
2048                 p = NULL;       /* XXXGCC -Wuninitialized */
2049                 prefp = NULL;   /* XXXGCC -Wuninitialized */
2050         }
2051
2052         if ((np = getnetbyname(cp)) != NULL) {
2053                 sin.sin_family = AF_INET;
2054                 sin.sin_len = sizeof sin;
2055                 sin.sin_addr = inet_makeaddr(np->n_net, 0);
2056                 sa = (struct sockaddr *)&sin;
2057         } else if (isdigit((unsigned char)*cp)) {
2058                 memset(&hints, 0, sizeof hints);
2059                 hints.ai_family = AF_UNSPEC;
2060                 hints.ai_flags = AI_NUMERICHOST;
2061                 if (getaddrinfo(cp, NULL, &hints, &ai) != 0) {
2062                         /*
2063                          * If getaddrinfo() failed, try the inet4 network
2064                          * notation with less than 3 dots.
2065                          */
2066                         sin.sin_family = AF_INET;
2067                         sin.sin_len = sizeof sin;
2068                         sin.sin_addr = inet_makeaddr(inet_network(cp),0);
2069                         if (debug)
2070                                 fprintf(stderr, "get_net: v4 addr %x\n",
2071                                     sin.sin_addr.s_addr);
2072                         sa = (struct sockaddr *)&sin;
2073                 } else
2074                         sa = ai->ai_addr;
2075         } else if (isxdigit((unsigned char)*cp) || *cp == ':') {
2076                 memset(&hints, 0, sizeof hints);
2077                 hints.ai_family = AF_UNSPEC;
2078                 hints.ai_flags = AI_NUMERICHOST;
2079                 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2080                         sa = ai->ai_addr;
2081                 else
2082                         goto fail;
2083         } else
2084                 goto fail;
2085
2086         /*
2087          * Only allow /pref notation for v6 addresses.
2088          */
2089         if (sa->sa_family == AF_INET6 && (!(opt_flags & OP_MASKLEN) || maskflg))
2090                 return 1;
2091
2092         ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2093             NULL, 0, ninumeric);
2094         if (ecode != 0)
2095                 goto fail;
2096
2097         if (maskflg)
2098                 net->nt_len = countones(sa);
2099         else {
2100                 if (opt_flags & OP_MASKLEN) {
2101                         errno = 0;
2102                         preflen = strtol(prefp, NULL, 10);
2103                         if (preflen == LONG_MIN && errno == ERANGE)
2104                                 goto fail;
2105                         net->nt_len = (int)preflen;
2106                         *p = '/';
2107                 }
2108
2109                 if (np)
2110                         thename = np->n_name;
2111                 else {
2112                         if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2113                             NULL, 0, ninumeric) != 0)
2114                                 strlcpy(netname, "?", sizeof(netname));
2115                         thename = netname;
2116                 }
2117                 net->nt_name = estrdup(thename);
2118                 memcpy(&net->nt_net, sa, sa->sa_len);
2119         }
2120
2121         if (!maskflg && sa->sa_family == AF_INET &&
2122             !(opt_flags & (OP_MASK|OP_MASKLEN))) {
2123                 sinp = (struct sockaddr_in *)sa;
2124                 if (IN_CLASSA(sinp->sin_addr.s_addr))
2125                         net->nt_len = 8;
2126                 else if (IN_CLASSB(sinp->sin_addr.s_addr))
2127                         net->nt_len = 16;
2128                 else if (IN_CLASSC(sinp->sin_addr.s_addr))
2129                         net->nt_len = 24;
2130                 else if (IN_CLASSD(sinp->sin_addr.s_addr))
2131                         net->nt_len = 28;
2132                 else
2133                         net->nt_len = 32;       /* XXX */
2134         }
2135
2136         if (ai)
2137                 freeaddrinfo(ai);
2138         return 0;
2139
2140 fail:
2141         if (ai)
2142                 freeaddrinfo(ai);
2143         return 1;
2144 }
2145
2146 /*
2147  * Parse out the next white space separated field
2148  */
2149 static void
2150 nextfield(cp, endcp)
2151         char **cp;
2152         char **endcp;
2153 {
2154         char *p;
2155
2156         p = *cp;
2157         while (*p == ' ' || *p == '\t')
2158                 p++;
2159         if (*p == '\n' || *p == '\0')
2160                 *cp = *endcp = p;
2161         else {
2162                 *cp = p++;
2163                 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2164                         p++;
2165                 *endcp = p;
2166         }
2167 }
2168
2169 /*
2170  * Parse a description of a credential.
2171  */
2172 static void
2173 parsecred(namelist, cr)
2174         char *namelist;
2175         struct uucred *cr;
2176 {
2177         char *thename;
2178         int cnt;
2179         char *names;
2180         struct passwd *pw;
2181         struct group *gr;
2182         int ngroups;
2183         gid_t grps[NGROUPS + 1];
2184
2185         /*
2186          * Set up the unprivileged user.
2187          */
2188         *cr = def_anon;
2189         /*
2190          * Get the user's password table entry.
2191          */
2192         names = strsep(&namelist, " \t\n");
2193         thename = strsep(&names, ":");
2194         if (isdigit((unsigned char)*thename) || *thename == '-')
2195                 pw = getpwuid(atoi(thename));
2196         else
2197                 pw = getpwnam(thename);
2198         /*
2199          * Credentials specified as those of a user.
2200          */
2201         if (names == NULL) {
2202                 if (pw == NULL) {
2203                         syslog(LOG_ERR, "Unknown user: %s", thename);
2204                         return;
2205                 }
2206                 cr->cr_uid = pw->pw_uid;
2207                 ngroups = NGROUPS + 1;
2208                 if (getgrouplist(pw->pw_name, pw->pw_gid, grps, &ngroups))
2209                         syslog(LOG_ERR, "Too many groups for user %s", thename);
2210                 /*
2211                  * Convert from int's to gid_t's and compress out duplicate
2212                  */
2213                 cr->cr_ngroups = ngroups - 1;
2214                 cr->cr_gid = grps[0];
2215                 for (cnt = 1; cnt < ngroups; cnt++)
2216                         cr->cr_groups[cnt - 1] = grps[cnt];
2217                 return;
2218         }
2219         /*
2220          * Explicit credential specified as a colon separated list:
2221          *      uid:gid:gid:...
2222          */
2223         if (pw != NULL)
2224                 cr->cr_uid = pw->pw_uid;
2225         else if (isdigit((unsigned char)*thename) || *thename == '-')
2226                 cr->cr_uid = atoi(thename);
2227         else {
2228                 syslog(LOG_ERR, "Unknown user: %s", thename);
2229                 return;
2230         }
2231         cr->cr_ngroups = 0;
2232         while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2233                 thename = strsep(&names, ":");
2234                 if (isdigit((unsigned char)*thename) || *thename == '-') {
2235                         cr->cr_groups[cr->cr_ngroups++] = atoi(thename);
2236                 } else {
2237                         if ((gr = getgrnam(thename)) == NULL) {
2238                                 syslog(LOG_ERR, "Unknown group: %s", thename);
2239                                 continue;
2240                         }
2241                         cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2242                 }
2243         }
2244         if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2245                 syslog(LOG_ERR, "Too many groups");
2246 }
2247
2248 #define STRSIZ  (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2249 /*
2250  * Routines that maintain the remote mounttab
2251  */
2252 static void
2253 get_mountlist()
2254 {
2255         struct mountlist *mlp, **mlpp;
2256         char *host, *dirp, *cp;
2257         char str[STRSIZ];
2258         FILE *mlfile;
2259
2260         if ((mlfile = rumpfopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2261                 syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST);
2262                 return;
2263         }
2264         mlpp = &mlhead;
2265         while (fgets(str, STRSIZ, mlfile) != NULL) {
2266                 cp = str;
2267                 host = strsep(&cp, " \t\n");
2268                 dirp = strsep(&cp, " \t\n");
2269                 if (host == NULL || dirp == NULL)
2270                         continue;
2271                 mlp = emalloc(sizeof(*mlp));
2272                 (void)strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2273                 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2274                 (void)strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2275                 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2276                 mlp->ml_next = NULL;
2277                 *mlpp = mlp;
2278                 mlpp = &mlp->ml_next;
2279         }
2280         (void)fclose(mlfile);
2281 }
2282
2283 static int
2284 del_mlist(hostp, dirp, saddr)
2285         char *hostp, *dirp;
2286         struct sockaddr *saddr;
2287 {
2288         struct mountlist *mlp, **mlpp;
2289         struct mountlist *mlp2;
2290         u_short sport;
2291         FILE *mlfile;
2292         int fnd = 0, ret = 0;
2293         char host[NI_MAXHOST];
2294
2295         switch (saddr->sa_family) {
2296         case AF_INET6:
2297                 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
2298                 break;
2299         case AF_INET:
2300                 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
2301                 break;
2302         default:
2303                 return -1;
2304         }
2305         mlpp = &mlhead;
2306         mlp = mlhead;
2307         while (mlp) {
2308                 if (!strcmp(mlp->ml_host, hostp) &&
2309                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2310                         if (!(mlp->ml_flag & DP_NORESMNT) &&
2311                             sport >= IPPORT_RESERVED) {
2312                                 if (getnameinfo(saddr, saddr->sa_len, host,
2313                                     sizeof host, NULL, 0, ninumeric) != 0)
2314                                         strlcpy(host, "?", sizeof(host));
2315                                 syslog(LOG_NOTICE,
2316                                 "Umount request for %s:%s from %s refused\n",
2317                                     mlp->ml_host, mlp->ml_dirp, host);
2318                                 ret = -1;
2319                                 goto cont;
2320                         }
2321                         fnd = 1;
2322                         mlp2 = mlp;
2323                         *mlpp = mlp = mlp->ml_next;
2324                         free(mlp2);
2325                 } else {
2326 cont:
2327                         mlpp = &mlp->ml_next;
2328                         mlp = mlp->ml_next;
2329                 }
2330         }
2331         if (fnd) {
2332                 if ((mlfile = rumpfopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2333                         syslog(LOG_ERR, "Can't update %s: %m",
2334                             _PATH_RMOUNTLIST);
2335                         return ret;
2336                 }
2337                 mlp = mlhead;
2338                 while (mlp) {
2339                         (void)fprintf(mlfile, "%s %s\n", mlp->ml_host,
2340                             mlp->ml_dirp);
2341                         mlp = mlp->ml_next;
2342                 }
2343                 (void)fclose(mlfile);
2344         }
2345         return ret;
2346 }
2347
2348 static void
2349 add_mlist(hostp, dirp, flags)
2350         char *hostp, *dirp;
2351         int flags;
2352 {
2353         struct mountlist *mlp, **mlpp;
2354         FILE *mlfile;
2355
2356         mlpp = &mlhead;
2357         mlp = mlhead;
2358         while (mlp) {
2359                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2360                         return;
2361                 mlpp = &mlp->ml_next;
2362                 mlp = mlp->ml_next;
2363         }
2364         mlp = emalloc(sizeof(*mlp));
2365         strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2366         mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2367         strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2368         mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2369         mlp->ml_flag = flags;
2370         mlp->ml_next = NULL;
2371         *mlpp = mlp;
2372         if ((mlfile = rumpfopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2373                 syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST);
2374                 return;
2375         }
2376         (void)fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2377         (void)fclose(mlfile);
2378 }
2379
2380 /*
2381  * This function is called via. SIGTERM when the system is going down.
2382  * It sends a broadcast RPCMNT_UMNTALL.
2383  */
2384 /* ARGSUSED */
2385 static void
2386 send_umntall(n)
2387         int n;
2388 {
2389 #if 0
2390         (void)clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
2391             xdr_void, NULL, xdr_void, NULL, (resultproc_t)umntall_each);
2392 #endif
2393         exit(0);
2394 }
2395
2396 #if 0
2397 static int
2398 umntall_each(resultsp, raddr)
2399         caddr_t resultsp;
2400         struct sockaddr_in *raddr;
2401 {
2402         return (1);
2403 }
2404 #endif
2405
2406 /*
2407  * Free up a group list.
2408  */
2409 static void
2410 free_grp(grp)
2411         struct grouplist *grp;
2412 {
2413
2414         if (grp->gr_type == GT_HOST) {
2415                 if (grp->gr_ptr.gt_addrinfo != NULL)
2416                         freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2417         } else if (grp->gr_type == GT_NET) {
2418                 if (grp->gr_ptr.gt_net.nt_name)
2419                         free(grp->gr_ptr.gt_net.nt_name);
2420         }
2421         free(grp);
2422 }
2423
2424 #if 0
2425 static void
2426 SYSLOG(int pri, const char *fmt,...)
2427 {
2428         va_list ap;
2429
2430         va_start(ap, fmt);
2431
2432         if (debug)
2433                 vfprintf(stderr, fmt, ap);
2434         else
2435                 vsyslog(pri, fmt, ap);
2436
2437         va_end(ap);
2438 }
2439 #endif
2440
2441 /*
2442  * Check options for consistency.
2443  */
2444 static int
2445 check_options(line, lineno, dp)
2446         const char *line;
2447         size_t lineno;
2448         struct dirlist *dp;
2449 {
2450
2451         if (dp == NULL) {
2452                 syslog(LOG_ERR,
2453                     "\"%s\", line %ld: missing directory list",
2454                     line, (unsigned long)lineno);
2455                 return (1);
2456         }
2457         if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) ||
2458             (opt_flags & (OP_MAPROOT|OP_KERB)) == (OP_MAPROOT|OP_KERB) ||
2459             (opt_flags & (OP_MAPALL|OP_KERB)) == (OP_MAPALL|OP_KERB)) {
2460                 syslog(LOG_ERR,
2461                     "\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive",
2462                     line, (unsigned long)lineno);
2463                 return (1);
2464         }
2465         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2466                 syslog(LOG_ERR, "\"%s\", line %ld: -mask requires -net",
2467                     line, (unsigned long)lineno);
2468                 return (1);
2469         }
2470         if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN) != 0) {
2471                 syslog(LOG_ERR, "\"%s\", line %ld: /pref and -mask mutually"
2472                     " exclusive",
2473                     line, (unsigned long)lineno);
2474                 return (1);
2475         }
2476         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2477                 syslog(LOG_ERR,
2478                     "\"%s\", line %ld: -alldirs has multiple directories",
2479                     line, (unsigned long)lineno);
2480                 return (1);
2481         }
2482         return (0);
2483 }
2484
2485 /*
2486  * Check an absolute directory path for any symbolic links. Return true
2487  * if no symbolic links are found.
2488  */
2489 static int
2490 check_dirpath(line, lineno, dirp)
2491         const char *line;
2492         size_t lineno;
2493         char *dirp;
2494 {
2495         char *cp;
2496         struct stat sb;
2497         char *file = "";
2498
2499         for (cp = dirp + 1; *cp; cp++) {
2500                 if (*cp == '/') {
2501                         *cp = '\0';
2502                         if (rump_sys_lstat(dirp, &sb) == -1)
2503                                 goto bad;
2504                         if (!S_ISDIR(sb.st_mode))
2505                                 goto bad1;
2506                         *cp = '/';
2507                 }
2508         }
2509
2510         cp = NULL;
2511         if (rump_sys_lstat(dirp, &sb) == -1)
2512                 goto bad;
2513
2514         if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
2515                 file = " file or a";
2516                 goto bad1;
2517         }
2518
2519         return 1;
2520
2521 bad:
2522         syslog(LOG_ERR,
2523             "\"%s\", line %ld: lstat for `%s' failed: %m",
2524             line, (unsigned long)lineno, dirp);
2525         if (cp)
2526                 *cp = '/';
2527         return 0;
2528
2529 bad1:
2530         syslog(LOG_ERR,
2531             "\"%s\", line %ld: `%s' is not a%s directory",
2532             line, (unsigned long)lineno, dirp, file);
2533         abort();
2534         if (cp)
2535                 *cp = '/';
2536         return 0;
2537 }
2538
2539 static void
2540 bind_resv_port(int sock, sa_family_t family, in_port_t port)
2541 {
2542         struct sockaddr *sa;
2543         struct sockaddr_in sasin;
2544         struct sockaddr_in6 sasin6;
2545
2546         switch (family) {
2547         case AF_INET:
2548                 (void)memset(&sasin, 0, sizeof(sasin));
2549                 sasin.sin_len = sizeof(sasin);
2550                 sasin.sin_family = family;
2551                 sasin.sin_port = htons(port);
2552                 sa = (struct sockaddr *)(void *)&sasin;
2553                 break;
2554         case AF_INET6:
2555                 (void)memset(&sasin6, 0, sizeof(sasin6));
2556                 sasin6.sin6_len = sizeof(sasin6);
2557                 sasin6.sin6_family = family;
2558                 sasin6.sin6_port = htons(port);
2559                 sa = (struct sockaddr *)(void *)&sasin6;
2560                 break;
2561         default:
2562                 syslog(LOG_ERR, "Unsupported address family %d", family);
2563                 return;
2564         }
2565         if (bindresvport_sa(sock, sa) == -1)
2566                 syslog(LOG_ERR, "Cannot bind to reserved port %d (%m)", port);
2567 }
2568
2569 /* ARGSUSED */
2570 static void
2571 no_nfs(int sig)
2572 {
2573         syslog(LOG_ERR, "kernel NFS support not present; exiting");
2574         exit(1);
2575 }