]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/mountd/mountd.c
This commit was generated by cvs2svn to compensate for changes in r94532,
[FreeBSD/FreeBSD.git] / sbin / mountd / mountd.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Herb Hasler and Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40         The Regents of the University of California.  All rights reserved.\n";
41 #endif /*not lint*/
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)mountd.c    8.15 (Berkeley) 5/1/95";
46 #endif
47 static const char rcsid[] =
48   "$FreeBSD$";
49 #endif /*not lint*/
50
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #include <sys/fcntl.h>
54 #include <sys/stat.h>
55 #include <sys/syslog.h>
56 #include <sys/sysctl.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 <nfsserver/nfs.h>
65 #include <ufs/ufs/ufsmount.h>
66 #include <fs/msdosfs/msdosfsmount.h>
67 #include <fs/ntfs/ntfsmount.h>
68 #include <isofs/cd9660/cd9660_mount.h>  /* XXX need isofs in include */
69
70 #include <arpa/inet.h>
71
72 #include <ctype.h>
73 #include <err.h>
74 #include <errno.h>
75 #include <grp.h>
76 #include <netdb.h>
77 #include <pwd.h>
78 #include <signal.h>
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <string.h>
82 #include <unistd.h>
83 #include "pathnames.h"
84
85 #ifdef DEBUG
86 #include <stdarg.h>
87 #endif
88
89 #ifndef MOUNTDLOCK
90 #define MOUNTDLOCK "/var/run/mountd.lock"
91 #endif
92
93 /*
94  * Structures for keeping the mount list and export list
95  */
96 struct mountlist {
97         struct mountlist *ml_next;
98         char    ml_host[RPCMNT_NAMELEN+1];
99         char    ml_dirp[RPCMNT_PATHLEN+1];
100 };
101
102 struct dirlist {
103         struct dirlist  *dp_left;
104         struct dirlist  *dp_right;
105         int             dp_flag;
106         struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
107         char            dp_dirp[1];     /* Actually malloc'd to size of dir */
108 };
109 /* dp_flag bits */
110 #define DP_DEFSET       0x1
111 #define DP_HOSTSET      0x2
112
113 struct exportlist {
114         struct exportlist *ex_next;
115         struct dirlist  *ex_dirl;
116         struct dirlist  *ex_defdir;
117         int             ex_flag;
118         fsid_t          ex_fs;
119         char            *ex_fsdir;
120         char            *ex_indexfile;
121 };
122 /* ex_flag bits */
123 #define EX_LINKED       0x1
124
125 struct netmsk {
126         struct sockaddr_storage nt_net;
127         struct sockaddr_storage nt_mask;
128         char            *nt_name;
129 };
130
131 union grouptypes {
132         struct addrinfo *gt_addrinfo;
133         struct netmsk   gt_net;
134 };
135
136 struct grouplist {
137         int gr_type;
138         union grouptypes gr_ptr;
139         struct grouplist *gr_next;
140 };
141 /* Group types */
142 #define GT_NULL         0x0
143 #define GT_HOST         0x1
144 #define GT_NET          0x2
145 #define GT_DEFAULT      0x3
146 #define GT_IGNORE       0x5
147
148 struct hostlist {
149         int              ht_flag;       /* Uses DP_xx bits */
150         struct grouplist *ht_grp;
151         struct hostlist  *ht_next;
152 };
153
154 struct fhreturn {
155         int     fhr_flag;
156         int     fhr_vers;
157         nfsfh_t fhr_fh;
158 };
159
160 /* Global defs */
161 char    *add_expdir(struct dirlist **, char *, int);
162 void    add_dlist(struct dirlist **, struct dirlist *,
163                                 struct grouplist *, int);
164 void    add_mlist(char *, char *);
165 int     check_dirpath(char *);
166 int     check_options(struct dirlist *);
167 int     checkmask(struct sockaddr *sa);
168 int     chk_host(struct dirlist *, struct sockaddr *, int *, int *);
169 void    del_mlist(char *hostp, char *dirp);
170 struct dirlist *dirp_search(struct dirlist *, char *);
171 int     do_mount(struct exportlist *, struct grouplist *, int,
172                 struct xucred *, char *, int, struct statfs *);
173 int     do_opt(char **, char **, struct exportlist *, struct grouplist *,
174                                 int *, int *, struct xucred *);
175 struct  exportlist *ex_search(fsid_t *);
176 struct  exportlist *get_exp(void);
177 void    free_dir(struct dirlist *);
178 void    free_exp(struct exportlist *);
179 void    free_grp(struct grouplist *);
180 void    free_host(struct hostlist *);
181 void    get_exportlist(void);
182 int     get_host(char *, struct grouplist *, struct grouplist *);
183 struct hostlist *get_ht(void);
184 int     get_line(void);
185 void    get_mountlist(void);
186 int     get_net(char *, struct netmsk *, int);
187 void    getexp_err(struct exportlist *, struct grouplist *);
188 struct grouplist *get_grp(void);
189 void    hang_dirp(struct dirlist *, struct grouplist *,
190                                 struct exportlist *, int);
191 void    huphandler(int sig);
192 int     makemask(struct sockaddr_storage *ssp, int bitlen);
193 void    mntsrv(struct svc_req *, SVCXPRT *);
194 void    nextfield(char **, char **);
195 void    out_of_mem(void);
196 void    parsecred(char *, struct xucred *);
197 int     put_exlist(struct dirlist *, XDR *, struct dirlist *, int *);
198 void    *sa_rawaddr(struct sockaddr *sa, int *nbytes);
199 int     sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
200     struct sockaddr *samask);
201 int     scan_tree(struct dirlist *, struct sockaddr *);
202 static void usage(void);
203 int     xdr_dir(XDR *, char *);
204 int     xdr_explist(XDR *, caddr_t);
205 int     xdr_fhs(XDR *, caddr_t);
206 int     xdr_mlist(XDR *, caddr_t);
207 void    terminate(int);
208
209 struct exportlist *exphead;
210 struct mountlist *mlhead;
211 struct grouplist *grphead;
212 char exname[MAXPATHLEN];
213 struct xucred def_anon = {
214         XUCRED_VERSION,
215         (uid_t)-2,
216         1,
217         { (gid_t)-2 },
218         NULL
219 };
220 int force_v2 = 0;
221 int resvport_only = 1;
222 int dir_only = 1;
223 int log = 0;
224 int got_sighup = 0;
225
226 int opt_flags;
227 static int have_v6 = 1;
228 #ifdef NI_WITHSCOPEID
229 static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
230 #else
231 static const int ninumeric = NI_NUMERICHOST;
232 #endif
233
234 int mountdlockfd;
235 /* Bits for opt_flags above */
236 #define OP_MAPROOT      0x01
237 #define OP_MAPALL       0x02
238 /* 0x4 free */
239 #define OP_MASK         0x08
240 #define OP_NET          0x10
241 #define OP_ALLDIRS      0x40
242 #define OP_HAVEMASK     0x80    /* A mask was specified or inferred. */
243 #define OP_MASKLEN      0x200
244
245 #ifdef DEBUG
246 int debug = 1;
247 void    SYSLOG(int, const char *, ...) __printflike(2, 3);
248 #define syslog SYSLOG
249 #else
250 int debug = 0;
251 #endif
252
253 /*
254  * Mountd server for NFS mount protocol as described in:
255  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
256  * The optional arguments are the exports file name
257  * default: _PATH_EXPORTS
258  * and "-n" to allow nonroot mount.
259  */
260 int
261 main(argc, argv)
262         int argc;
263         char **argv;
264 {
265         fd_set readfds;
266         SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
267         struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
268         int udpsock, tcpsock, udp6sock, tcp6sock;
269         int xcreated = 0, s;
270         int one = 1;
271         int c, error;
272
273         udp6conf = tcp6conf = NULL;
274         udp6sock = tcp6sock = NULL;
275
276         /* Check that another mountd isn't already running. */
277         if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
278                 err(1, "%s", MOUNTDLOCK);
279
280         if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
281                 errx(1, "another rpc.mountd is already running. Aborting");
282         s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
283         if (s < 0)
284                 have_v6 = 0;
285         else
286                 close(s);
287         if (modfind("nfsserver") < 0) {
288                 /* Not present in kernel, try loading it */
289                 if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
290                         errx(1, "NFS server is not available or loadable");
291         }
292
293         while ((c = getopt(argc, argv, "2dlnr")) != -1)
294                 switch (c) {
295                 case '2':
296                         force_v2 = 1;
297                         break;
298                 case 'n':
299                         resvport_only = 0;
300                         break;
301                 case 'r':
302                         dir_only = 0;
303                         break;
304                 case 'd':
305                         debug = debug ? 0 : 1;
306                         break;
307                 case 'l':
308                         log = 1;
309                         break;
310                 default:
311                         usage();
312                 };
313         argc -= optind;
314         argv += optind;
315         grphead = (struct grouplist *)NULL;
316         exphead = (struct exportlist *)NULL;
317         mlhead = (struct mountlist *)NULL;
318         if (argc == 1) {
319                 strncpy(exname, *argv, MAXPATHLEN-1);
320                 exname[MAXPATHLEN-1] = '\0';
321         } else
322                 strcpy(exname, _PATH_EXPORTS);
323         openlog("mountd", LOG_PID, LOG_DAEMON);
324         if (debug)
325                 warnx("getting export list");
326         get_exportlist();
327         if (debug)
328                 warnx("getting mount list");
329         get_mountlist();
330         if (debug)
331                 warnx("here we go");
332         if (debug == 0) {
333                 daemon(0, 0);
334                 signal(SIGINT, SIG_IGN);
335                 signal(SIGQUIT, SIG_IGN);
336         }
337         signal(SIGHUP, huphandler);
338         signal(SIGTERM, terminate);
339         { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
340           if (pidfile != NULL) {
341                 fprintf(pidfile, "%d\n", getpid());
342                 fclose(pidfile);
343           }
344         }
345         rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
346         rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
347         udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
348         tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
349         udpconf  = getnetconfigent("udp");
350         tcpconf  = getnetconfigent("tcp");
351         if (!have_v6)
352                 goto skip_v6;
353         udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
354         tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
355         /*
356          * We're doing host-based access checks here, so don't allow
357          * v4-in-v6 to confuse things. The kernel will disable it
358          * by default on NFS sockets too.
359          */
360         if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
361                 IPV6_BINDV6ONLY, &one, sizeof one) < 0){
362                 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
363                 exit(1);
364         }
365         if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
366                 IPV6_BINDV6ONLY, &one, sizeof one) < 0){
367                 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
368                 exit(1);
369         }
370         udp6conf = getnetconfigent("udp6");
371         tcp6conf = getnetconfigent("tcp6");
372
373 skip_v6:
374         if (!resvport_only) {
375                 if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
376                     &resvport_only, sizeof(resvport_only)) != 0 &&
377                     errno != ENOENT) {
378                         syslog(LOG_ERR, "sysctl: %m");
379                         exit(1);
380                 }
381         }
382         if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
383             (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
384                 syslog(LOG_ERR, "can't create socket");
385                 exit(1);
386         }
387         if (udpsock != -1 && udpconf != NULL) {
388                 bindresvport(udpsock, NULL);
389                 udptransp = svc_dg_create(udpsock, 0, 0);
390                 if (udptransp != NULL) {
391                         if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
392                             mntsrv, udpconf))
393                                 syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
394                         else
395                                 xcreated++;
396                         if (!force_v2) {
397                                 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
398                                     mntsrv, udpconf))
399                                         syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
400                                 else
401                                         xcreated++;
402                         }
403                 } else
404                         syslog(LOG_WARNING, "can't create UDP services");
405
406         }
407         if (tcpsock != -1 && tcpconf != NULL) {
408                 bindresvport(tcpsock, NULL);
409                 listen(tcpsock, SOMAXCONN);
410                 tcptransp = svc_vc_create(tcpsock, 0, 0);
411                 if (tcptransp != NULL) {
412                         if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
413                             mntsrv, tcpconf))
414                                 syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
415                         else
416                                 xcreated++;
417                         if (!force_v2) {
418                                 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
419                                     mntsrv, tcpconf))
420                                         syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
421                                 else
422                                         xcreated++;
423                         }
424                 } else
425                         syslog(LOG_WARNING, "can't create TCP service");
426
427         }
428         if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
429                 bindresvport(udp6sock, NULL);
430                 udp6transp = svc_dg_create(udp6sock, 0, 0);
431                 if (udp6transp != NULL) {
432                         if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
433                             mntsrv, udp6conf))
434                                 syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
435                         else
436                                 xcreated++;
437                         if (!force_v2) {
438                                 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
439                                     mntsrv, udp6conf))
440                                         syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
441                                 else
442                                         xcreated++;
443                         }
444                 } else
445                         syslog(LOG_WARNING, "can't create UDP6 service");
446
447         }
448         if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
449                 bindresvport(tcp6sock, NULL);
450                 listen(tcp6sock, SOMAXCONN);
451                 tcp6transp = svc_vc_create(tcp6sock, 0, 0);
452                 if (tcp6transp != NULL) {
453                         if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
454                             mntsrv, tcp6conf))
455                                 syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
456                         else
457                                 xcreated++;
458                         if (!force_v2) {
459                                 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
460                                     mntsrv, tcp6conf))
461                                         syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
462                                         else
463                                                 xcreated++;
464                                 }
465                 } else
466                         syslog(LOG_WARNING, "can't create TCP6 service");
467
468         }
469         if (xcreated == 0) {
470                 syslog(LOG_ERR, "could not create any services");
471                 exit(1);
472         }
473
474         /* Expand svc_run() here so that we can call get_exportlist(). */
475         for (;;) {
476                 if (got_sighup) {
477                         get_exportlist();
478                         got_sighup = 0;
479                 }
480                 readfds = svc_fdset;
481                 switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
482                 case -1:
483                         if (errno == EINTR)
484                                 continue;
485                         syslog(LOG_ERR, "mountd died: select: %m");
486                         exit(1);
487                 case 0:
488                         continue;
489                 default:
490                         svc_getreqset(&readfds);
491                 }
492         }
493 }
494
495 static void
496 usage()
497 {
498         fprintf(stderr,
499                 "usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
500         exit(1);
501 }
502
503 /*
504  * The mount rpc service
505  */
506 void
507 mntsrv(rqstp, transp)
508         struct svc_req *rqstp;
509         SVCXPRT *transp;
510 {
511         struct exportlist *ep;
512         struct dirlist *dp;
513         struct fhreturn fhr;
514         struct stat stb;
515         struct statfs fsb;
516         struct addrinfo *ai;
517         char host[NI_MAXHOST], numerichost[NI_MAXHOST];
518         int lookup_failed = 1;
519         struct sockaddr *saddr;
520         u_short sport;
521         char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
522         int bad = 0, defset, hostset;
523         sigset_t sighup_mask;
524
525         sigemptyset(&sighup_mask);
526         sigaddset(&sighup_mask, SIGHUP);
527         saddr = svc_getrpccaller(transp)->buf;
528         switch (saddr->sa_family) {
529         case AF_INET6:
530                 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
531                 break;
532         case AF_INET:
533                 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
534                 break;
535         default:
536                 syslog(LOG_ERR, "request from unknown address family");
537                 return;
538         }
539         lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 
540             NULL, 0, 0);
541         getnameinfo(saddr, saddr->sa_len, numerichost,
542             sizeof numerichost, NULL, 0, NI_NUMERICHOST);
543         ai = NULL;
544         switch (rqstp->rq_proc) {
545         case NULLPROC:
546                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
547                         syslog(LOG_ERR, "can't send reply");
548                 return;
549         case RPCMNT_MOUNT:
550                 if (sport >= IPPORT_RESERVED && resvport_only) {
551                         syslog(LOG_NOTICE,
552                             "mount request from %s from unprivileged port",
553                             numerichost);
554                         svcerr_weakauth(transp);
555                         return;
556                 }
557                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
558                         syslog(LOG_NOTICE, "undecodable mount request from %s",
559                             numerichost);
560                         svcerr_decode(transp);
561                         return;
562                 }
563
564                 /*
565                  * Get the real pathname and make sure it is a directory
566                  * or a regular file if the -r option was specified
567                  * and it exists.
568                  */
569                 if (realpath(rpcpath, dirpath) == NULL ||
570                     stat(dirpath, &stb) < 0 ||
571                     (!S_ISDIR(stb.st_mode) &&
572                     (dir_only || !S_ISREG(stb.st_mode))) ||
573                     statfs(dirpath, &fsb) < 0) {
574                         chdir("/");     /* Just in case realpath doesn't */
575                         syslog(LOG_NOTICE,
576                             "mount request from %s for non existent path %s",
577                             numerichost, dirpath);
578                         if (debug)
579                                 warnx("stat failed on %s", dirpath);
580                         bad = ENOENT;   /* We will send error reply later */
581                 }
582
583                 /* Check in the exports list */
584                 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
585                 ep = ex_search(&fsb.f_fsid);
586                 hostset = defset = 0;
587                 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
588                     ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
589                       chk_host(dp, saddr, &defset, &hostset)) ||
590                     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
591                      scan_tree(ep->ex_dirl, saddr) == 0))) {
592                         if (bad) {
593                                 if (!svc_sendreply(transp, xdr_long,
594                                     (caddr_t)&bad))
595                                         syslog(LOG_ERR, "can't send reply");
596                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
597                                 return;
598                         }
599                         if (hostset & DP_HOSTSET)
600                                 fhr.fhr_flag = hostset;
601                         else
602                                 fhr.fhr_flag = defset;
603                         fhr.fhr_vers = rqstp->rq_vers;
604                         /* Get the file handle */
605                         memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
606                         if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
607                                 bad = errno;
608                                 syslog(LOG_ERR, "can't get fh for %s", dirpath);
609                                 if (!svc_sendreply(transp, xdr_long,
610                                     (caddr_t)&bad))
611                                         syslog(LOG_ERR, "can't send reply");
612                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
613                                 return;
614                         }
615                         if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
616                                 syslog(LOG_ERR, "can't send reply");
617                         if (!lookup_failed)
618                                 add_mlist(host, dirpath);
619                         else
620                                 add_mlist(numerichost, dirpath);
621                         if (debug)
622                                 warnx("mount successful");
623                         if (log)
624                                 syslog(LOG_NOTICE,
625                                     "mount request succeeded from %s for %s",
626                                     numerichost, dirpath);
627                 } else {
628                         bad = EACCES;
629                         syslog(LOG_NOTICE,
630                             "mount request denied from %s for %s",
631                             numerichost, dirpath);
632                 }
633
634                 if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad))
635                         syslog(LOG_ERR, "can't send reply");
636                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
637                 return;
638         case RPCMNT_DUMP:
639                 if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
640                         syslog(LOG_ERR, "can't send reply");
641                 else if (log)
642                         syslog(LOG_NOTICE,
643                             "dump request succeeded from %s",
644                             numerichost);
645                 return;
646         case RPCMNT_UMOUNT:
647                 if (sport >= IPPORT_RESERVED && resvport_only) {
648                         syslog(LOG_NOTICE,
649                             "umount request from %s from unprivileged port",
650                             numerichost);
651                         svcerr_weakauth(transp);
652                         return;
653                 }
654                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
655                         syslog(LOG_NOTICE, "undecodable umount request from %s",
656                             numerichost);
657                         svcerr_decode(transp);
658                         return;
659                 }
660                 if (realpath(rpcpath, dirpath) == NULL) {
661                         syslog(LOG_NOTICE, "umount request from %s "
662                             "for non existent path %s",
663                             numerichost, dirpath);
664                 }
665                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
666                         syslog(LOG_ERR, "can't send reply");
667                 if (!lookup_failed)
668                         del_mlist(host, dirpath);
669                 del_mlist(numerichost, dirpath);
670                 if (log)
671                         syslog(LOG_NOTICE,
672                             "umount request succeeded from %s for %s",
673                             numerichost, dirpath);
674                 return;
675         case RPCMNT_UMNTALL:
676                 if (sport >= IPPORT_RESERVED && resvport_only) {
677                         syslog(LOG_NOTICE,
678                             "umountall request from %s from unprivileged port",
679                             numerichost);
680                         svcerr_weakauth(transp);
681                         return;
682                 }
683                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
684                         syslog(LOG_ERR, "can't send reply");
685                 if (!lookup_failed)
686                         del_mlist(host, NULL);
687                 del_mlist(numerichost, NULL);
688                 if (log)
689                         syslog(LOG_NOTICE,
690                             "umountall request succeeded from %s",
691                             numerichost);
692                 return;
693         case RPCMNT_EXPORT:
694                 if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
695                         syslog(LOG_ERR, "can't send reply");
696                 if (log)
697                         syslog(LOG_NOTICE,
698                             "export request succeeded from %s",
699                             numerichost);
700                 return;
701         default:
702                 svcerr_noproc(transp);
703                 return;
704         }
705 }
706
707 /*
708  * Xdr conversion for a dirpath string
709  */
710 int
711 xdr_dir(xdrsp, dirp)
712         XDR *xdrsp;
713         char *dirp;
714 {
715         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
716 }
717
718 /*
719  * Xdr routine to generate file handle reply
720  */
721 int
722 xdr_fhs(xdrsp, cp)
723         XDR *xdrsp;
724         caddr_t cp;
725 {
726         struct fhreturn *fhrp = (struct fhreturn *)cp;
727         u_long ok = 0, len, auth;
728
729         if (!xdr_long(xdrsp, &ok))
730                 return (0);
731         switch (fhrp->fhr_vers) {
732         case 1:
733                 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
734         case 3:
735                 len = NFSX_V3FH;
736                 if (!xdr_long(xdrsp, &len))
737                         return (0);
738                 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
739                         return (0);
740                 auth = RPCAUTH_UNIX;
741                 len = 1;
742                 if (!xdr_long(xdrsp, &len))
743                         return (0);
744                 return (xdr_long(xdrsp, &auth));
745         };
746         return (0);
747 }
748
749 int
750 xdr_mlist(xdrsp, cp)
751         XDR *xdrsp;
752         caddr_t cp;
753 {
754         struct mountlist *mlp;
755         int true = 1;
756         int false = 0;
757         char *strp;
758
759         mlp = mlhead;
760         while (mlp) {
761                 if (!xdr_bool(xdrsp, &true))
762                         return (0);
763                 strp = &mlp->ml_host[0];
764                 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
765                         return (0);
766                 strp = &mlp->ml_dirp[0];
767                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
768                         return (0);
769                 mlp = mlp->ml_next;
770         }
771         if (!xdr_bool(xdrsp, &false))
772                 return (0);
773         return (1);
774 }
775
776 /*
777  * Xdr conversion for export list
778  */
779 int
780 xdr_explist(xdrsp, cp)
781         XDR *xdrsp;
782         caddr_t cp;
783 {
784         struct exportlist *ep;
785         int false = 0;
786         int putdef;
787         sigset_t sighup_mask;
788
789         sigemptyset(&sighup_mask);
790         sigaddset(&sighup_mask, SIGHUP);
791         sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
792         ep = exphead;
793         while (ep) {
794                 putdef = 0;
795                 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
796                         goto errout;
797                 if (ep->ex_defdir && putdef == 0 &&
798                         put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
799                         &putdef))
800                         goto errout;
801                 ep = ep->ex_next;
802         }
803         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
804         if (!xdr_bool(xdrsp, &false))
805                 return (0);
806         return (1);
807 errout:
808         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
809         return (0);
810 }
811
812 /*
813  * Called from xdr_explist() to traverse the tree and export the
814  * directory paths.
815  */
816 int
817 put_exlist(dp, xdrsp, adp, putdefp)
818         struct dirlist *dp;
819         XDR *xdrsp;
820         struct dirlist *adp;
821         int *putdefp;
822 {
823         struct grouplist *grp;
824         struct hostlist *hp;
825         int true = 1;
826         int false = 0;
827         int gotalldir = 0;
828         char *strp;
829
830         if (dp) {
831                 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
832                         return (1);
833                 if (!xdr_bool(xdrsp, &true))
834                         return (1);
835                 strp = dp->dp_dirp;
836                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
837                         return (1);
838                 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
839                         gotalldir = 1;
840                         *putdefp = 1;
841                 }
842                 if ((dp->dp_flag & DP_DEFSET) == 0 &&
843                     (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
844                         hp = dp->dp_hosts;
845                         while (hp) {
846                                 grp = hp->ht_grp;
847                                 if (grp->gr_type == GT_HOST) {
848                                         if (!xdr_bool(xdrsp, &true))
849                                                 return (1);
850                                         strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
851                                         if (!xdr_string(xdrsp, &strp,
852                                             RPCMNT_NAMELEN))
853                                                 return (1);
854                                 } else if (grp->gr_type == GT_NET) {
855                                         if (!xdr_bool(xdrsp, &true))
856                                                 return (1);
857                                         strp = grp->gr_ptr.gt_net.nt_name;
858                                         if (!xdr_string(xdrsp, &strp,
859                                             RPCMNT_NAMELEN))
860                                                 return (1);
861                                 }
862                                 hp = hp->ht_next;
863                                 if (gotalldir && hp == (struct hostlist *)NULL) {
864                                         hp = adp->dp_hosts;
865                                         gotalldir = 0;
866                                 }
867                         }
868                 }
869                 if (!xdr_bool(xdrsp, &false))
870                         return (1);
871                 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
872                         return (1);
873         }
874         return (0);
875 }
876
877 #define LINESIZ 10240
878 char line[LINESIZ];
879 FILE *exp_file;
880
881 /*
882  * Get the export list
883  */
884 void
885 get_exportlist()
886 {
887         struct exportlist *ep, *ep2;
888         struct grouplist *grp, *tgrp;
889         struct exportlist **epp;
890         struct dirlist *dirhead;
891         struct statfs fsb, *fsp;
892         struct xucred anon;
893         char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
894         int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
895
896         dirp = NULL;
897         dirplen = 0;
898
899         /*
900          * First, get rid of the old list
901          */
902         ep = exphead;
903         while (ep) {
904                 ep2 = ep;
905                 ep = ep->ex_next;
906                 free_exp(ep2);
907         }
908         exphead = (struct exportlist *)NULL;
909
910         grp = grphead;
911         while (grp) {
912                 tgrp = grp;
913                 grp = grp->gr_next;
914                 free_grp(tgrp);
915         }
916         grphead = (struct grouplist *)NULL;
917
918         /*
919          * And delete exports that are in the kernel for all local
920          * file systems.
921          * XXX: Should know how to handle all local exportable file systems
922          *      instead of just "ufs".
923          */
924         num = getmntinfo(&fsp, MNT_NOWAIT);
925         for (i = 0; i < num; i++) {
926                 union {
927                         struct ufs_args ua;
928                         struct iso_args ia;
929                         struct msdosfs_args da;
930                         struct ntfs_args na;
931                 } targs;
932
933                 if (!strcmp(fsp->f_fstypename, "ufs") ||
934                     !strcmp(fsp->f_fstypename, "msdosfs") ||
935                     !strcmp(fsp->f_fstypename, "ntfs") ||
936                     !strcmp(fsp->f_fstypename, "cd9660")) {
937                         targs.ua.fspec = NULL;
938                         targs.ua.export.ex_flags = MNT_DELEXPORT;
939                         if (mount(fsp->f_fstypename, fsp->f_mntonname,
940                             fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
941                             errno != ENOENT)
942                                 syslog(LOG_ERR,
943                                     "can't delete exports for %s: %m",
944                                     fsp->f_mntonname);
945                 }
946                 fsp++;
947         }
948
949         /*
950          * Read in the exports file and build the list, calling
951          * mount() as we go along to push the export rules into the kernel.
952          */
953         if ((exp_file = fopen(exname, "r")) == NULL) {
954                 syslog(LOG_ERR, "can't open %s", exname);
955                 exit(2);
956         }
957         dirhead = (struct dirlist *)NULL;
958         while (get_line()) {
959                 if (debug)
960                         warnx("got line %s", line);
961                 cp = line;
962                 nextfield(&cp, &endcp);
963                 if (*cp == '#')
964                         goto nextline;
965
966                 /*
967                  * Set defaults.
968                  */
969                 has_host = FALSE;
970                 anon = def_anon;
971                 exflags = MNT_EXPORTED;
972                 got_nondir = 0;
973                 opt_flags = 0;
974                 ep = (struct exportlist *)NULL;
975
976                 /*
977                  * Create new exports list entry
978                  */
979                 len = endcp-cp;
980                 tgrp = grp = get_grp();
981                 while (len > 0) {
982                         if (len > RPCMNT_NAMELEN) {
983                             getexp_err(ep, tgrp);
984                             goto nextline;
985                         }
986                         if (*cp == '-') {
987                             if (ep == (struct exportlist *)NULL) {
988                                 getexp_err(ep, tgrp);
989                                 goto nextline;
990                             }
991                             if (debug)
992                                 warnx("doing opt %s", cp);
993                             got_nondir = 1;
994                             if (do_opt(&cp, &endcp, ep, grp, &has_host,
995                                 &exflags, &anon)) {
996                                 getexp_err(ep, tgrp);
997                                 goto nextline;
998                             }
999                         } else if (*cp == '/') {
1000                             savedc = *endcp;
1001                             *endcp = '\0';
1002                             if (check_dirpath(cp) &&
1003                                 statfs(cp, &fsb) >= 0) {
1004                                 if (got_nondir) {
1005                                     syslog(LOG_ERR, "dirs must be first");
1006                                     getexp_err(ep, tgrp);
1007                                     goto nextline;
1008                                 }
1009                                 if (ep) {
1010                                     if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1011                                         ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1012                                         getexp_err(ep, tgrp);
1013                                         goto nextline;
1014                                     }
1015                                 } else {
1016                                     /*
1017                                      * See if this directory is already
1018                                      * in the list.
1019                                      */
1020                                     ep = ex_search(&fsb.f_fsid);
1021                                     if (ep == (struct exportlist *)NULL) {
1022                                         ep = get_exp();
1023                                         ep->ex_fs = fsb.f_fsid;
1024                                         ep->ex_fsdir = (char *)
1025                                             malloc(strlen(fsb.f_mntonname) + 1);
1026                                         if (ep->ex_fsdir)
1027                                             strcpy(ep->ex_fsdir,
1028                                                 fsb.f_mntonname);
1029                                         else
1030                                             out_of_mem();
1031                                         if (debug)
1032                                                 warnx("making new ep fs=0x%x,0x%x",
1033                                                     fsb.f_fsid.val[0],
1034                                                     fsb.f_fsid.val[1]);
1035                                     } else if (debug)
1036                                         warnx("found ep fs=0x%x,0x%x",
1037                                             fsb.f_fsid.val[0],
1038                                             fsb.f_fsid.val[1]);
1039                                 }
1040
1041                                 /*
1042                                  * Add dirpath to export mount point.
1043                                  */
1044                                 dirp = add_expdir(&dirhead, cp, len);
1045                                 dirplen = len;
1046                             } else {
1047                                 getexp_err(ep, tgrp);
1048                                 goto nextline;
1049                             }
1050                             *endcp = savedc;
1051                         } else {
1052                             savedc = *endcp;
1053                             *endcp = '\0';
1054                             got_nondir = 1;
1055                             if (ep == (struct exportlist *)NULL) {
1056                                 getexp_err(ep, tgrp);
1057                                 goto nextline;
1058                             }
1059
1060                             /*
1061                              * Get the host or netgroup.
1062                              */
1063                             setnetgrent(cp);
1064                             netgrp = getnetgrent(&hst, &usr, &dom);
1065                             do {
1066                                 if (has_host) {
1067                                     grp->gr_next = get_grp();
1068                                     grp = grp->gr_next;
1069                                 }
1070                                 if (netgrp) {
1071                                     if (hst == 0) {
1072                                         syslog(LOG_ERR,
1073                                 "null hostname in netgroup %s, skipping", cp);
1074                                         grp->gr_type = GT_IGNORE;
1075                                     } else if (get_host(hst, grp, tgrp)) {
1076                                         syslog(LOG_ERR,
1077                         "bad host %s in netgroup %s, skipping", hst, cp);
1078                                         grp->gr_type = GT_IGNORE;
1079                                     }
1080                                 } else if (get_host(cp, grp, tgrp)) {
1081                                     syslog(LOG_ERR, "bad host %s, skipping", cp);
1082                                     grp->gr_type = GT_IGNORE;
1083                                 }
1084                                 has_host = TRUE;
1085                             } while (netgrp && getnetgrent(&hst, &usr, &dom));
1086                             endnetgrent();
1087                             *endcp = savedc;
1088                         }
1089                         cp = endcp;
1090                         nextfield(&cp, &endcp);
1091                         len = endcp - cp;
1092                 }
1093                 if (check_options(dirhead)) {
1094                         getexp_err(ep, tgrp);
1095                         goto nextline;
1096                 }
1097                 if (!has_host) {
1098                         grp->gr_type = GT_DEFAULT;
1099                         if (debug)
1100                                 warnx("adding a default entry");
1101
1102                 /*
1103                  * Don't allow a network export coincide with a list of
1104                  * host(s) on the same line.
1105                  */
1106                 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1107                         syslog(LOG_ERR, "network/host conflict");
1108                         getexp_err(ep, tgrp);
1109                         goto nextline;
1110
1111                 /*
1112                  * If an export list was specified on this line, make sure
1113                  * that we have at least one valid entry, otherwise skip it.
1114                  */
1115                 } else {
1116                         grp = tgrp;
1117                         while (grp && grp->gr_type == GT_IGNORE)
1118                                 grp = grp->gr_next;
1119                         if (! grp) {
1120                             getexp_err(ep, tgrp);
1121                             goto nextline;
1122                         }
1123                 }
1124
1125                 /*
1126                  * Loop through hosts, pushing the exports into the kernel.
1127                  * After loop, tgrp points to the start of the list and
1128                  * grp points to the last entry in the list.
1129                  */
1130                 grp = tgrp;
1131                 do {
1132                         if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1133                             &fsb)) {
1134                                 getexp_err(ep, tgrp);
1135                                 goto nextline;
1136                         }
1137                 } while (grp->gr_next && (grp = grp->gr_next));
1138
1139                 /*
1140                  * Success. Update the data structures.
1141                  */
1142                 if (has_host) {
1143                         hang_dirp(dirhead, tgrp, ep, opt_flags);
1144                         grp->gr_next = grphead;
1145                         grphead = tgrp;
1146                 } else {
1147                         hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1148                                 opt_flags);
1149                         free_grp(grp);
1150                 }
1151                 dirhead = (struct dirlist *)NULL;
1152                 if ((ep->ex_flag & EX_LINKED) == 0) {
1153                         ep2 = exphead;
1154                         epp = &exphead;
1155
1156                         /*
1157                          * Insert in the list in alphabetical order.
1158                          */
1159                         while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1160                                 epp = &ep2->ex_next;
1161                                 ep2 = ep2->ex_next;
1162                         }
1163                         if (ep2)
1164                                 ep->ex_next = ep2;
1165                         *epp = ep;
1166                         ep->ex_flag |= EX_LINKED;
1167                 }
1168 nextline:
1169                 if (dirhead) {
1170                         free_dir(dirhead);
1171                         dirhead = (struct dirlist *)NULL;
1172                 }
1173         }
1174         fclose(exp_file);
1175 }
1176
1177 /*
1178  * Allocate an export list element
1179  */
1180 struct exportlist *
1181 get_exp()
1182 {
1183         struct exportlist *ep;
1184
1185         ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1186         if (ep == (struct exportlist *)NULL)
1187                 out_of_mem();
1188         memset(ep, 0, sizeof(struct exportlist));
1189         return (ep);
1190 }
1191
1192 /*
1193  * Allocate a group list element
1194  */
1195 struct grouplist *
1196 get_grp()
1197 {
1198         struct grouplist *gp;
1199
1200         gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1201         if (gp == (struct grouplist *)NULL)
1202                 out_of_mem();
1203         memset(gp, 0, sizeof(struct grouplist));
1204         return (gp);
1205 }
1206
1207 /*
1208  * Clean up upon an error in get_exportlist().
1209  */
1210 void
1211 getexp_err(ep, grp)
1212         struct exportlist *ep;
1213         struct grouplist *grp;
1214 {
1215         struct grouplist *tgrp;
1216
1217         syslog(LOG_ERR, "bad exports list line %s", line);
1218         if (ep && (ep->ex_flag & EX_LINKED) == 0)
1219                 free_exp(ep);
1220         while (grp) {
1221                 tgrp = grp;
1222                 grp = grp->gr_next;
1223                 free_grp(tgrp);
1224         }
1225 }
1226
1227 /*
1228  * Search the export list for a matching fs.
1229  */
1230 struct exportlist *
1231 ex_search(fsid)
1232         fsid_t *fsid;
1233 {
1234         struct exportlist *ep;
1235
1236         ep = exphead;
1237         while (ep) {
1238                 if (ep->ex_fs.val[0] == fsid->val[0] &&
1239                     ep->ex_fs.val[1] == fsid->val[1])
1240                         return (ep);
1241                 ep = ep->ex_next;
1242         }
1243         return (ep);
1244 }
1245
1246 /*
1247  * Add a directory path to the list.
1248  */
1249 char *
1250 add_expdir(dpp, cp, len)
1251         struct dirlist **dpp;
1252         char *cp;
1253         int len;
1254 {
1255         struct dirlist *dp;
1256
1257         dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1258         if (dp == (struct dirlist *)NULL)
1259                 out_of_mem();
1260         dp->dp_left = *dpp;
1261         dp->dp_right = (struct dirlist *)NULL;
1262         dp->dp_flag = 0;
1263         dp->dp_hosts = (struct hostlist *)NULL;
1264         strcpy(dp->dp_dirp, cp);
1265         *dpp = dp;
1266         return (dp->dp_dirp);
1267 }
1268
1269 /*
1270  * Hang the dir list element off the dirpath binary tree as required
1271  * and update the entry for host.
1272  */
1273 void
1274 hang_dirp(dp, grp, ep, flags)
1275         struct dirlist *dp;
1276         struct grouplist *grp;
1277         struct exportlist *ep;
1278         int flags;
1279 {
1280         struct hostlist *hp;
1281         struct dirlist *dp2;
1282
1283         if (flags & OP_ALLDIRS) {
1284                 if (ep->ex_defdir)
1285                         free((caddr_t)dp);
1286                 else
1287                         ep->ex_defdir = dp;
1288                 if (grp == (struct grouplist *)NULL) {
1289                         ep->ex_defdir->dp_flag |= DP_DEFSET;
1290                 } else while (grp) {
1291                         hp = get_ht();
1292                         hp->ht_grp = grp;
1293                         hp->ht_next = ep->ex_defdir->dp_hosts;
1294                         ep->ex_defdir->dp_hosts = hp;
1295                         grp = grp->gr_next;
1296                 }
1297         } else {
1298
1299                 /*
1300                  * Loop through the directories adding them to the tree.
1301                  */
1302                 while (dp) {
1303                         dp2 = dp->dp_left;
1304                         add_dlist(&ep->ex_dirl, dp, grp, flags);
1305                         dp = dp2;
1306                 }
1307         }
1308 }
1309
1310 /*
1311  * Traverse the binary tree either updating a node that is already there
1312  * for the new directory or adding the new node.
1313  */
1314 void
1315 add_dlist(dpp, newdp, grp, flags)
1316         struct dirlist **dpp;
1317         struct dirlist *newdp;
1318         struct grouplist *grp;
1319         int flags;
1320 {
1321         struct dirlist *dp;
1322         struct hostlist *hp;
1323         int cmp;
1324
1325         dp = *dpp;
1326         if (dp) {
1327                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1328                 if (cmp > 0) {
1329                         add_dlist(&dp->dp_left, newdp, grp, flags);
1330                         return;
1331                 } else if (cmp < 0) {
1332                         add_dlist(&dp->dp_right, newdp, grp, flags);
1333                         return;
1334                 } else
1335                         free((caddr_t)newdp);
1336         } else {
1337                 dp = newdp;
1338                 dp->dp_left = (struct dirlist *)NULL;
1339                 *dpp = dp;
1340         }
1341         if (grp) {
1342
1343                 /*
1344                  * Hang all of the host(s) off of the directory point.
1345                  */
1346                 do {
1347                         hp = get_ht();
1348                         hp->ht_grp = grp;
1349                         hp->ht_next = dp->dp_hosts;
1350                         dp->dp_hosts = hp;
1351                         grp = grp->gr_next;
1352                 } while (grp);
1353         } else {
1354                 dp->dp_flag |= DP_DEFSET;
1355         }
1356 }
1357
1358 /*
1359  * Search for a dirpath on the export point.
1360  */
1361 struct dirlist *
1362 dirp_search(dp, dirp)
1363         struct dirlist *dp;
1364         char *dirp;
1365 {
1366         int cmp;
1367
1368         if (dp) {
1369                 cmp = strcmp(dp->dp_dirp, dirp);
1370                 if (cmp > 0)
1371                         return (dirp_search(dp->dp_left, dirp));
1372                 else if (cmp < 0)
1373                         return (dirp_search(dp->dp_right, dirp));
1374                 else
1375                         return (dp);
1376         }
1377         return (dp);
1378 }
1379
1380 /*
1381  * Scan for a host match in a directory tree.
1382  */
1383 int
1384 chk_host(dp, saddr, defsetp, hostsetp)
1385         struct dirlist *dp;
1386         struct sockaddr *saddr;
1387         int *defsetp;
1388         int *hostsetp;
1389 {
1390         struct hostlist *hp;
1391         struct grouplist *grp;
1392         struct addrinfo *ai;
1393
1394         if (dp) {
1395                 if (dp->dp_flag & DP_DEFSET)
1396                         *defsetp = dp->dp_flag;
1397                 hp = dp->dp_hosts;
1398                 while (hp) {
1399                         grp = hp->ht_grp;
1400                         switch (grp->gr_type) {
1401                         case GT_HOST:
1402                                 ai = grp->gr_ptr.gt_addrinfo;
1403                                 for (; ai; ai = ai->ai_next) {
1404                                         if (!sacmp(ai->ai_addr, saddr, NULL)) {
1405                                                 *hostsetp =
1406                                                     (hp->ht_flag | DP_HOSTSET);
1407                                                 return (1);
1408                                         }
1409                                 }
1410                                 break;
1411                         case GT_NET:
1412                                 if (!sacmp(saddr, (struct sockaddr *)
1413                                     &grp->gr_ptr.gt_net.nt_net,
1414                                     (struct sockaddr *)
1415                                     &grp->gr_ptr.gt_net.nt_mask)) {
1416                                         *hostsetp = (hp->ht_flag | DP_HOSTSET);
1417                                         return (1);
1418                                 }
1419                                 break;
1420                         }
1421                         hp = hp->ht_next;
1422                 }
1423         }
1424         return (0);
1425 }
1426
1427 /*
1428  * Scan tree for a host that matches the address.
1429  */
1430 int
1431 scan_tree(dp, saddr)
1432         struct dirlist *dp;
1433         struct sockaddr *saddr;
1434 {
1435         int defset, hostset;
1436
1437         if (dp) {
1438                 if (scan_tree(dp->dp_left, saddr))
1439                         return (1);
1440                 if (chk_host(dp, saddr, &defset, &hostset))
1441                         return (1);
1442                 if (scan_tree(dp->dp_right, saddr))
1443                         return (1);
1444         }
1445         return (0);
1446 }
1447
1448 /*
1449  * Traverse the dirlist tree and free it up.
1450  */
1451 void
1452 free_dir(dp)
1453         struct dirlist *dp;
1454 {
1455
1456         if (dp) {
1457                 free_dir(dp->dp_left);
1458                 free_dir(dp->dp_right);
1459                 free_host(dp->dp_hosts);
1460                 free((caddr_t)dp);
1461         }
1462 }
1463
1464 /*
1465  * Parse the option string and update fields.
1466  * Option arguments may either be -<option>=<value> or
1467  * -<option> <value>
1468  */
1469 int
1470 do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1471         char **cpp, **endcpp;
1472         struct exportlist *ep;
1473         struct grouplist *grp;
1474         int *has_hostp;
1475         int *exflagsp;
1476         struct xucred *cr;
1477 {
1478         char *cpoptarg, *cpoptend;
1479         char *cp, *endcp, *cpopt, savedc, savedc2;
1480         int allflag, usedarg;
1481
1482         savedc2 = '\0';
1483         cpopt = *cpp;
1484         cpopt++;
1485         cp = *endcpp;
1486         savedc = *cp;
1487         *cp = '\0';
1488         while (cpopt && *cpopt) {
1489                 allflag = 1;
1490                 usedarg = -2;
1491                 if ((cpoptend = strchr(cpopt, ','))) {
1492                         *cpoptend++ = '\0';
1493                         if ((cpoptarg = strchr(cpopt, '=')))
1494                                 *cpoptarg++ = '\0';
1495                 } else {
1496                         if ((cpoptarg = strchr(cpopt, '=')))
1497                                 *cpoptarg++ = '\0';
1498                         else {
1499                                 *cp = savedc;
1500                                 nextfield(&cp, &endcp);
1501                                 **endcpp = '\0';
1502                                 if (endcp > cp && *cp != '-') {
1503                                         cpoptarg = cp;
1504                                         savedc2 = *endcp;
1505                                         *endcp = '\0';
1506                                         usedarg = 0;
1507                                 }
1508                         }
1509                 }
1510                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1511                         *exflagsp |= MNT_EXRDONLY;
1512                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1513                     !(allflag = strcmp(cpopt, "mapall")) ||
1514                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1515                         usedarg++;
1516                         parsecred(cpoptarg, cr);
1517                         if (allflag == 0) {
1518                                 *exflagsp |= MNT_EXPORTANON;
1519                                 opt_flags |= OP_MAPALL;
1520                         } else
1521                                 opt_flags |= OP_MAPROOT;
1522                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1523                     !strcmp(cpopt, "m"))) {
1524                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1525                                 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1526                                 return (1);
1527                         }
1528                         usedarg++;
1529                         opt_flags |= OP_MASK;
1530                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1531                         !strcmp(cpopt, "n"))) {
1532                         if (strchr(cpoptarg, '/') != NULL) {
1533                                 if (debug)
1534                                         fprintf(stderr, "setting OP_MASKLEN\n");
1535                                 opt_flags |= OP_MASKLEN;
1536                         }
1537                         if (grp->gr_type != GT_NULL) {
1538                                 syslog(LOG_ERR, "network/host conflict");
1539                                 return (1);
1540                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1541                                 syslog(LOG_ERR, "bad net: %s", cpoptarg);
1542                                 return (1);
1543                         }
1544                         grp->gr_type = GT_NET;
1545                         *has_hostp = 1;
1546                         usedarg++;
1547                         opt_flags |= OP_NET;
1548                 } else if (!strcmp(cpopt, "alldirs")) {
1549                         opt_flags |= OP_ALLDIRS;
1550                 } else if (!strcmp(cpopt, "public")) {
1551                         *exflagsp |= MNT_EXPUBLIC;
1552                 } else if (!strcmp(cpopt, "webnfs")) {
1553                         *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1554                         opt_flags |= OP_MAPALL;
1555                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1556                         ep->ex_indexfile = strdup(cpoptarg);
1557                 } else {
1558                         syslog(LOG_ERR, "bad opt %s", cpopt);
1559                         return (1);
1560                 }
1561                 if (usedarg >= 0) {
1562                         *endcp = savedc2;
1563                         **endcpp = savedc;
1564                         if (usedarg > 0) {
1565                                 *cpp = cp;
1566                                 *endcpp = endcp;
1567                         }
1568                         return (0);
1569                 }
1570                 cpopt = cpoptend;
1571         }
1572         **endcpp = savedc;
1573         return (0);
1574 }
1575
1576 /*
1577  * Translate a character string to the corresponding list of network
1578  * addresses for a hostname.
1579  */
1580 int
1581 get_host(cp, grp, tgrp)
1582         char *cp;
1583         struct grouplist *grp;
1584         struct grouplist *tgrp;
1585 {
1586         struct grouplist *checkgrp;
1587         struct addrinfo *ai, *tai, hints;
1588         int ecode;
1589         char host[NI_MAXHOST];
1590
1591         if (grp->gr_type != GT_NULL) {
1592                 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
1593                 return (1);
1594         }
1595         memset(&hints, 0, sizeof hints);
1596         hints.ai_flags = AI_CANONNAME;
1597         hints.ai_protocol = IPPROTO_UDP;
1598         ecode = getaddrinfo(cp, NULL, &hints, &ai);
1599         if (ecode != 0) {
1600                 syslog(LOG_ERR,"can't get address info for host %s", cp);
1601                 return 1;
1602         }
1603         grp->gr_ptr.gt_addrinfo = ai;
1604         while (ai != NULL) {
1605                 if (ai->ai_canonname == NULL) {
1606                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1607                             sizeof host, NULL, 0, ninumeric) != 0)
1608                                 strlcpy(host, "?", sizeof(host));
1609                         ai->ai_canonname = strdup(host);
1610                         ai->ai_flags |= AI_CANONNAME;
1611                 }
1612                 if (debug)
1613                         fprintf(stderr, "got host %s\n", ai->ai_canonname);
1614                 /*
1615                  * Sanity check: make sure we don't already have an entry
1616                  * for this host in the grouplist.
1617                  */
1618                 for (checkgrp = tgrp; checkgrp != NULL;
1619                     checkgrp = checkgrp->gr_next) {
1620                         if (checkgrp->gr_type != GT_HOST)
1621                                 continue;
1622                         for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
1623                             tai = tai->ai_next) {
1624                                 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
1625                                         continue;
1626                                 if (debug)
1627                                         fprintf(stderr,
1628                                             "ignoring duplicate host %s\n",
1629                                             ai->ai_canonname);
1630                                 grp->gr_type = GT_IGNORE;
1631                                 return (0);
1632                         }
1633                 }
1634                 ai = ai->ai_next;
1635         }
1636         grp->gr_type = GT_HOST;
1637         return (0);
1638 }
1639
1640 /*
1641  * Free up an exports list component
1642  */
1643 void
1644 free_exp(ep)
1645         struct exportlist *ep;
1646 {
1647
1648         if (ep->ex_defdir) {
1649                 free_host(ep->ex_defdir->dp_hosts);
1650                 free((caddr_t)ep->ex_defdir);
1651         }
1652         if (ep->ex_fsdir)
1653                 free(ep->ex_fsdir);
1654         if (ep->ex_indexfile)
1655                 free(ep->ex_indexfile);
1656         free_dir(ep->ex_dirl);
1657         free((caddr_t)ep);
1658 }
1659
1660 /*
1661  * Free hosts.
1662  */
1663 void
1664 free_host(hp)
1665         struct hostlist *hp;
1666 {
1667         struct hostlist *hp2;
1668
1669         while (hp) {
1670                 hp2 = hp;
1671                 hp = hp->ht_next;
1672                 free((caddr_t)hp2);
1673         }
1674 }
1675
1676 struct hostlist *
1677 get_ht()
1678 {
1679         struct hostlist *hp;
1680
1681         hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1682         if (hp == (struct hostlist *)NULL)
1683                 out_of_mem();
1684         hp->ht_next = (struct hostlist *)NULL;
1685         hp->ht_flag = 0;
1686         return (hp);
1687 }
1688
1689 /*
1690  * Out of memory, fatal
1691  */
1692 void
1693 out_of_mem()
1694 {
1695
1696         syslog(LOG_ERR, "out of memory");
1697         exit(2);
1698 }
1699
1700 /*
1701  * Do the mount syscall with the update flag to push the export info into
1702  * the kernel.
1703  */
1704 int
1705 do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1706         struct exportlist *ep;
1707         struct grouplist *grp;
1708         int exflags;
1709         struct xucred *anoncrp;
1710         char *dirp;
1711         int dirplen;
1712         struct statfs *fsb;
1713 {
1714         struct statfs fsb1;
1715         struct addrinfo *ai;
1716         struct export_args *eap;
1717         char *cp = NULL;
1718         int done;
1719         char savedc = '\0';
1720         union {
1721                 struct ufs_args ua;
1722                 struct iso_args ia;
1723                 struct msdosfs_args da;
1724                 struct ntfs_args na;
1725         } args;
1726
1727         bzero(&args, sizeof args);
1728         /* XXX, we assume that all xx_args look like ufs_args. */
1729         args.ua.fspec = 0;
1730         eap = &args.ua.export;
1731
1732         eap->ex_flags = exflags;
1733         eap->ex_anon = *anoncrp;
1734         eap->ex_indexfile = ep->ex_indexfile;
1735         if (grp->gr_type == GT_HOST)
1736                 ai = grp->gr_ptr.gt_addrinfo;
1737         else
1738                 ai = NULL;
1739         done = FALSE;
1740         while (!done) {
1741                 switch (grp->gr_type) {
1742                 case GT_HOST:
1743                         if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
1744                                 goto skip;
1745                         eap->ex_addr = ai->ai_addr;
1746                         eap->ex_addrlen = ai->ai_addrlen;
1747                         eap->ex_masklen = 0;
1748                         break;
1749                 case GT_NET:
1750                         if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
1751                             have_v6 == 0)
1752                                 goto skip;
1753                         eap->ex_addr =
1754                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
1755                         eap->ex_addrlen = args.ua.export.ex_addr->sa_len;
1756                         eap->ex_mask =
1757                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
1758                         eap->ex_masklen = args.ua.export.ex_mask->sa_len;
1759                         break;
1760                 case GT_DEFAULT:
1761                         eap->ex_addr = NULL;
1762                         eap->ex_addrlen = 0;
1763                         eap->ex_mask = NULL;
1764                         eap->ex_masklen = 0;
1765                         break;
1766                 case GT_IGNORE:
1767                         return(0);
1768                         break;
1769                 default:
1770                         syslog(LOG_ERR, "bad grouptype");
1771                         if (cp)
1772                                 *cp = savedc;
1773                         return (1);
1774                 };
1775
1776                 /*
1777                  * XXX:
1778                  * Maybe I should just use the fsb->f_mntonname path instead
1779                  * of looping back up the dirp to the mount point??
1780                  * Also, needs to know how to export all types of local
1781                  * exportable file systems and not just "ufs".
1782                  */
1783                 while (mount(fsb->f_fstypename, dirp,
1784                     fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1785                         if (cp)
1786                                 *cp-- = savedc;
1787                         else
1788                                 cp = dirp + dirplen - 1;
1789                         if (errno == EPERM) {
1790                                 if (debug)
1791                                         warnx("can't change attributes for %s",
1792                                             dirp);
1793                                 syslog(LOG_ERR,
1794                                    "can't change attributes for %s", dirp);
1795                                 return (1);
1796                         }
1797                         if (opt_flags & OP_ALLDIRS) {
1798                                 syslog(LOG_ERR, "could not remount %s: %m",
1799                                         dirp);
1800                                 return (1);
1801                         }
1802                         /* back up over the last component */
1803                         while (*cp == '/' && cp > dirp)
1804                                 cp--;
1805                         while (*(cp - 1) != '/' && cp > dirp)
1806                                 cp--;
1807                         if (cp == dirp) {
1808                                 if (debug)
1809                                         warnx("mnt unsucc");
1810                                 syslog(LOG_ERR, "can't export %s", dirp);
1811                                 return (1);
1812                         }
1813                         savedc = *cp;
1814                         *cp = '\0';
1815                         /* Check that we're still on the same filesystem. */
1816                         if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
1817                             &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
1818                                 *cp = savedc;
1819                                 syslog(LOG_ERR, "can't export %s", dirp);
1820                                 return (1);
1821                         }
1822                 }
1823 skip:
1824                 if (ai != NULL)
1825                         ai = ai->ai_next;
1826                 if (ai == NULL)
1827                         done = TRUE;
1828         }
1829         if (cp)
1830                 *cp = savedc;
1831         return (0);
1832 }
1833
1834 /*
1835  * Translate a net address.
1836  *
1837  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
1838  */
1839 int
1840 get_net(cp, net, maskflg)
1841         char *cp;
1842         struct netmsk *net;
1843         int maskflg;
1844 {
1845         struct netent *np = NULL;
1846         char *name, *p, *prefp;
1847         struct sockaddr_in sin;
1848         struct sockaddr *sa = NULL;
1849         struct addrinfo hints, *ai = NULL;
1850         char netname[NI_MAXHOST];
1851         long preflen;
1852
1853         p = prefp = NULL;
1854         if ((opt_flags & OP_MASKLEN) && !maskflg) {
1855                 p = strchr(cp, '/');
1856                 *p = '\0';
1857                 prefp = p + 1;
1858         }
1859
1860         /*
1861          * Check for a numeric address first. We wish to avoid
1862          * possible DNS lookups in getnetbyname().
1863          */
1864         if (isxdigit(*cp) || *cp == ':') {
1865                 memset(&hints, 0, sizeof hints);
1866                 /* Ensure the mask and the network have the same family. */
1867                 if (maskflg && (opt_flags & OP_NET))
1868                         hints.ai_family = net->nt_net.ss_family;
1869                 else if (!maskflg && (opt_flags & OP_HAVEMASK))
1870                         hints.ai_family = net->nt_mask.ss_family;
1871                 else
1872                         hints.ai_family = AF_UNSPEC;
1873                 hints.ai_flags = AI_NUMERICHOST;
1874                 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
1875                         sa = ai->ai_addr;
1876                 if (sa != NULL && ai->ai_family == AF_INET) {
1877                         /*
1878                          * The address in `cp' is really a network address, so
1879                          * use inet_network() to re-interpret this correctly.
1880                          * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
1881                          */
1882                         bzero(&sin, sizeof sin);
1883                         sin.sin_family = AF_INET;
1884                         sin.sin_len = sizeof sin;
1885                         sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
1886                         if (debug)
1887                                 fprintf(stderr, "get_net: v4 addr %s\n",
1888                                     inet_ntoa(sin.sin_addr));
1889                         sa = (struct sockaddr *)&sin;
1890                 }
1891         }
1892         if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
1893                 bzero(&sin, sizeof sin);
1894                 sin.sin_family = AF_INET;
1895                 sin.sin_len = sizeof sin;
1896                 sin.sin_addr = inet_makeaddr(np->n_net, 0);
1897                 sa = (struct sockaddr *)&sin;
1898         }
1899         if (sa == NULL)
1900                 goto fail;
1901
1902         if (maskflg) {
1903                 /* The specified sockaddr is a mask. */
1904                 if (checkmask(sa) != 0)
1905                         goto fail;
1906                 bcopy(sa, &net->nt_mask, sa->sa_len);
1907                 opt_flags |= OP_HAVEMASK;
1908         } else {
1909                 /* The specified sockaddr is a network address. */
1910                 bcopy(sa, &net->nt_net, sa->sa_len);
1911
1912                 /* Get a network name for the export list. */
1913                 if (np) {
1914                         name = np->n_name;
1915                 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
1916                    NULL, 0, ninumeric) == 0) {
1917                         name = netname;
1918                 } else {
1919                         goto fail;
1920                 }
1921                 if ((net->nt_name = strdup(name)) == NULL)
1922                         out_of_mem();
1923
1924                 /*
1925                  * Extract a mask from either a "/<masklen>" suffix, or
1926                  * from the class of an IPv4 address.
1927                  */
1928                 if (opt_flags & OP_MASKLEN) {
1929                         preflen = strtol(prefp, NULL, 10);
1930                         if (preflen < 0L || preflen == LONG_MAX)
1931                                 goto fail;
1932                         bcopy(sa, &net->nt_mask, sa->sa_len);
1933                         if (makemask(&net->nt_mask, (int)preflen) != 0)
1934                                 goto fail;
1935                         opt_flags |= OP_HAVEMASK;
1936                         *p = '/';
1937                 } else if (sa->sa_family == AF_INET &&
1938                     (opt_flags & OP_MASK) == 0) {
1939                         in_addr_t addr;
1940
1941                         addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
1942                         if (IN_CLASSA(addr))
1943                                 preflen = 8;
1944                         else if (IN_CLASSB(addr))
1945                                 preflen = 16;
1946                         else if (IN_CLASSC(addr))
1947                                 preflen = 24;
1948                         else if (IN_CLASSD(addr))
1949                                 preflen = 28;
1950                         else
1951                                 preflen = 32;   /* XXX */
1952
1953                         bcopy(sa, &net->nt_mask, sa->sa_len);
1954                         makemask(&net->nt_mask, (int)preflen);
1955                         opt_flags |= OP_HAVEMASK;
1956                 }
1957         }
1958
1959         if (ai)
1960                 freeaddrinfo(ai);
1961         return 0;
1962
1963 fail:
1964         if (ai)
1965                 freeaddrinfo(ai);
1966         return 1;
1967 }
1968
1969 /*
1970  * Parse out the next white space separated field
1971  */
1972 void
1973 nextfield(cp, endcp)
1974         char **cp;
1975         char **endcp;
1976 {
1977         char *p;
1978
1979         p = *cp;
1980         while (*p == ' ' || *p == '\t')
1981                 p++;
1982         if (*p == '\n' || *p == '\0')
1983                 *cp = *endcp = p;
1984         else {
1985                 *cp = p++;
1986                 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1987                         p++;
1988                 *endcp = p;
1989         }
1990 }
1991
1992 /*
1993  * Get an exports file line. Skip over blank lines and handle line
1994  * continuations.
1995  */
1996 int
1997 get_line()
1998 {
1999         char *p, *cp;
2000         int len;
2001         int totlen, cont_line;
2002
2003         /*
2004          * Loop around ignoring blank lines and getting all continuation lines.
2005          */
2006         p = line;
2007         totlen = 0;
2008         do {
2009                 if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
2010                         return (0);
2011                 len = strlen(p);
2012                 cp = p + len - 1;
2013                 cont_line = 0;
2014                 while (cp >= p &&
2015                     (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2016                         if (*cp == '\\')
2017                                 cont_line = 1;
2018                         cp--;
2019                         len--;
2020                 }
2021                 if (cont_line) {
2022                         *++cp = ' ';
2023                         len++;
2024                 }
2025                 *++cp = '\0';
2026                 if (len > 0) {
2027                         totlen += len;
2028                         if (totlen >= LINESIZ) {
2029                                 syslog(LOG_ERR, "exports line too long");
2030                                 exit(2);
2031                         }
2032                         p = cp;
2033                 }
2034         } while (totlen == 0 || cont_line);
2035         return (1);
2036 }
2037
2038 /*
2039  * Parse a description of a credential.
2040  */
2041 void
2042 parsecred(namelist, cr)
2043         char *namelist;
2044         struct xucred *cr;
2045 {
2046         char *name;
2047         int cnt;
2048         char *names;
2049         struct passwd *pw;
2050         struct group *gr;
2051         int ngroups, groups[NGROUPS + 1];
2052
2053         cr->cr_version = XUCRED_VERSION;
2054         /*
2055          * Set up the unprivileged user.
2056          */
2057         cr->cr_uid = -2;
2058         cr->cr_groups[0] = -2;
2059         cr->cr_ngroups = 1;
2060         /*
2061          * Get the user's password table entry.
2062          */
2063         names = strsep(&namelist, " \t\n");
2064         name = strsep(&names, ":");
2065         if (isdigit(*name) || *name == '-')
2066                 pw = getpwuid(atoi(name));
2067         else
2068                 pw = getpwnam(name);
2069         /*
2070          * Credentials specified as those of a user.
2071          */
2072         if (names == NULL) {
2073                 if (pw == NULL) {
2074                         syslog(LOG_ERR, "unknown user: %s", name);
2075                         return;
2076                 }
2077                 cr->cr_uid = pw->pw_uid;
2078                 ngroups = NGROUPS + 1;
2079                 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2080                         syslog(LOG_ERR, "too many groups");
2081                 /*
2082                  * Convert from int's to gid_t's and compress out duplicate
2083                  */
2084                 cr->cr_ngroups = ngroups - 1;
2085                 cr->cr_groups[0] = groups[0];
2086                 for (cnt = 2; cnt < ngroups; cnt++)
2087                         cr->cr_groups[cnt - 1] = groups[cnt];
2088                 return;
2089         }
2090         /*
2091          * Explicit credential specified as a colon separated list:
2092          *      uid:gid:gid:...
2093          */
2094         if (pw != NULL)
2095                 cr->cr_uid = pw->pw_uid;
2096         else if (isdigit(*name) || *name == '-')
2097                 cr->cr_uid = atoi(name);
2098         else {
2099                 syslog(LOG_ERR, "unknown user: %s", name);
2100                 return;
2101         }
2102         cr->cr_ngroups = 0;
2103         while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2104                 name = strsep(&names, ":");
2105                 if (isdigit(*name) || *name == '-') {
2106                         cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2107                 } else {
2108                         if ((gr = getgrnam(name)) == NULL) {
2109                                 syslog(LOG_ERR, "unknown group: %s", name);
2110                                 continue;
2111                         }
2112                         cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2113                 }
2114         }
2115         if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2116                 syslog(LOG_ERR, "too many groups");
2117 }
2118
2119 #define STRSIZ  (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2120 /*
2121  * Routines that maintain the remote mounttab
2122  */
2123 void
2124 get_mountlist()
2125 {
2126         struct mountlist *mlp, **mlpp;
2127         char *host, *dirp, *cp;
2128         char str[STRSIZ];
2129         FILE *mlfile;
2130
2131         if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2132                 if (errno == ENOENT)
2133                         return;
2134                 else {
2135                         syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2136                         return;
2137                 }
2138         }
2139         mlpp = &mlhead;
2140         while (fgets(str, STRSIZ, mlfile) != NULL) {
2141                 cp = str;
2142                 host = strsep(&cp, " \t\n");
2143                 dirp = strsep(&cp, " \t\n");
2144                 if (host == NULL || dirp == NULL)
2145                         continue;
2146                 mlp = (struct mountlist *)malloc(sizeof (*mlp));
2147                 if (mlp == (struct mountlist *)NULL)
2148                         out_of_mem();
2149                 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2150                 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2151                 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2152                 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2153                 mlp->ml_next = (struct mountlist *)NULL;
2154                 *mlpp = mlp;
2155                 mlpp = &mlp->ml_next;
2156         }
2157         fclose(mlfile);
2158 }
2159
2160 void
2161 del_mlist(char *hostp, char *dirp)
2162 {
2163         struct mountlist *mlp, **mlpp;
2164         struct mountlist *mlp2;
2165         FILE *mlfile;
2166         int fnd = 0;
2167
2168         mlpp = &mlhead;
2169         mlp = mlhead;
2170         while (mlp) {
2171                 if (!strcmp(mlp->ml_host, hostp) &&
2172                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2173                         fnd = 1;
2174                         mlp2 = mlp;
2175                         *mlpp = mlp = mlp->ml_next;
2176                         free((caddr_t)mlp2);
2177                 } else {
2178                         mlpp = &mlp->ml_next;
2179                         mlp = mlp->ml_next;
2180                 }
2181         }
2182         if (fnd) {
2183                 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2184                         syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2185                         return;
2186                 }
2187                 mlp = mlhead;
2188                 while (mlp) {
2189                         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2190                         mlp = mlp->ml_next;
2191                 }
2192                 fclose(mlfile);
2193         }
2194 }
2195
2196 void
2197 add_mlist(hostp, dirp)
2198         char *hostp, *dirp;
2199 {
2200         struct mountlist *mlp, **mlpp;
2201         FILE *mlfile;
2202
2203         mlpp = &mlhead;
2204         mlp = mlhead;
2205         while (mlp) {
2206                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2207                         return;
2208                 mlpp = &mlp->ml_next;
2209                 mlp = mlp->ml_next;
2210         }
2211         mlp = (struct mountlist *)malloc(sizeof (*mlp));
2212         if (mlp == (struct mountlist *)NULL)
2213                 out_of_mem();
2214         strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2215         mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2216         strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2217         mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2218         mlp->ml_next = (struct mountlist *)NULL;
2219         *mlpp = mlp;
2220         if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2221                 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2222                 return;
2223         }
2224         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2225         fclose(mlfile);
2226 }
2227
2228 /*
2229  * Free up a group list.
2230  */
2231 void
2232 free_grp(grp)
2233         struct grouplist *grp;
2234 {
2235         if (grp->gr_type == GT_HOST) {
2236                 if (grp->gr_ptr.gt_addrinfo != NULL)
2237                         freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2238         } else if (grp->gr_type == GT_NET) {
2239                 if (grp->gr_ptr.gt_net.nt_name)
2240                         free(grp->gr_ptr.gt_net.nt_name);
2241         }
2242         free((caddr_t)grp);
2243 }
2244
2245 #ifdef DEBUG
2246 void
2247 SYSLOG(int pri, const char *fmt, ...)
2248 {
2249         va_list ap;
2250
2251         va_start(ap, fmt);
2252         vfprintf(stderr, fmt, ap);
2253         va_end(ap);
2254 }
2255 #endif /* DEBUG */
2256
2257 /*
2258  * Check options for consistency.
2259  */
2260 int
2261 check_options(dp)
2262         struct dirlist *dp;
2263 {
2264
2265         if (dp == (struct dirlist *)NULL)
2266             return (1);
2267         if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2268             syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2269             return (1);
2270         }
2271         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2272                 syslog(LOG_ERR, "-mask requires -network");
2273                 return (1);
2274         }
2275         if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2276                 syslog(LOG_ERR, "-network requires mask specification");
2277                 return (1);
2278         }
2279         if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
2280                 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
2281                 return (1);
2282         }
2283         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2284             syslog(LOG_ERR, "-alldirs has multiple directories");
2285             return (1);
2286         }
2287         return (0);
2288 }
2289
2290 /*
2291  * Check an absolute directory path for any symbolic links. Return true
2292  */
2293 int
2294 check_dirpath(dirp)
2295         char *dirp;
2296 {
2297         char *cp;
2298         int ret = 1;
2299         struct stat sb;
2300
2301         cp = dirp + 1;
2302         while (*cp && ret) {
2303                 if (*cp == '/') {
2304                         *cp = '\0';
2305                         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2306                                 ret = 0;
2307                         *cp = '/';
2308                 }
2309                 cp++;
2310         }
2311         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2312                 ret = 0;
2313         return (ret);
2314 }
2315
2316 /*
2317  * Make a netmask according to the specified prefix length. The ss_family
2318  * and other non-address fields must be initialised before calling this.
2319  */
2320 int
2321 makemask(struct sockaddr_storage *ssp, int bitlen)
2322 {
2323         u_char *p;
2324         int bits, i, len;
2325
2326         if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
2327                 return (-1);
2328         if (bitlen > len * NBBY)
2329                 return (-1);
2330
2331         for (i = 0; i < len; i++) {
2332                 bits = (bitlen > NBBY) ? NBBY : bitlen;
2333                 *p++ = (1 << bits) - 1;
2334                 bitlen -= bits;
2335         }
2336         return 0;
2337 }
2338
2339 /*
2340  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
2341  * is acceptable (i.e. of the form 1...10....0).
2342  */
2343 int
2344 checkmask(struct sockaddr *sa)
2345 {
2346         u_char *mask;
2347         int i, len;
2348
2349         if ((mask = sa_rawaddr(sa, &len)) == NULL)
2350                 return (-1);
2351
2352         for (i = 0; i < len; i++)
2353                 if (mask[i] != 0xff)
2354                         break;
2355         if (i < len) {
2356                 if (~mask[i] & (u_char)(~mask[i] + 1))
2357                         return (-1);
2358                 i++;
2359         }
2360         for (; i < len; i++)
2361                 if (mask[i] != 0)
2362                         return (-1);
2363         return (0);
2364 }
2365
2366 /*
2367  * Compare two sockaddrs according to a specified mask. Return zero if
2368  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
2369  * If samask is NULL, perform a full comparision.
2370  */
2371 int
2372 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
2373 {
2374         unsigned char *p1, *p2, *mask;
2375         int len, i;
2376
2377         if (sa1->sa_family != sa2->sa_family ||
2378             (p1 = sa_rawaddr(sa1, &len)) == NULL ||
2379             (p2 = sa_rawaddr(sa2, NULL)) == NULL)
2380                 return (1);
2381
2382         switch (sa1->sa_family) {
2383         case AF_INET6:
2384                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
2385                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
2386                         return (1);
2387                 break;
2388         }
2389
2390         /* Simple binary comparison if no mask specified. */
2391         if (samask == NULL)
2392                 return (memcmp(p1, p2, len));
2393
2394         /* Set up the mask, and do a mask-based comparison. */
2395         if (sa1->sa_family != samask->sa_family ||
2396             (mask = sa_rawaddr(samask, NULL)) == NULL)
2397                 return (1);
2398
2399         for (i = 0; i < len; i++)
2400                 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
2401                         return (1);
2402         return (0);
2403 }
2404
2405 /*
2406  * Return a pointer to the part of the sockaddr that contains the
2407  * raw address, and set *nbytes to its length in bytes. Returns
2408  * NULL if the address family is unknown.
2409  */
2410 void *
2411 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
2412         void *p;
2413         int len;
2414
2415         switch (sa->sa_family) {
2416         case AF_INET:
2417                 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
2418                 p = &((struct sockaddr_in *)sa)->sin_addr;
2419                 break;
2420         case AF_INET6:
2421                 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
2422                 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
2423                 break;
2424         default:
2425                 p = NULL;
2426                 len = 0;
2427         }
2428
2429         if (nbytes != NULL)
2430                 *nbytes = len;
2431         return (p);
2432 }
2433
2434 void
2435 huphandler(int sig)
2436 {
2437         got_sighup = 1;
2438 }
2439
2440 void terminate(sig)
2441 int sig;
2442 {
2443         close(mountdlockfd);
2444         unlink(MOUNTDLOCK);
2445         rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
2446         rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
2447         exit (0);
2448 }