Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.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  * @(#) Copyright (c) 1989, 1993 The Regents of the University of California.  All rights reserved.
37  * @(#)mountd.c 8.15 (Berkeley) 5/1/95
38  * $FreeBSD: src/sbin/mountd/mountd.c,v 1.39.2.5 2002/09/13 15:57:43 joerg Exp $
39  * $DragonFly: src/sbin/mountd/mountd.c,v 1.2 2003/06/17 04:27:34 dillon Exp $
40  */
41
42 #include <sys/param.h>
43 #include <sys/mount.h>
44 #include <sys/stat.h>
45 #include <sys/syslog.h>
46 #include <sys/sysctl.h>
47
48 #include <rpc/rpc.h>
49 #include <rpc/pmap_clnt.h>
50 #include <nfs/rpcv2.h>
51 #include <nfs/nfsproto.h>
52 #include <nfs/nfs.h>
53 #include <ufs/ufs/ufsmount.h>
54 #include <msdosfs/msdosfsmount.h>
55 #include <ntfs/ntfsmount.h>
56 #include <isofs/cd9660/cd9660_mount.h>  /* XXX need isofs in include */
57
58 #include <arpa/inet.h>
59
60 #include <ctype.h>
61 #include <err.h>
62 #include <errno.h>
63 #include <grp.h>
64 #include <netdb.h>
65 #include <pwd.h>
66 #include <signal.h>
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <unistd.h>
71 #include "pathnames.h"
72
73 #ifdef DEBUG
74 #include <stdarg.h>
75 #endif
76
77 /*
78  * Structures for keeping the mount list and export list
79  */
80 struct mountlist {
81         struct mountlist *ml_next;
82         char    ml_host[RPCMNT_NAMELEN+1];
83         char    ml_dirp[RPCMNT_PATHLEN+1];
84 };
85
86 struct dirlist {
87         struct dirlist  *dp_left;
88         struct dirlist  *dp_right;
89         int             dp_flag;
90         struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
91         char            dp_dirp[1];     /* Actually malloc'd to size of dir */
92 };
93 /* dp_flag bits */
94 #define DP_DEFSET       0x1
95 #define DP_HOSTSET      0x2
96 #define DP_KERB         0x4
97
98 struct exportlist {
99         struct exportlist *ex_next;
100         struct dirlist  *ex_dirl;
101         struct dirlist  *ex_defdir;
102         int             ex_flag;
103         fsid_t          ex_fs;
104         char            *ex_fsdir;
105         char            *ex_indexfile;
106 };
107 /* ex_flag bits */
108 #define EX_LINKED       0x1
109
110 struct netmsk {
111         u_int32_t       nt_net;
112         u_int32_t       nt_mask;
113         char            *nt_name;
114 };
115
116 union grouptypes {
117         struct hostent *gt_hostent;
118         struct netmsk   gt_net;
119 };
120
121 struct grouplist {
122         int gr_type;
123         union grouptypes gr_ptr;
124         struct grouplist *gr_next;
125 };
126 /* Group types */
127 #define GT_NULL         0x0
128 #define GT_HOST         0x1
129 #define GT_NET          0x2
130 #define GT_IGNORE       0x5
131
132 struct hostlist {
133         int              ht_flag;       /* Uses DP_xx bits */
134         struct grouplist *ht_grp;
135         struct hostlist  *ht_next;
136 };
137
138 struct fhreturn {
139         int     fhr_flag;
140         int     fhr_vers;
141         nfsfh_t fhr_fh;
142 };
143
144 /* Global defs */
145 char    *add_expdir __P((struct dirlist **, char *, int));
146 void    add_dlist __P((struct dirlist **, struct dirlist *,
147                                 struct grouplist *, int));
148 void    add_mlist __P((char *, char *));
149 int     check_dirpath __P((char *));
150 int     check_options __P((struct dirlist *));
151 int     chk_host __P((struct dirlist *, u_int32_t, int *, int *));
152 void    del_mlist __P((char *, char *));
153 struct dirlist *dirp_search __P((struct dirlist *, char *));
154 int     do_mount __P((struct exportlist *, struct grouplist *, int,
155                 struct ucred *, char *, int, struct statfs *));
156 int     do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
157                                 int *, int *, struct ucred *));
158 struct  exportlist *ex_search __P((fsid_t *));
159 struct  exportlist *get_exp __P((void));
160 void    free_dir __P((struct dirlist *));
161 void    free_exp __P((struct exportlist *));
162 void    free_grp __P((struct grouplist *));
163 void    free_host __P((struct hostlist *));
164 void    get_exportlist __P((void));
165 int     get_host __P((char *, struct grouplist *, struct grouplist *));
166 int     get_num __P((char *));
167 struct hostlist *get_ht __P((void));
168 int     get_line __P((void));
169 void    get_mountlist __P((void));
170 int     get_net __P((char *, struct netmsk *, int));
171 void    getexp_err __P((struct exportlist *, struct grouplist *));
172 struct grouplist *get_grp __P((void));
173 void    hang_dirp __P((struct dirlist *, struct grouplist *,
174                                 struct exportlist *, int));
175 void    mntsrv __P((struct svc_req *, SVCXPRT *));
176 void    nextfield __P((char **, char **));
177 void    out_of_mem __P((void));
178 void    parsecred __P((char *, struct ucred *));
179 int     put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
180 int     scan_tree __P((struct dirlist *, u_int32_t));
181 static void usage __P((void));
182 int     xdr_dir __P((XDR *, char *));
183 int     xdr_explist __P((XDR *, caddr_t));
184 int     xdr_fhs __P((XDR *, caddr_t));
185 int     xdr_mlist __P((XDR *, caddr_t));
186
187 struct exportlist *exphead;
188 struct mountlist *mlhead;
189 struct grouplist *grphead;
190 char exname[MAXPATHLEN];
191 struct ucred def_anon = {
192         1,
193         (uid_t) -2,
194         1,
195         { (gid_t) -2 }
196 };
197 int force_v2 = 0;
198 int resvport_only = 1;
199 int dir_only = 1;
200 int log = 0;
201 int opt_flags;
202 /* Bits for above */
203 #define OP_MAPROOT      0x01
204 #define OP_MAPALL       0x02
205 #define OP_KERB         0x04
206 #define OP_MASK         0x08
207 #define OP_NET          0x10
208 #define OP_ALLDIRS      0x40
209 /* 0x80 is OP_HAVEMASK in FreeBSD 5+ */
210 #define OP_QUIET        0x100
211
212 #ifdef DEBUG
213 int debug = 1;
214 void    SYSLOG __P((int, const char *, ...));
215 #define syslog SYSLOG
216 #else
217 int debug = 0;
218 #endif
219
220 /*
221  * Mountd server for NFS mount protocol as described in:
222  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
223  * The optional arguments are the exports file name
224  * default: _PATH_EXPORTS
225  * and "-n" to allow nonroot mount.
226  */
227 int
228 main(argc, argv)
229         int argc;
230         char **argv;
231 {
232         SVCXPRT *udptransp, *tcptransp;
233         int c, error, mib[3];
234         struct vfsconf vfc;
235
236         error = getvfsbyname("nfs", &vfc);
237         if (error && vfsisloadable("nfs")) {
238                 if(vfsload("nfs"))
239                         err(1, "vfsload(nfs)");
240                 endvfsent();    /* flush cache */
241                 error = getvfsbyname("nfs", &vfc);
242         }
243         if (error)
244                 errx(1, "NFS support is not available in the running kernel");
245
246         while ((c = getopt(argc, argv, "2dlnr")) != -1)
247                 switch (c) {
248                 case '2':
249                         force_v2 = 1;
250                         break;
251                 case 'n':
252                         resvport_only = 0;
253                         break;
254                 case 'r':
255                         dir_only = 0;
256                         break;
257                 case 'd':
258                         debug = debug ? 0 : 1;
259                         break;
260                 case 'l':
261                         log = 1;
262                         break;
263                 default:
264                         usage();
265                 };
266         argc -= optind;
267         argv += optind;
268         grphead = (struct grouplist *)NULL;
269         exphead = (struct exportlist *)NULL;
270         mlhead = (struct mountlist *)NULL;
271         if (argc == 1) {
272                 strncpy(exname, *argv, MAXPATHLEN-1);
273                 exname[MAXPATHLEN-1] = '\0';
274         } else
275                 strcpy(exname, _PATH_EXPORTS);
276         openlog("mountd", LOG_PID, LOG_DAEMON);
277         if (debug)
278                 warnx("getting export list");
279         get_exportlist();
280         if (debug)
281                 warnx("getting mount list");
282         get_mountlist();
283         if (debug)
284                 warnx("here we go");
285         if (debug == 0) {
286                 daemon(0, 0);
287                 signal(SIGINT, SIG_IGN);
288                 signal(SIGQUIT, SIG_IGN);
289         }
290         signal(SIGHUP, (void (*) __P((int))) get_exportlist);
291         { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
292           if (pidfile != NULL) {
293                 fprintf(pidfile, "%d\n", getpid());
294                 fclose(pidfile);
295           }
296         }
297         if (!resvport_only) {
298                 mib[0] = CTL_VFS;
299                 mib[1] = vfc.vfc_typenum;
300                 mib[2] = NFS_NFSPRIVPORT;
301                 if (sysctl(mib, 3, NULL, NULL, &resvport_only,
302                     sizeof(resvport_only)) != 0 && errno != ENOENT) {
303                         syslog(LOG_ERR, "sysctl: %m");
304                         exit(1);
305                 }
306         }
307         if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
308             (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
309                 syslog(LOG_ERR, "can't create socket");
310                 exit(1);
311         }
312         pmap_unset(RPCPROG_MNT, 1);
313         pmap_unset(RPCPROG_MNT, 3);
314         if (!force_v2)
315                 if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
316                     !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
317                         syslog(LOG_ERR, "can't register mount");
318                         exit(1);
319                 }
320         if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
321             !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) {
322                 syslog(LOG_ERR, "can't register mount");
323                 exit(1);
324         }
325         svc_run();
326         syslog(LOG_ERR, "mountd died");
327         exit(1);
328 }
329
330 static void
331 usage()
332 {
333         fprintf(stderr,
334                 "usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
335         exit(1);
336 }
337
338 /*
339  * The mount rpc service
340  */
341 void
342 mntsrv(rqstp, transp)
343         struct svc_req *rqstp;
344         SVCXPRT *transp;
345 {
346         struct exportlist *ep;
347         struct dirlist *dp;
348         struct fhreturn fhr;
349         struct stat stb;
350         struct statfs fsb;
351         struct hostent *hp;
352         struct in_addr saddrin;
353         u_int32_t saddr;
354         u_short sport;
355         char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
356         int bad = 0, defset, hostset;
357         sigset_t sighup_mask;
358
359         sigemptyset(&sighup_mask);
360         sigaddset(&sighup_mask, SIGHUP);
361         saddr = transp->xp_raddr.sin_addr.s_addr;
362         saddrin = transp->xp_raddr.sin_addr;
363         sport = ntohs(transp->xp_raddr.sin_port);
364         hp = (struct hostent *)NULL;
365         switch (rqstp->rq_proc) {
366         case NULLPROC:
367                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
368                         syslog(LOG_ERR, "can't send reply");
369                 return;
370         case RPCMNT_MOUNT:
371                 if (sport >= IPPORT_RESERVED && resvport_only) {
372                         syslog(LOG_NOTICE,
373                             "mount request from %s from unprivileged port",
374                             inet_ntoa(saddrin));
375                         svcerr_weakauth(transp);
376                         return;
377                 }
378                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
379                         syslog(LOG_NOTICE, "undecodable mount request from %s",
380                             inet_ntoa(saddrin));
381                         svcerr_decode(transp);
382                         return;
383                 }
384
385                 /*
386                  * Get the real pathname and make sure it is a directory
387                  * or a regular file if the -r option was specified
388                  * and it exists.
389                  */
390                 if (realpath(rpcpath, dirpath) == NULL ||
391                     stat(dirpath, &stb) < 0 ||
392                     (!S_ISDIR(stb.st_mode) &&
393                      (dir_only || !S_ISREG(stb.st_mode))) ||
394                     statfs(dirpath, &fsb) < 0) {
395                         chdir("/");     /* Just in case realpath doesn't */
396                         syslog(LOG_NOTICE,
397                             "mount request from %s for non existent path %s",
398                             inet_ntoa(saddrin), dirpath);
399                         if (debug)
400                                 warnx("stat failed on %s", dirpath);
401                         bad = ENOENT;   /* We will send error reply later */
402                 }
403
404                 /* Check in the exports list */
405                 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
406                 ep = ex_search(&fsb.f_fsid);
407                 hostset = defset = 0;
408                 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
409                     ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
410                      chk_host(dp, saddr, &defset, &hostset)) ||
411                      (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
412                       scan_tree(ep->ex_dirl, saddr) == 0))) {
413                         if (bad) {
414                                 if (!svc_sendreply(transp, xdr_long,
415                                     (caddr_t)&bad))
416                                         syslog(LOG_ERR, "can't send reply");
417                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
418                                 return;
419                         }
420                         if (hostset & DP_HOSTSET)
421                                 fhr.fhr_flag = hostset;
422                         else
423                                 fhr.fhr_flag = defset;
424                         fhr.fhr_vers = rqstp->rq_vers;
425                         /* Get the file handle */
426                         memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
427                         if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
428                                 bad = errno;
429                                 syslog(LOG_ERR, "can't get fh for %s", dirpath);
430                                 if (!svc_sendreply(transp, xdr_long,
431                                     (caddr_t)&bad))
432                                         syslog(LOG_ERR, "can't send reply");
433                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
434                                 return;
435                         }
436                         if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
437                                 syslog(LOG_ERR, "can't send reply");
438                         if (hp == NULL)
439                                 hp = gethostbyaddr((caddr_t)&saddr,
440                                     sizeof(saddr), AF_INET);
441                         if (hp)
442                                 add_mlist(hp->h_name, dirpath);
443                         else
444                                 add_mlist(inet_ntoa(saddrin),
445                                         dirpath);
446                         if (debug)
447                                 warnx("mount successful");
448                         if (log)
449                                 syslog(LOG_NOTICE,
450                                     "mount request succeeded from %s for %s",
451                                     inet_ntoa(saddrin), dirpath);
452                 } else {
453                         bad = EACCES;
454                         syslog(LOG_NOTICE,
455                             "mount request denied from %s for %s",
456                             inet_ntoa(saddrin), dirpath);
457                 }
458
459                 if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad))
460                         syslog(LOG_ERR, "can't send reply");
461                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
462                 return;
463         case RPCMNT_DUMP:
464                 if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
465                         syslog(LOG_ERR, "can't send reply");
466                 else if (log)
467                         syslog(LOG_NOTICE,
468                             "dump request succeeded from %s",
469                             inet_ntoa(saddrin));
470                 return;
471         case RPCMNT_UMOUNT:
472                 if (sport >= IPPORT_RESERVED && resvport_only) {
473                         syslog(LOG_NOTICE,
474                             "umount request from %s from unprivileged port",
475                             inet_ntoa(saddrin));
476                         svcerr_weakauth(transp);
477                         return;
478                 }
479                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
480                         syslog(LOG_NOTICE, "undecodable umount request from %s",
481                             inet_ntoa(saddrin));
482                         svcerr_decode(transp);
483                         return;
484                 }
485                 if (realpath(rpcpath, dirpath) == NULL) {
486                         syslog(LOG_NOTICE, "umount request from %s "
487                             "for non existent path %s",
488                             inet_ntoa(saddrin), dirpath);
489                 }
490                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
491                         syslog(LOG_ERR, "can't send reply");
492                 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
493                 if (hp)
494                         del_mlist(hp->h_name, dirpath);
495                 del_mlist(inet_ntoa(saddrin), dirpath);
496                 if (log)
497                         syslog(LOG_NOTICE,
498                             "umount request succeeded from %s for %s",
499                             inet_ntoa(saddrin), dirpath);
500                 return;
501         case RPCMNT_UMNTALL:
502                 if (sport >= IPPORT_RESERVED && resvport_only) {
503                         syslog(LOG_NOTICE,
504                             "umountall request from %s from unprivileged port",
505                             inet_ntoa(saddrin));
506                         svcerr_weakauth(transp);
507                         return;
508                 }
509                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
510                         syslog(LOG_ERR, "can't send reply");
511                 hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
512                 if (hp)
513                         del_mlist(hp->h_name, (char *)NULL);
514                 del_mlist(inet_ntoa(saddrin), (char *)NULL);
515                 if (log)
516                         syslog(LOG_NOTICE,
517                             "umountall request succeeded from %s",
518                             inet_ntoa(saddrin));
519                 return;
520         case RPCMNT_EXPORT:
521                 if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
522                         syslog(LOG_ERR, "can't send reply");
523                 if (log)
524                         syslog(LOG_NOTICE,
525                             "export request succeeded from %s",
526                             inet_ntoa(saddrin));
527                 return;
528         default:
529                 svcerr_noproc(transp);
530                 return;
531         }
532 }
533
534 /*
535  * Xdr conversion for a dirpath string
536  */
537 int
538 xdr_dir(xdrsp, dirp)
539         XDR *xdrsp;
540         char *dirp;
541 {
542         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
543 }
544
545 /*
546  * Xdr routine to generate file handle reply
547  */
548 int
549 xdr_fhs(xdrsp, cp)
550         XDR *xdrsp;
551         caddr_t cp;
552 {
553         register struct fhreturn *fhrp = (struct fhreturn *)cp;
554         u_long ok = 0, len, auth;
555
556         if (!xdr_long(xdrsp, &ok))
557                 return (0);
558         switch (fhrp->fhr_vers) {
559         case 1:
560                 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
561         case 3:
562                 len = NFSX_V3FH;
563                 if (!xdr_long(xdrsp, &len))
564                         return (0);
565                 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
566                         return (0);
567                 if (fhrp->fhr_flag & DP_KERB)
568                         auth = RPCAUTH_KERB4;
569                 else
570                         auth = RPCAUTH_UNIX;
571                 len = 1;
572                 if (!xdr_long(xdrsp, &len))
573                         return (0);
574                 return (xdr_long(xdrsp, &auth));
575         };
576         return (0);
577 }
578
579 int
580 xdr_mlist(xdrsp, cp)
581         XDR *xdrsp;
582         caddr_t cp;
583 {
584         struct mountlist *mlp;
585         int true = 1;
586         int false = 0;
587         char *strp;
588
589         mlp = mlhead;
590         while (mlp) {
591                 if (!xdr_bool(xdrsp, &true))
592                         return (0);
593                 strp = &mlp->ml_host[0];
594                 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
595                         return (0);
596                 strp = &mlp->ml_dirp[0];
597                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
598                         return (0);
599                 mlp = mlp->ml_next;
600         }
601         if (!xdr_bool(xdrsp, &false))
602                 return (0);
603         return (1);
604 }
605
606 /*
607  * Xdr conversion for export list
608  */
609 int
610 xdr_explist(xdrsp, cp)
611         XDR *xdrsp;
612         caddr_t cp;
613 {
614         struct exportlist *ep;
615         int false = 0;
616         int putdef;
617         sigset_t sighup_mask;
618
619         sigemptyset(&sighup_mask);
620         sigaddset(&sighup_mask, SIGHUP);
621         sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
622         ep = exphead;
623         while (ep) {
624                 putdef = 0;
625                 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
626                         goto errout;
627                 if (ep->ex_defdir && putdef == 0 &&
628                         put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
629                         &putdef))
630                         goto errout;
631                 ep = ep->ex_next;
632         }
633         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
634         if (!xdr_bool(xdrsp, &false))
635                 return (0);
636         return (1);
637 errout:
638         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
639         return (0);
640 }
641
642 /*
643  * Called from xdr_explist() to traverse the tree and export the
644  * directory paths.
645  */
646 int
647 put_exlist(dp, xdrsp, adp, putdefp)
648         struct dirlist *dp;
649         XDR *xdrsp;
650         struct dirlist *adp;
651         int *putdefp;
652 {
653         struct grouplist *grp;
654         struct hostlist *hp;
655         int true = 1;
656         int false = 0;
657         int gotalldir = 0;
658         char *strp;
659
660         if (dp) {
661                 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
662                         return (1);
663                 if (!xdr_bool(xdrsp, &true))
664                         return (1);
665                 strp = dp->dp_dirp;
666                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
667                         return (1);
668                 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
669                         gotalldir = 1;
670                         *putdefp = 1;
671                 }
672                 if ((dp->dp_flag & DP_DEFSET) == 0 &&
673                     (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
674                         hp = dp->dp_hosts;
675                         while (hp) {
676                                 grp = hp->ht_grp;
677                                 if (grp->gr_type == GT_HOST) {
678                                         if (!xdr_bool(xdrsp, &true))
679                                                 return (1);
680                                         strp = grp->gr_ptr.gt_hostent->h_name;
681                                         if (!xdr_string(xdrsp, &strp,
682                                             RPCMNT_NAMELEN))
683                                                 return (1);
684                                 } else if (grp->gr_type == GT_NET) {
685                                         if (!xdr_bool(xdrsp, &true))
686                                                 return (1);
687                                         strp = grp->gr_ptr.gt_net.nt_name;
688                                         if (!xdr_string(xdrsp, &strp,
689                                             RPCMNT_NAMELEN))
690                                                 return (1);
691                                 }
692                                 hp = hp->ht_next;
693                                 if (gotalldir && hp == (struct hostlist *)NULL) {
694                                         hp = adp->dp_hosts;
695                                         gotalldir = 0;
696                                 }
697                         }
698                 }
699                 if (!xdr_bool(xdrsp, &false))
700                         return (1);
701                 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
702                         return (1);
703         }
704         return (0);
705 }
706
707 #define LINESIZ 10240
708 char line[LINESIZ];
709 FILE *exp_file;
710
711 /*
712  * Get the export list
713  */
714 void
715 get_exportlist()
716 {
717         struct exportlist *ep, *ep2;
718         struct grouplist *grp, *tgrp;
719         struct exportlist **epp;
720         struct dirlist *dirhead;
721         struct statfs fsb, *fsp;
722         struct hostent *hpe;
723         struct ucred anon;
724         char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
725         int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
726
727         dirp = NULL;
728         dirplen = 0;
729
730         /*
731          * First, get rid of the old list
732          */
733         ep = exphead;
734         while (ep) {
735                 ep2 = ep;
736                 ep = ep->ex_next;
737                 free_exp(ep2);
738         }
739         exphead = (struct exportlist *)NULL;
740
741         grp = grphead;
742         while (grp) {
743                 tgrp = grp;
744                 grp = grp->gr_next;
745                 free_grp(tgrp);
746         }
747         grphead = (struct grouplist *)NULL;
748
749         /*
750          * And delete exports that are in the kernel for all local
751          * file systems.
752          * XXX: Should know how to handle all local exportable file systems
753          *      instead of just "ufs".
754          */
755         num = getmntinfo(&fsp, MNT_NOWAIT);
756         for (i = 0; i < num; i++) {
757                 union {
758                         struct ufs_args ua;
759                         struct iso_args ia;
760                         struct mfs_args ma;
761                         struct msdosfs_args da;
762                         struct ntfs_args na;
763                 } targs;
764
765                 if (!strcmp(fsp->f_fstypename, "mfs") ||
766                     !strcmp(fsp->f_fstypename, "ufs") ||
767                     !strcmp(fsp->f_fstypename, "msdos") ||
768                     !strcmp(fsp->f_fstypename, "ntfs") ||
769                     !strcmp(fsp->f_fstypename, "cd9660")) {
770                         targs.ua.fspec = NULL;
771                         targs.ua.export.ex_flags = MNT_DELEXPORT;
772                         if (mount(fsp->f_fstypename, fsp->f_mntonname,
773                                   fsp->f_flags | MNT_UPDATE,
774                                   (caddr_t)&targs) < 0)
775                                 syslog(LOG_ERR, "can't delete exports for %s",
776                                        fsp->f_mntonname);
777                 }
778                 fsp++;
779         }
780
781         /*
782          * Read in the exports file and build the list, calling
783          * mount() as we go along to push the export rules into the kernel.
784          */
785         if ((exp_file = fopen(exname, "r")) == NULL) {
786                 syslog(LOG_ERR, "can't open %s", exname);
787                 exit(2);
788         }
789         dirhead = (struct dirlist *)NULL;
790         while (get_line()) {
791                 if (debug)
792                         warnx("got line %s", line);
793                 cp = line;
794                 nextfield(&cp, &endcp);
795                 if (*cp == '#')
796                         goto nextline;
797
798                 /*
799                  * Set defaults.
800                  */
801                 has_host = FALSE;
802                 anon = def_anon;
803                 exflags = MNT_EXPORTED;
804                 got_nondir = 0;
805                 opt_flags = 0;
806                 ep = (struct exportlist *)NULL;
807
808                 /*
809                  * Create new exports list entry
810                  */
811                 len = endcp-cp;
812                 tgrp = grp = get_grp();
813                 while (len > 0) {
814                         if (len > RPCMNT_NAMELEN) {
815                             getexp_err(ep, tgrp);
816                             goto nextline;
817                         }
818                         if (*cp == '-') {
819                             if (ep == (struct exportlist *)NULL) {
820                                 getexp_err(ep, tgrp);
821                                 goto nextline;
822                             }
823                             if (debug)
824                                 warnx("doing opt %s", cp);
825                             got_nondir = 1;
826                             if (do_opt(&cp, &endcp, ep, grp, &has_host,
827                                 &exflags, &anon)) {
828                                 getexp_err(ep, tgrp);
829                                 goto nextline;
830                             }
831                         } else if (*cp == '/') {
832                             savedc = *endcp;
833                             *endcp = '\0';
834                             if (check_dirpath(cp) &&
835                                 statfs(cp, &fsb) >= 0) {
836                                 if (got_nondir) {
837                                     syslog(LOG_ERR, "dirs must be first");
838                                     getexp_err(ep, tgrp);
839                                     goto nextline;
840                                 }
841                                 if (ep) {
842                                     if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
843                                         ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
844                                         getexp_err(ep, tgrp);
845                                         goto nextline;
846                                     }
847                                 } else {
848                                     /*
849                                      * See if this directory is already
850                                      * in the list.
851                                      */
852                                     ep = ex_search(&fsb.f_fsid);
853                                     if (ep == (struct exportlist *)NULL) {
854                                         ep = get_exp();
855                                         ep->ex_fs = fsb.f_fsid;
856                                         ep->ex_fsdir = (char *)
857                                             malloc(strlen(fsb.f_mntonname) + 1);
858                                         if (ep->ex_fsdir)
859                                             strcpy(ep->ex_fsdir,
860                                                 fsb.f_mntonname);
861                                         else
862                                             out_of_mem();
863                                         if (debug)
864                                           warnx("making new ep fs=0x%x,0x%x",
865                                               fsb.f_fsid.val[0],
866                                               fsb.f_fsid.val[1]);
867                                     } else if (debug)
868                                         warnx("found ep fs=0x%x,0x%x",
869                                             fsb.f_fsid.val[0],
870                                             fsb.f_fsid.val[1]);
871                                 }
872
873                                 /*
874                                  * Add dirpath to export mount point.
875                                  */
876                                 dirp = add_expdir(&dirhead, cp, len);
877                                 dirplen = len;
878                             } else {
879                                 getexp_err(ep, tgrp);
880                                 goto nextline;
881                             }
882                             *endcp = savedc;
883                         } else {
884                             savedc = *endcp;
885                             *endcp = '\0';
886                             got_nondir = 1;
887                             if (ep == (struct exportlist *)NULL) {
888                                 getexp_err(ep, tgrp);
889                                 goto nextline;
890                             }
891
892                             /*
893                              * Get the host or netgroup.
894                              */
895                             setnetgrent(cp);
896                             netgrp = getnetgrent(&hst, &usr, &dom);
897                             do {
898                                 if (has_host) {
899                                     grp->gr_next = get_grp();
900                                     grp = grp->gr_next;
901                                 }
902                                 if (netgrp) {
903                                     if (hst == 0) {
904                                         syslog(LOG_ERR,
905                                 "null hostname in netgroup %s, skipping", cp);
906                                         grp->gr_type = GT_IGNORE;
907                                     } else if (get_host(hst, grp, tgrp)) {
908                                         syslog(LOG_ERR,
909                         "bad host %s in netgroup %s, skipping", hst, cp);
910                                         grp->gr_type = GT_IGNORE;
911                                     }
912                                 } else if (get_host(cp, grp, tgrp)) {
913                                     syslog(LOG_ERR, "bad host %s, skipping", cp);
914                                     grp->gr_type = GT_IGNORE;
915                                 }
916                                 has_host = TRUE;
917                             } while (netgrp && getnetgrent(&hst, &usr, &dom));
918                             endnetgrent();
919                             *endcp = savedc;
920                         }
921                         cp = endcp;
922                         nextfield(&cp, &endcp);
923                         len = endcp - cp;
924                 }
925                 if (check_options(dirhead)) {
926                         getexp_err(ep, tgrp);
927                         goto nextline;
928                 }
929                 if (!has_host) {
930                         grp->gr_type = GT_HOST;
931                         if (debug)
932                                 warnx("adding a default entry");
933                         /* add a default group and make the grp list NULL */
934                         hpe = (struct hostent *)malloc(sizeof(struct hostent));
935                         if (hpe == (struct hostent *)NULL)
936                                 out_of_mem();
937                         hpe->h_name = strdup("Default");
938                         hpe->h_addrtype = AF_INET;
939                         hpe->h_length = sizeof (u_int32_t);
940                         hpe->h_addr_list = (char **)NULL;
941                         grp->gr_ptr.gt_hostent = hpe;
942
943                 /*
944                  * Don't allow a network export coincide with a list of
945                  * host(s) on the same line.
946                  */
947                 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
948                         getexp_err(ep, tgrp);
949                         goto nextline;
950
951                 /*
952                  * If an export list was specified on this line, make sure
953                  * that we have at least one valid entry, otherwise skip it.
954                  */
955                 } else {
956                         grp = tgrp;
957                         while (grp && grp->gr_type == GT_IGNORE)
958                                 grp = grp->gr_next;
959                         if (! grp) {
960                             getexp_err(ep, tgrp);
961                             goto nextline;
962                         }
963                 }
964
965                 /*
966                  * Loop through hosts, pushing the exports into the kernel.
967                  * After loop, tgrp points to the start of the list and
968                  * grp points to the last entry in the list.
969                  */
970                 grp = tgrp;
971                 do {
972                     if (do_mount(ep, grp, exflags, &anon, dirp,
973                         dirplen, &fsb)) {
974                         getexp_err(ep, tgrp);
975                         goto nextline;
976                     }
977                 } while (grp->gr_next && (grp = grp->gr_next));
978
979                 /*
980                  * Success. Update the data structures.
981                  */
982                 if (has_host) {
983                         hang_dirp(dirhead, tgrp, ep, opt_flags);
984                         grp->gr_next = grphead;
985                         grphead = tgrp;
986                 } else {
987                         hang_dirp(dirhead, (struct grouplist *)NULL, ep,
988                                 opt_flags);
989                         free_grp(grp);
990                 }
991                 dirhead = (struct dirlist *)NULL;
992                 if ((ep->ex_flag & EX_LINKED) == 0) {
993                         ep2 = exphead;
994                         epp = &exphead;
995
996                         /*
997                          * Insert in the list in alphabetical order.
998                          */
999                         while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1000                                 epp = &ep2->ex_next;
1001                                 ep2 = ep2->ex_next;
1002                         }
1003                         if (ep2)
1004                                 ep->ex_next = ep2;
1005                         *epp = ep;
1006                         ep->ex_flag |= EX_LINKED;
1007                 }
1008 nextline:
1009                 if (dirhead) {
1010                         free_dir(dirhead);
1011                         dirhead = (struct dirlist *)NULL;
1012                 }
1013         }
1014         fclose(exp_file);
1015 }
1016
1017 /*
1018  * Allocate an export list element
1019  */
1020 struct exportlist *
1021 get_exp()
1022 {
1023         struct exportlist *ep;
1024
1025         ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1026         if (ep == (struct exportlist *)NULL)
1027                 out_of_mem();
1028         memset(ep, 0, sizeof(struct exportlist));
1029         return (ep);
1030 }
1031
1032 /*
1033  * Allocate a group list element
1034  */
1035 struct grouplist *
1036 get_grp()
1037 {
1038         struct grouplist *gp;
1039
1040         gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1041         if (gp == (struct grouplist *)NULL)
1042                 out_of_mem();
1043         memset(gp, 0, sizeof(struct grouplist));
1044         return (gp);
1045 }
1046
1047 /*
1048  * Clean up upon an error in get_exportlist().
1049  */
1050 void
1051 getexp_err(ep, grp)
1052         struct exportlist *ep;
1053         struct grouplist *grp;
1054 {
1055         struct grouplist *tgrp;
1056
1057         if (!(opt_flags & OP_QUIET))
1058                 syslog(LOG_ERR, "bad exports list line %s", line);
1059         if (ep && (ep->ex_flag & EX_LINKED) == 0)
1060                 free_exp(ep);
1061         while (grp) {
1062                 tgrp = grp;
1063                 grp = grp->gr_next;
1064                 free_grp(tgrp);
1065         }
1066 }
1067
1068 /*
1069  * Search the export list for a matching fs.
1070  */
1071 struct exportlist *
1072 ex_search(fsid)
1073         fsid_t *fsid;
1074 {
1075         struct exportlist *ep;
1076
1077         ep = exphead;
1078         while (ep) {
1079                 if (ep->ex_fs.val[0] == fsid->val[0] &&
1080                     ep->ex_fs.val[1] == fsid->val[1])
1081                         return (ep);
1082                 ep = ep->ex_next;
1083         }
1084         return (ep);
1085 }
1086
1087 /*
1088  * Add a directory path to the list.
1089  */
1090 char *
1091 add_expdir(dpp, cp, len)
1092         struct dirlist **dpp;
1093         char *cp;
1094         int len;
1095 {
1096         struct dirlist *dp;
1097
1098         dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1099         if (dp == (struct dirlist *)NULL)
1100                 out_of_mem();
1101         dp->dp_left = *dpp;
1102         dp->dp_right = (struct dirlist *)NULL;
1103         dp->dp_flag = 0;
1104         dp->dp_hosts = (struct hostlist *)NULL;
1105         strcpy(dp->dp_dirp, cp);
1106         *dpp = dp;
1107         return (dp->dp_dirp);
1108 }
1109
1110 /*
1111  * Hang the dir list element off the dirpath binary tree as required
1112  * and update the entry for host.
1113  */
1114 void
1115 hang_dirp(dp, grp, ep, flags)
1116         struct dirlist *dp;
1117         struct grouplist *grp;
1118         struct exportlist *ep;
1119         int flags;
1120 {
1121         struct hostlist *hp;
1122         struct dirlist *dp2;
1123
1124         if (flags & OP_ALLDIRS) {
1125                 if (ep->ex_defdir)
1126                         free((caddr_t)dp);
1127                 else
1128                         ep->ex_defdir = dp;
1129                 if (grp == (struct grouplist *)NULL) {
1130                         ep->ex_defdir->dp_flag |= DP_DEFSET;
1131                         if (flags & OP_KERB)
1132                                 ep->ex_defdir->dp_flag |= DP_KERB;
1133                 } else while (grp) {
1134                         hp = get_ht();
1135                         if (flags & OP_KERB)
1136                                 hp->ht_flag |= DP_KERB;
1137                         hp->ht_grp = grp;
1138                         hp->ht_next = ep->ex_defdir->dp_hosts;
1139                         ep->ex_defdir->dp_hosts = hp;
1140                         grp = grp->gr_next;
1141                 }
1142         } else {
1143
1144                 /*
1145                  * Loop through the directories adding them to the tree.
1146                  */
1147                 while (dp) {
1148                         dp2 = dp->dp_left;
1149                         add_dlist(&ep->ex_dirl, dp, grp, flags);
1150                         dp = dp2;
1151                 }
1152         }
1153 }
1154
1155 /*
1156  * Traverse the binary tree either updating a node that is already there
1157  * for the new directory or adding the new node.
1158  */
1159 void
1160 add_dlist(dpp, newdp, grp, flags)
1161         struct dirlist **dpp;
1162         struct dirlist *newdp;
1163         struct grouplist *grp;
1164         int flags;
1165 {
1166         struct dirlist *dp;
1167         struct hostlist *hp;
1168         int cmp;
1169
1170         dp = *dpp;
1171         if (dp) {
1172                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1173                 if (cmp > 0) {
1174                         add_dlist(&dp->dp_left, newdp, grp, flags);
1175                         return;
1176                 } else if (cmp < 0) {
1177                         add_dlist(&dp->dp_right, newdp, grp, flags);
1178                         return;
1179                 } else
1180                         free((caddr_t)newdp);
1181         } else {
1182                 dp = newdp;
1183                 dp->dp_left = (struct dirlist *)NULL;
1184                 *dpp = dp;
1185         }
1186         if (grp) {
1187
1188                 /*
1189                  * Hang all of the host(s) off of the directory point.
1190                  */
1191                 do {
1192                         hp = get_ht();
1193                         if (flags & OP_KERB)
1194                                 hp->ht_flag |= DP_KERB;
1195                         hp->ht_grp = grp;
1196                         hp->ht_next = dp->dp_hosts;
1197                         dp->dp_hosts = hp;
1198                         grp = grp->gr_next;
1199                 } while (grp);
1200         } else {
1201                 dp->dp_flag |= DP_DEFSET;
1202                 if (flags & OP_KERB)
1203                         dp->dp_flag |= DP_KERB;
1204         }
1205 }
1206
1207 /*
1208  * Search for a dirpath on the export point.
1209  */
1210 struct dirlist *
1211 dirp_search(dp, dirpath)
1212         struct dirlist *dp;
1213         char *dirpath;
1214 {
1215         int cmp;
1216
1217         if (dp) {
1218                 cmp = strcmp(dp->dp_dirp, dirpath);
1219                 if (cmp > 0)
1220                         return (dirp_search(dp->dp_left, dirpath));
1221                 else if (cmp < 0)
1222                         return (dirp_search(dp->dp_right, dirpath));
1223                 else
1224                         return (dp);
1225         }
1226         return (dp);
1227 }
1228
1229 /*
1230  * Scan for a host match in a directory tree.
1231  */
1232 int
1233 chk_host(dp, saddr, defsetp, hostsetp)
1234         struct dirlist *dp;
1235         u_int32_t saddr;
1236         int *defsetp;
1237         int *hostsetp;
1238 {
1239         struct hostlist *hp;
1240         struct grouplist *grp;
1241         u_int32_t **addrp;
1242
1243         if (dp) {
1244                 if (dp->dp_flag & DP_DEFSET)
1245                         *defsetp = dp->dp_flag;
1246                 hp = dp->dp_hosts;
1247                 while (hp) {
1248                         grp = hp->ht_grp;
1249                         switch (grp->gr_type) {
1250                         case GT_HOST:
1251                             addrp = (u_int32_t **)
1252                                 grp->gr_ptr.gt_hostent->h_addr_list;
1253                             while (*addrp) {
1254                                 if (**addrp == saddr) {
1255                                     *hostsetp = (hp->ht_flag | DP_HOSTSET);
1256                                     return (1);
1257                                 }
1258                                 addrp++;
1259                             }
1260                             break;
1261                         case GT_NET:
1262                             if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
1263                                 grp->gr_ptr.gt_net.nt_net) {
1264                                 *hostsetp = (hp->ht_flag | DP_HOSTSET);
1265                                 return (1);
1266                             }
1267                             break;
1268                         };
1269                         hp = hp->ht_next;
1270                 }
1271         }
1272         return (0);
1273 }
1274
1275 /*
1276  * Scan tree for a host that matches the address.
1277  */
1278 int
1279 scan_tree(dp, saddr)
1280         struct dirlist *dp;
1281         u_int32_t saddr;
1282 {
1283         int defset, hostset;
1284
1285         if (dp) {
1286                 if (scan_tree(dp->dp_left, saddr))
1287                         return (1);
1288                 if (chk_host(dp, saddr, &defset, &hostset))
1289                         return (1);
1290                 if (scan_tree(dp->dp_right, saddr))
1291                         return (1);
1292         }
1293         return (0);
1294 }
1295
1296 /*
1297  * Traverse the dirlist tree and free it up.
1298  */
1299 void
1300 free_dir(dp)
1301         struct dirlist *dp;
1302 {
1303
1304         if (dp) {
1305                 free_dir(dp->dp_left);
1306                 free_dir(dp->dp_right);
1307                 free_host(dp->dp_hosts);
1308                 free((caddr_t)dp);
1309         }
1310 }
1311
1312 /*
1313  * Parse the option string and update fields.
1314  * Option arguments may either be -<option>=<value> or
1315  * -<option> <value>
1316  */
1317 int
1318 do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1319         char **cpp, **endcpp;
1320         struct exportlist *ep;
1321         struct grouplist *grp;
1322         int *has_hostp;
1323         int *exflagsp;
1324         struct ucred *cr;
1325 {
1326         char *cpoptarg, *cpoptend;
1327         char *cp, *endcp, *cpopt, savedc, savedc2;
1328         int allflag, usedarg;
1329
1330         savedc2 = '\0';
1331         cpopt = *cpp;
1332         cpopt++;
1333         cp = *endcpp;
1334         savedc = *cp;
1335         *cp = '\0';
1336         while (cpopt && *cpopt) {
1337                 allflag = 1;
1338                 usedarg = -2;
1339                 if ((cpoptend = strchr(cpopt, ','))) {
1340                         *cpoptend++ = '\0';
1341                         if ((cpoptarg = strchr(cpopt, '=')))
1342                                 *cpoptarg++ = '\0';
1343                 } else {
1344                         if ((cpoptarg = strchr(cpopt, '=')))
1345                                 *cpoptarg++ = '\0';
1346                         else {
1347                                 *cp = savedc;
1348                                 nextfield(&cp, &endcp);
1349                                 **endcpp = '\0';
1350                                 if (endcp > cp && *cp != '-') {
1351                                         cpoptarg = cp;
1352                                         savedc2 = *endcp;
1353                                         *endcp = '\0';
1354                                         usedarg = 0;
1355                                 }
1356                         }
1357                 }
1358                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1359                         *exflagsp |= MNT_EXRDONLY;
1360                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1361                     !(allflag = strcmp(cpopt, "mapall")) ||
1362                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1363                         usedarg++;
1364                         parsecred(cpoptarg, cr);
1365                         if (allflag == 0) {
1366                                 *exflagsp |= MNT_EXPORTANON;
1367                                 opt_flags |= OP_MAPALL;
1368                         } else
1369                                 opt_flags |= OP_MAPROOT;
1370                 } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1371                         *exflagsp |= MNT_EXKERB;
1372                         opt_flags |= OP_KERB;
1373                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1374                         !strcmp(cpopt, "m"))) {
1375                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1376                                 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1377                                 return (1);
1378                         }
1379                         usedarg++;
1380                         opt_flags |= OP_MASK;
1381                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1382                         !strcmp(cpopt, "n"))) {
1383                         if (grp->gr_type != GT_NULL) {
1384                                 syslog(LOG_ERR, "network/host conflict");
1385                                 return (1);
1386                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1387                                 syslog(LOG_ERR, "bad net: %s", cpoptarg);
1388                                 return (1);
1389                         }
1390                         grp->gr_type = GT_NET;
1391                         *has_hostp = 1;
1392                         usedarg++;
1393                         opt_flags |= OP_NET;
1394                 } else if (!strcmp(cpopt, "alldirs")) {
1395                         opt_flags |= OP_ALLDIRS;
1396                 } else if (!strcmp(cpopt, "public")) {
1397                         *exflagsp |= MNT_EXPUBLIC;
1398                 } else if (!strcmp(cpopt, "webnfs")) {
1399                         *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1400                         opt_flags |= OP_MAPALL;
1401                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1402                         ep->ex_indexfile = strdup(cpoptarg);
1403                 } else if (!strcmp(cpopt, "quiet")) {
1404                         opt_flags |= OP_QUIET;
1405                 } else {
1406                         syslog(LOG_ERR, "bad opt %s", cpopt);
1407                         return (1);
1408                 }
1409                 if (usedarg >= 0) {
1410                         *endcp = savedc2;
1411                         **endcpp = savedc;
1412                         if (usedarg > 0) {
1413                                 *cpp = cp;
1414                                 *endcpp = endcp;
1415                         }
1416                         return (0);
1417                 }
1418                 cpopt = cpoptend;
1419         }
1420         **endcpp = savedc;
1421         return (0);
1422 }
1423
1424 /*
1425  * Translate a character string to the corresponding list of network
1426  * addresses for a hostname.
1427  */
1428 int
1429 get_host(cp, grp, tgrp)
1430         char *cp;
1431         struct grouplist *grp;
1432         struct grouplist *tgrp;
1433 {
1434         struct grouplist *checkgrp;
1435         struct hostent *hp, *nhp;
1436         char **addrp, **naddrp;
1437         struct hostent t_host;
1438         int i;
1439         u_int32_t saddr;
1440         char *aptr[2];
1441
1442         if (grp->gr_type != GT_NULL)
1443                 return (1);
1444         if ((hp = gethostbyname(cp)) == NULL) {
1445                 if (isdigit(*cp)) {
1446                         saddr = inet_addr(cp);
1447                         if (saddr == -1) {
1448                                 syslog(LOG_ERR, "inet_addr failed for %s", cp);
1449                                 return (1);
1450                         }
1451                         if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
1452                                 AF_INET)) == NULL) {
1453                                 hp = &t_host;
1454                                 hp->h_name = cp;
1455                                 hp->h_addrtype = AF_INET;
1456                                 hp->h_length = sizeof (u_int32_t);
1457                                 hp->h_addr_list = aptr;
1458                                 aptr[0] = (char *)&saddr;
1459                                 aptr[1] = (char *)NULL;
1460                         }
1461                 } else {
1462                         syslog(LOG_ERR, "gethostbyname failed for %s", cp);
1463                         return (1);
1464                 }
1465         }
1466         /*
1467          * Sanity check: make sure we don't already have an entry
1468          * for this host in the grouplist.
1469          */
1470         checkgrp = tgrp;
1471         while (checkgrp != NULL) {
1472                 if (checkgrp->gr_type == GT_HOST &&
1473                     checkgrp->gr_ptr.gt_hostent != NULL &&
1474                     (!strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)
1475                 || *(u_int32_t *)checkgrp->gr_ptr.gt_hostent->h_addr ==
1476                         *(u_int32_t *)hp->h_addr)) {
1477                         grp->gr_type = GT_IGNORE;
1478                         return(0);
1479                 }
1480                 checkgrp = checkgrp->gr_next;
1481         }
1482
1483         grp->gr_type = GT_HOST;
1484         nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
1485                 malloc(sizeof(struct hostent));
1486         if (nhp == (struct hostent *)NULL)
1487                 out_of_mem();
1488         memmove(nhp, hp, sizeof(struct hostent));
1489         i = strlen(hp->h_name)+1;
1490         nhp->h_name = (char *)malloc(i);
1491         if (nhp->h_name == (char *)NULL)
1492                 out_of_mem();
1493         memmove(nhp->h_name, hp->h_name, i);
1494         addrp = hp->h_addr_list;
1495         i = 1;
1496         while (*addrp++)
1497                 i++;
1498         naddrp = nhp->h_addr_list = (char **)malloc(i*sizeof(char *));
1499         if (naddrp == (char **)NULL)
1500                 out_of_mem();
1501         addrp = hp->h_addr_list;
1502         while (*addrp) {
1503                 *naddrp = (char *)malloc(hp->h_length);
1504                 if (*naddrp == (char *)NULL)
1505                     out_of_mem();
1506                 memmove(*naddrp, *addrp, hp->h_length);
1507                 addrp++;
1508                 naddrp++;
1509         }
1510         *naddrp = (char *)NULL;
1511         if (debug)
1512                 warnx("got host %s", hp->h_name);
1513         return (0);
1514 }
1515
1516 /*
1517  * Free up an exports list component
1518  */
1519 void
1520 free_exp(ep)
1521         struct exportlist *ep;
1522 {
1523
1524         if (ep->ex_defdir) {
1525                 free_host(ep->ex_defdir->dp_hosts);
1526                 free((caddr_t)ep->ex_defdir);
1527         }
1528         if (ep->ex_fsdir)
1529                 free(ep->ex_fsdir);
1530         if (ep->ex_indexfile)
1531                 free(ep->ex_indexfile);
1532         free_dir(ep->ex_dirl);
1533         free((caddr_t)ep);
1534 }
1535
1536 /*
1537  * Free hosts.
1538  */
1539 void
1540 free_host(hp)
1541         struct hostlist *hp;
1542 {
1543         struct hostlist *hp2;
1544
1545         while (hp) {
1546                 hp2 = hp;
1547                 hp = hp->ht_next;
1548                 free((caddr_t)hp2);
1549         }
1550 }
1551
1552 struct hostlist *
1553 get_ht()
1554 {
1555         struct hostlist *hp;
1556
1557         hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1558         if (hp == (struct hostlist *)NULL)
1559                 out_of_mem();
1560         hp->ht_next = (struct hostlist *)NULL;
1561         hp->ht_flag = 0;
1562         return (hp);
1563 }
1564
1565 /*
1566  * Out of memory, fatal
1567  */
1568 void
1569 out_of_mem()
1570 {
1571
1572         syslog(LOG_ERR, "out of memory");
1573         exit(2);
1574 }
1575
1576 /*
1577  * Do the mount syscall with the update flag to push the export info into
1578  * the kernel.
1579  */
1580 int
1581 do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1582         struct exportlist *ep;
1583         struct grouplist *grp;
1584         int exflags;
1585         struct ucred *anoncrp;
1586         char *dirp;
1587         int dirplen;
1588         struct statfs *fsb;
1589 {
1590         struct statfs fsb1;
1591         char *cp = NULL;
1592         u_int32_t **addrp;
1593         int done;
1594         char savedc = '\0';
1595         struct sockaddr_in sin, imask;
1596         union {
1597                 struct ufs_args ua;
1598                 struct iso_args ia;
1599                 struct mfs_args ma;
1600 #ifdef __NetBSD__
1601                 struct msdosfs_args da;
1602 #endif
1603                 struct ntfs_args na;
1604         } args;
1605         u_int32_t net;
1606
1607         args.ua.fspec = 0;
1608         args.ua.export.ex_flags = exflags;
1609         args.ua.export.ex_anon = *anoncrp;
1610         args.ua.export.ex_indexfile = ep->ex_indexfile;
1611         memset(&sin, 0, sizeof(sin));
1612         memset(&imask, 0, sizeof(imask));
1613         sin.sin_family = AF_INET;
1614         sin.sin_len = sizeof(sin);
1615         imask.sin_family = AF_INET;
1616         imask.sin_len = sizeof(sin);
1617         if (grp->gr_type == GT_HOST)
1618                 addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list;
1619         else
1620                 addrp = (u_int32_t **)NULL;
1621         done = FALSE;
1622         while (!done) {
1623                 switch (grp->gr_type) {
1624                 case GT_HOST:
1625                         if (addrp) {
1626                                 sin.sin_addr.s_addr = **addrp;
1627                                 args.ua.export.ex_addrlen = sizeof(sin);
1628                         } else
1629                                 args.ua.export.ex_addrlen = 0;
1630                         args.ua.export.ex_addr = (struct sockaddr *)&sin;
1631                         args.ua.export.ex_masklen = 0;
1632                         break;
1633                 case GT_NET:
1634                         if (grp->gr_ptr.gt_net.nt_mask)
1635                             imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
1636                         else {
1637                             net = ntohl(grp->gr_ptr.gt_net.nt_net);
1638                             if (IN_CLASSA(net))
1639                                 imask.sin_addr.s_addr = inet_addr("255.0.0.0");
1640                             else if (IN_CLASSB(net))
1641                                 imask.sin_addr.s_addr =
1642                                     inet_addr("255.255.0.0");
1643                             else
1644                                 imask.sin_addr.s_addr =
1645                                     inet_addr("255.255.255.0");
1646                             grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
1647                         }
1648                         sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
1649                         args.ua.export.ex_addr = (struct sockaddr *)&sin;
1650                         args.ua.export.ex_addrlen = sizeof (sin);
1651                         args.ua.export.ex_mask = (struct sockaddr *)&imask;
1652                         args.ua.export.ex_masklen = sizeof (imask);
1653                         break;
1654                 case GT_IGNORE:
1655                         return(0);
1656                         break;
1657                 default:
1658                         syslog(LOG_ERR, "bad grouptype");
1659                         if (cp)
1660                                 *cp = savedc;
1661                         return (1);
1662                 };
1663
1664                 /*
1665                  * XXX:
1666                  * Maybe I should just use the fsb->f_mntonname path instead
1667                  * of looping back up the dirp to the mount point??
1668                  * Also, needs to know how to export all types of local
1669                  * exportable file systems and not just "ufs".
1670                  */
1671                 while (mount(fsb->f_fstypename, dirp,
1672                        fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1673                         if (cp)
1674                                 *cp-- = savedc;
1675                         else
1676                                 cp = dirp + dirplen - 1;
1677                         if (opt_flags & OP_QUIET)
1678                                 return (1);
1679                         if (errno == EPERM) {
1680                                 syslog(LOG_ERR,
1681                                    "can't change attributes for %s", dirp);
1682                                 return (1);
1683                         }
1684                         if (opt_flags & OP_ALLDIRS) {
1685                                 if (errno == EINVAL)
1686                                         syslog(LOG_ERR,
1687                 "-alldirs requested but %s is not a filesystem mountpoint",
1688                                                 dirp);
1689                                 else
1690                                         syslog(LOG_ERR,
1691                                                 "could not remount %s: %m",
1692                                                 dirp);
1693                                 return (1);
1694                         }
1695                         /* back up over the last component */
1696                         while (*cp == '/' && cp > dirp)
1697                                 cp--;
1698                         while (*(cp - 1) != '/' && cp > dirp)
1699                                 cp--;
1700                         if (cp == dirp) {
1701                                 if (debug)
1702                                         warnx("mnt unsucc");
1703                                 syslog(LOG_ERR, "can't export %s", dirp);
1704                                 return (1);
1705                         }
1706                         savedc = *cp;
1707                         *cp = '\0';
1708                         /* Check that we're still on the same filesystem. */
1709                         if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
1710                             &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
1711                                 *cp = savedc;
1712                                 syslog(LOG_ERR, "can't export %s", dirp);
1713                                 return (1);
1714                         }
1715                 }
1716                 if (addrp) {
1717                         ++addrp;
1718                         if (*addrp == (u_int32_t *)NULL)
1719                                 done = TRUE;
1720                 } else
1721                         done = TRUE;
1722         }
1723         if (cp)
1724                 *cp = savedc;
1725         return (0);
1726 }
1727
1728 /*
1729  * Translate a net address.
1730  */
1731 int
1732 get_net(cp, net, maskflg)
1733         char *cp;
1734         struct netmsk *net;
1735         int maskflg;
1736 {
1737         struct netent *np;
1738         long netaddr;
1739         struct in_addr inetaddr, inetaddr2;
1740         char *name;
1741
1742         if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) {
1743                 inetaddr = inet_makeaddr(netaddr, 0);
1744                 /*
1745                  * Due to arbitrary subnet masks, you don't know how many
1746                  * bits to shift the address to make it into a network,
1747                  * however you do know how to make a network address into
1748                  * a host with host == 0 and then compare them.
1749                  * (What a pest)
1750                  */
1751                 if (!maskflg) {
1752                         setnetent(0);
1753                         while ((np = getnetent())) {
1754                                 inetaddr2 = inet_makeaddr(np->n_net, 0);
1755                                 if (inetaddr2.s_addr == inetaddr.s_addr)
1756                                         break;
1757                         }
1758                         endnetent();
1759                 }
1760         } else if ((np = getnetbyname(cp)) != NULL) {
1761                 inetaddr = inet_makeaddr(np->n_net, 0);
1762         } else
1763                 return (1);
1764
1765         if (maskflg)
1766                 net->nt_mask = inetaddr.s_addr;
1767         else {
1768                 if (np)
1769                         name = np->n_name;
1770                 else
1771                         name = inet_ntoa(inetaddr);
1772                 net->nt_name = (char *)malloc(strlen(name) + 1);
1773                 if (net->nt_name == (char *)NULL)
1774                         out_of_mem();
1775                 strcpy(net->nt_name, name);
1776                 net->nt_net = inetaddr.s_addr;
1777         }
1778         return (0);
1779 }
1780
1781 /*
1782  * Parse out the next white space separated field
1783  */
1784 void
1785 nextfield(cp, endcp)
1786         char **cp;
1787         char **endcp;
1788 {
1789         char *p;
1790
1791         p = *cp;
1792         while (*p == ' ' || *p == '\t')
1793                 p++;
1794         if (*p == '\n' || *p == '\0')
1795                 *cp = *endcp = p;
1796         else {
1797                 *cp = p++;
1798                 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1799                         p++;
1800                 *endcp = p;
1801         }
1802 }
1803
1804 /*
1805  * Get an exports file line. Skip over blank lines and handle line
1806  * continuations.
1807  */
1808 int
1809 get_line()
1810 {
1811         char *p, *cp;
1812         int len;
1813         int totlen, cont_line;
1814
1815         /*
1816          * Loop around ignoring blank lines and getting all continuation lines.
1817          */
1818         p = line;
1819         totlen = 0;
1820         do {
1821                 if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
1822                         return (0);
1823                 len = strlen(p);
1824                 cp = p + len - 1;
1825                 cont_line = 0;
1826                 while (cp >= p &&
1827                     (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
1828                         if (*cp == '\\')
1829                                 cont_line = 1;
1830                         cp--;
1831                         len--;
1832                 }
1833                 if (cont_line) {
1834                         *++cp = ' ';
1835                         len++;
1836                 }
1837                 *++cp = '\0';
1838                 if (len > 0) {
1839                         totlen += len;
1840                         if (totlen >= LINESIZ) {
1841                                 syslog(LOG_ERR, "exports line too long");
1842                                 exit(2);
1843                         }
1844                         p = cp;
1845                 }
1846         } while (totlen == 0 || cont_line);
1847         return (1);
1848 }
1849
1850 /*
1851  * Parse a description of a credential.
1852  */
1853 void
1854 parsecred(namelist, cr)
1855         char *namelist;
1856         struct ucred *cr;
1857 {
1858         char *name;
1859         int cnt;
1860         char *names;
1861         struct passwd *pw;
1862         struct group *gr;
1863         int ngroups, groups[NGROUPS + 1];
1864
1865         /*
1866          * Set up the unprivileged user.
1867          */
1868         cr->cr_ref = 1;
1869         cr->cr_uid = -2;
1870         cr->cr_groups[0] = -2;
1871         cr->cr_ngroups = 1;
1872         /*
1873          * Get the user's password table entry.
1874          */
1875         names = strsep(&namelist, " \t\n");
1876         name = strsep(&names, ":");
1877         if (isdigit(*name) || *name == '-')
1878                 pw = getpwuid(atoi(name));
1879         else
1880                 pw = getpwnam(name);
1881         /*
1882          * Credentials specified as those of a user.
1883          */
1884         if (names == NULL) {
1885                 if (pw == NULL) {
1886                         syslog(LOG_ERR, "unknown user: %s", name);
1887                         return;
1888                 }
1889                 cr->cr_uid = pw->pw_uid;
1890                 ngroups = NGROUPS + 1;
1891                 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
1892                         syslog(LOG_ERR, "too many groups");
1893                 /*
1894                  * Convert from int's to gid_t's and compress out duplicate
1895                  */
1896                 cr->cr_ngroups = ngroups - 1;
1897                 cr->cr_groups[0] = groups[0];
1898                 for (cnt = 2; cnt < ngroups; cnt++)
1899                         cr->cr_groups[cnt - 1] = groups[cnt];
1900                 return;
1901         }
1902         /*
1903          * Explicit credential specified as a colon separated list:
1904          *      uid:gid:gid:...
1905          */
1906         if (pw != NULL)
1907                 cr->cr_uid = pw->pw_uid;
1908         else if (isdigit(*name) || *name == '-')
1909                 cr->cr_uid = atoi(name);
1910         else {
1911                 syslog(LOG_ERR, "unknown user: %s", name);
1912                 return;
1913         }
1914         cr->cr_ngroups = 0;
1915         while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
1916                 name = strsep(&names, ":");
1917                 if (isdigit(*name) || *name == '-') {
1918                         cr->cr_groups[cr->cr_ngroups++] = atoi(name);
1919                 } else {
1920                         if ((gr = getgrnam(name)) == NULL) {
1921                                 syslog(LOG_ERR, "unknown group: %s", name);
1922                                 continue;
1923                         }
1924                         cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
1925                 }
1926         }
1927         if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
1928                 syslog(LOG_ERR, "too many groups");
1929 }
1930
1931 #define STRSIZ  (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
1932 /*
1933  * Routines that maintain the remote mounttab
1934  */
1935 void
1936 get_mountlist()
1937 {
1938         struct mountlist *mlp, **mlpp;
1939         char *host, *dirp, *cp;
1940         char str[STRSIZ];
1941         FILE *mlfile;
1942
1943         if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
1944                 if (errno == ENOENT)
1945                         return;
1946                 else {
1947                         syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
1948                         return;
1949                 }
1950         }
1951         mlpp = &mlhead;
1952         while (fgets(str, STRSIZ, mlfile) != NULL) {
1953                 cp = str;
1954                 host = strsep(&cp, " \t\n");
1955                 dirp = strsep(&cp, " \t\n");
1956                 if (host == NULL || dirp == NULL)
1957                         continue;
1958                 mlp = (struct mountlist *)malloc(sizeof (*mlp));
1959                 if (mlp == (struct mountlist *)NULL)
1960                         out_of_mem();
1961                 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
1962                 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
1963                 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
1964                 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
1965                 mlp->ml_next = (struct mountlist *)NULL;
1966                 *mlpp = mlp;
1967                 mlpp = &mlp->ml_next;
1968         }
1969         fclose(mlfile);
1970 }
1971
1972 void
1973 del_mlist(hostp, dirp)
1974         char *hostp, *dirp;
1975 {
1976         struct mountlist *mlp, **mlpp;
1977         struct mountlist *mlp2;
1978         FILE *mlfile;
1979         int fnd = 0;
1980
1981         mlpp = &mlhead;
1982         mlp = mlhead;
1983         while (mlp) {
1984                 if (!strcmp(mlp->ml_host, hostp) &&
1985                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
1986                         fnd = 1;
1987                         mlp2 = mlp;
1988                         *mlpp = mlp = mlp->ml_next;
1989                         free((caddr_t)mlp2);
1990                 } else {
1991                         mlpp = &mlp->ml_next;
1992                         mlp = mlp->ml_next;
1993                 }
1994         }
1995         if (fnd) {
1996                 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
1997                         syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
1998                         return;
1999                 }
2000                 mlp = mlhead;
2001                 while (mlp) {
2002                         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2003                         mlp = mlp->ml_next;
2004                 }
2005                 fclose(mlfile);
2006         }
2007 }
2008
2009 void
2010 add_mlist(hostp, dirp)
2011         char *hostp, *dirp;
2012 {
2013         struct mountlist *mlp, **mlpp;
2014         FILE *mlfile;
2015
2016         mlpp = &mlhead;
2017         mlp = mlhead;
2018         while (mlp) {
2019                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2020                         return;
2021                 mlpp = &mlp->ml_next;
2022                 mlp = mlp->ml_next;
2023         }
2024         mlp = (struct mountlist *)malloc(sizeof (*mlp));
2025         if (mlp == (struct mountlist *)NULL)
2026                 out_of_mem();
2027         strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2028         mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2029         strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2030         mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2031         mlp->ml_next = (struct mountlist *)NULL;
2032         *mlpp = mlp;
2033         if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2034                 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2035                 return;
2036         }
2037         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2038         fclose(mlfile);
2039 }
2040
2041 /*
2042  * Free up a group list.
2043  */
2044 void
2045 free_grp(grp)
2046         struct grouplist *grp;
2047 {
2048         char **addrp;
2049
2050         if (grp->gr_type == GT_HOST) {
2051                 if (grp->gr_ptr.gt_hostent->h_name) {
2052                         addrp = grp->gr_ptr.gt_hostent->h_addr_list;
2053                         while (addrp && *addrp)
2054                                 free(*addrp++);
2055                         free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
2056                         free(grp->gr_ptr.gt_hostent->h_name);
2057                 }
2058                 free((caddr_t)grp->gr_ptr.gt_hostent);
2059         } else if (grp->gr_type == GT_NET) {
2060                 if (grp->gr_ptr.gt_net.nt_name)
2061                         free(grp->gr_ptr.gt_net.nt_name);
2062         }
2063         free((caddr_t)grp);
2064 }
2065
2066 #ifdef DEBUG
2067 void
2068 SYSLOG(int pri, const char *fmt, ...)
2069 {
2070         va_list ap;
2071
2072         va_start(ap, fmt);
2073         vfprintf(stderr, fmt, ap);
2074         va_end(ap);
2075 }
2076 #endif /* DEBUG */
2077
2078 /*
2079  * Check options for consistency.
2080  */
2081 int
2082 check_options(dp)
2083         struct dirlist *dp;
2084 {
2085
2086         if (dp == (struct dirlist *)NULL)
2087             return (1);
2088         if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
2089             (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
2090             (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
2091             syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
2092             return (1);
2093         }
2094         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2095                 syslog(LOG_ERR, "-mask requires -network");
2096                 return (1);
2097         }
2098         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2099             syslog(LOG_ERR, "-alldirs has multiple directories");
2100             return (1);
2101         }
2102         return (0);
2103 }
2104
2105 /*
2106  * Check an absolute directory path for any symbolic links. Return true
2107  * if no symbolic links are found.
2108  */
2109 int
2110 check_dirpath(dirp)
2111         char *dirp;
2112 {
2113         char *cp;
2114         int ret = 1;
2115         struct stat sb;
2116
2117         cp = dirp + 1;
2118         while (*cp && ret) {
2119                 if (*cp == '/') {
2120                         *cp = '\0';
2121                         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2122                                 ret = 0;
2123                         *cp = '/';
2124                 }
2125                 cp++;
2126         }
2127         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2128                 ret = 0;
2129         return (ret);
2130 }
2131
2132 /*
2133  * Just translate an ascii string to an integer.
2134  */
2135 int
2136 get_num(cp)
2137         register char *cp;
2138 {
2139         register int res = 0;
2140
2141         while (*cp) {
2142                 if (*cp < '0' || *cp > '9')
2143                         return (-1);
2144                 res = res * 10 + (*cp++ - '0');
2145         }
2146         return (res);
2147 }