-n option allows access from non-priviledged ports (from cgd)
[unix-history] / usr / src / sbin / mountd / mountd.c
index d9070c8..029d55f 100644 (file)
@@ -1,47 +1,59 @@
 /*
 /*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  *
  * This code is derived from software contributed to Berkeley by
- * Rick Macklem at The University of Guelph.
+ * Herb Hasler and Rick Macklem at The University of Guelph.
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
  *
  * %sccs.include.redist.c%
  */
 
 #ifndef lint
-char copyright[] =
-"@(#) Copyright (c) 1989 Regents of the University of California.\n\
- All rights reserved.\n";
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
      The Regents of the University of California.  All rights reserved.\n";
 #endif not lint
 
 #ifndef lint
 #endif not lint
 
 #ifndef lint
-static char sccsid[] = "@(#)mountd.c   5.9 (Berkeley) %G%";
+static char sccsid[] = "@(#)mountd.c   8.10 (Berkeley) %G%";
 #endif not lint
 
 #include <sys/param.h>
 #endif not lint
 
 #include <sys/param.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
 #include <sys/file.h>
 #include <sys/file.h>
+#include <sys/ioctl.h>
 #include <sys/mount.h>
 #include <sys/socket.h>
 #include <sys/mount.h>
 #include <sys/socket.h>
-#include <sys/errno.h>
-#include <sys/signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <syslog.h>
-#include <netdb.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/ucred.h>
+
 #include <rpc/rpc.h>
 #include <rpc/pmap_clnt.h>
 #include <rpc/pmap_prot.h>
 #include <rpc/rpc.h>
 #include <rpc/pmap_clnt.h>
 #include <rpc/pmap_prot.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
 #include <nfs/rpcv2.h>
 #include <nfs/nfsv2.h>
 #include <nfs/rpcv2.h>
 #include <nfs/nfsv2.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
 #include "pathnames.h"
 
 #include "pathnames.h"
 
-struct ufid {
-       u_short ufid_len;
-       ino_t   ufid_ino;
-       long    ufid_gen;
-};
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
 /*
  * Structures for keeping the mount list and export list
  */
 /*
  * Structures for keeping the mount list and export list
  */
@@ -51,33 +63,134 @@ struct mountlist {
        char    ml_dirp[RPCMNT_PATHLEN+1];
 };
 
        char    ml_dirp[RPCMNT_PATHLEN+1];
 };
 
+struct dirlist {
+       struct dirlist  *dp_left;
+       struct dirlist  *dp_right;
+       int             dp_flag;
+       struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
+       char            dp_dirp[1];     /* Actually malloc'd to size of dir */
+};
+/* dp_flag bits */
+#define        DP_DEFSET       0x1
+
 struct exportlist {
        struct exportlist *ex_next;
 struct exportlist {
        struct exportlist *ex_next;
-       struct exportlist *ex_prev;
-       struct grouplist *ex_groups;
-       int     ex_rootuid;
-       int     ex_exflags;
-       dev_t   ex_dev;
-       char    ex_dirp[RPCMNT_PATHLEN+1];
+       struct dirlist  *ex_dirl;
+       struct dirlist  *ex_defdir;
+       int             ex_flag;
+       fsid_t          ex_fs;
+       char            *ex_fsdir;
+};
+/* ex_flag bits */
+#define        EX_LINKED       0x1
+
+struct netmsk {
+       u_long  nt_net;
+       u_long  nt_mask;
+       char *nt_name;
+};
+
+union grouptypes {
+       struct hostent *gt_hostent;
+       struct netmsk   gt_net;
+#ifdef ISO
+       struct sockaddr_iso *gt_isoaddr;
+#endif
 };
 
 struct grouplist {
 };
 
 struct grouplist {
+       int gr_type;
+       union grouptypes gr_ptr;
        struct grouplist *gr_next;
        struct grouplist *gr_next;
-       struct hostent *gr_hp;
+};
+/* Group types */
+#define        GT_NULL         0x0
+#define        GT_HOST         0x1
+#define        GT_NET          0x2
+#define        GT_ISO          0x4
+
+struct hostlist {
+       struct grouplist *ht_grp;
+       struct hostlist  *ht_next;
 };
 
 /* Global defs */
 };
 
 /* Global defs */
-int xdr_fhs(), xdr_mlist(), xdr_dir(), xdr_explist();
-int mntsrv(), get_exportlist(), send_umntall(), umntall_each();
-void get_mountlist(), add_mlist(), del_mlist();
-struct exportlist exphead;
+char   *add_expdir __P((struct dirlist **, char *, int));
+void   add_dlist __P((struct dirlist **, struct dirlist *,
+                               struct grouplist *));
+void   add_mlist __P((char *, char *));
+int    check_dirpath __P((char *));
+int    check_options __P((struct dirlist *));
+int    chk_host __P((struct dirlist *, u_long, int *));
+void   del_mlist __P((char *, char *));
+struct dirlist *dirp_search __P((struct dirlist *, char *));
+int    do_mount __P((struct exportlist *, struct grouplist *, int,
+                               struct ucred *, char *, int, struct statfs *));
+int    do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
+                               int *, int *, struct ucred *));
+struct exportlist *ex_search __P((fsid_t *));
+struct exportlist *get_exp __P((void));
+void   free_dir __P((struct dirlist *));
+void   free_exp __P((struct exportlist *));
+void   free_grp __P((struct grouplist *));
+void   free_host __P((struct hostlist *));
+void   get_exportlist __P((void));
+int    get_host __P((char *, struct grouplist *));
+struct hostlist *get_ht __P((void));
+int    get_line __P((void));
+void   get_mountlist __P((void));
+int    get_net __P((char *, struct netmsk *, int));
+void   getexp_err __P((struct exportlist *, struct grouplist *));
+struct grouplist *get_grp __P((void));
+void   hang_dirp __P((struct dirlist *, struct grouplist *,
+                               struct exportlist *, int));
+void   mntsrv __P((struct svc_req *, SVCXPRT *));
+void   nextfield __P((char **, char **));
+void   out_of_mem __P((void));
+void   parsecred __P((char *, struct ucred *));
+int    put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
+int    scan_tree __P((struct dirlist *, u_long));
+void   send_umntall __P((void));
+int    umntall_each __P((caddr_t, struct sockaddr_in *));
+int    xdr_dir __P((XDR *, char *));
+int    xdr_explist __P((XDR *, caddr_t));
+int    xdr_fhs __P((XDR *, nfsv2fh_t *));
+int    xdr_mlist __P((XDR *, caddr_t));
+
+/* C library */
+int    getnetgrent();
+void   endnetgrent();
+void   setnetgrent();
+
+#ifdef ISO
+struct iso_addr *iso_addr();
+#endif
+
+struct exportlist *exphead;
 struct mountlist *mlhead;
 struct mountlist *mlhead;
+struct grouplist *grphead;
 char exname[MAXPATHLEN];
 char exname[MAXPATHLEN];
-int def_rootuid = -2;
-int root_only = 1;
-extern int errno;
+struct ucred def_anon = {
+       1,
+       (uid_t) -2,
+       1,
+       { (gid_t) -2 }
+};
+int resvport_only = 1;
+int opt_flags;
+/* Bits for above */
+#define        OP_MAPROOT      0x01
+#define        OP_MAPALL       0x02
+#define        OP_KERB         0x04
+#define        OP_MASK         0x08
+#define        OP_NET          0x10
+#define        OP_ISO          0x20
+#define        OP_ALLDIRS      0x40
+
 #ifdef DEBUG
 int debug = 1;
 #ifdef DEBUG
 int debug = 1;
+void   SYSLOG __P((int, const char *, ...));
+#define syslog SYSLOG
 #else
 int debug = 0;
 #endif
 #else
 int debug = 0;
 #endif
@@ -89,19 +202,18 @@ int debug = 0;
  * default: _PATH_EXPORTS
  * and "-n" to allow nonroot mount.
  */
  * default: _PATH_EXPORTS
  * and "-n" to allow nonroot mount.
  */
+int
 main(argc, argv)
        int argc;
        char **argv;
 {
        SVCXPRT *transp;
        int c;
 main(argc, argv)
        int argc;
        char **argv;
 {
        SVCXPRT *transp;
        int c;
-       extern int optind;
-       extern char *optarg;
 
        while ((c = getopt(argc, argv, "n")) != EOF)
                switch (c) {
                case 'n':
 
        while ((c = getopt(argc, argv, "n")) != EOF)
                switch (c) {
                case 'n':
-                       root_only = 0;
+                       resvport_only = 0;
                        break;
                default:
                        fprintf(stderr, "Usage: mountd [-n] [export_file]\n");
                        break;
                default:
                        fprintf(stderr, "Usage: mountd [-n] [export_file]\n");
@@ -109,41 +221,30 @@ main(argc, argv)
                };
        argc -= optind;
        argv += optind;
                };
        argc -= optind;
        argv += optind;
-       exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
-       mlhead = (struct mountlist *)0;
+       grphead = (struct grouplist *)NULL;
+       exphead = (struct exportlist *)NULL;
+       mlhead = (struct mountlist *)NULL;
        if (argc == 1) {
                strncpy(exname, *argv, MAXPATHLEN-1);
                exname[MAXPATHLEN-1] = '\0';
        } else
                strcpy(exname, _PATH_EXPORTS);
        if (argc == 1) {
                strncpy(exname, *argv, MAXPATHLEN-1);
                exname[MAXPATHLEN-1] = '\0';
        } else
                strcpy(exname, _PATH_EXPORTS);
+       openlog("mountd", LOG_PID, LOG_DAEMON);
+       if (debug)
+               fprintf(stderr,"Getting export list.\n");
        get_exportlist();
        get_exportlist();
+       if (debug)
+               fprintf(stderr,"Getting mount list.\n");
        get_mountlist();
        get_mountlist();
+       if (debug)
+               fprintf(stderr,"Here we go.\n");
        if (debug == 0) {
        if (debug == 0) {
-               if (fork())
-                       exit(0);
-               { int s;
-               for (s = 0; s < 10; s++)
-                       (void) close(s);
-               }
-               (void) open("/", O_RDONLY);
-               (void) dup2(0, 1);
-               (void) dup2(0, 2);
-               { int tt = open("/dev/tty", O_RDWR);
-                 if (tt > 0) {
-                       ioctl(tt, TIOCNOTTY, (char *)0);
-                       close(tt);
-                 }
-               }
-               (void) setpgrp(0, 0);
-               signal(SIGTSTP, SIG_IGN);
-               signal(SIGTTIN, SIG_IGN);
-               signal(SIGTTOU, SIG_IGN);
+               daemon(0, 0);
                signal(SIGINT, SIG_IGN);
                signal(SIGQUIT, SIG_IGN);
        }
                signal(SIGINT, SIG_IGN);
                signal(SIGQUIT, SIG_IGN);
        }
-       openlog("mountd:", LOG_PID, LOG_DAEMON);
-       signal(SIGHUP, get_exportlist);
-       signal(SIGTERM, send_umntall);
+       signal(SIGHUP, (void (*) __P((int))) get_exportlist);
+       signal(SIGTERM, (void (*) __P((int))) send_umntall);
        { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
          if (pidfile != NULL) {
                fprintf(pidfile, "%d\n", getpid());
        { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
          if (pidfile != NULL) {
                fprintf(pidfile, "%d\n", getpid());
@@ -155,65 +256,64 @@ main(argc, argv)
                exit(1);
        }
        pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
                exit(1);
        }
        pmap_unset(RPCPROG_MNT, RPCMNT_VER1);
-       if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv, IPPROTO_UDP)) {
+       if (!svc_register(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,
+           IPPROTO_UDP)) {
                syslog(LOG_ERR, "Can't register mount");
                exit(1);
        }
        svc_run();
        syslog(LOG_ERR, "Mountd died");
                syslog(LOG_ERR, "Can't register mount");
                exit(1);
        }
        svc_run();
        syslog(LOG_ERR, "Mountd died");
+       exit(1);
 }
 
 /*
  * The mount rpc service
  */
 }
 
 /*
  * The mount rpc service
  */
+void
 mntsrv(rqstp, transp)
 mntsrv(rqstp, transp)
-       register struct svc_req *rqstp;
-       register SVCXPRT *transp;
+       struct svc_req *rqstp;
+       SVCXPRT *transp;
 {
 {
-       register struct grouplist *grp;
-       register u_long **addrp;
-       register struct exportlist *ep;
+       struct exportlist *ep;
+       struct dirlist *dp;
        nfsv2fh_t nfh;
        nfsv2fh_t nfh;
-       struct authunix_parms *ucr;
        struct stat stb;
        struct stat stb;
+       struct statfs fsb;
        struct hostent *hp;
        u_long saddr;
        struct hostent *hp;
        u_long saddr;
-       char dirpath[RPCMNT_PATHLEN+1];
-       int bad = ENOENT;
-       int omask;
-       uid_t uid = -2;
-
-       /* Get authorization */
-       switch (rqstp->rq_cred.oa_flavor) {
-       case AUTH_UNIX:
-               ucr = (struct authunix_parms *)rqstp->rq_clntcred;
-               uid = ucr->aup_uid;
-               break;
-       case AUTH_NULL:
-       default:
-               break;
-       }
+       u_short sport;
+       char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN];
+       int bad = ENOENT, omask, defset;
 
        saddr = transp->xp_raddr.sin_addr.s_addr;
 
        saddr = transp->xp_raddr.sin_addr.s_addr;
-       hp = (struct hostent *)0;
+       sport = ntohs(transp->xp_raddr.sin_port);
+       hp = (struct hostent *)NULL;
        switch (rqstp->rq_proc) {
        case NULLPROC:
        switch (rqstp->rq_proc) {
        case NULLPROC:
-               if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
+               if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
                        syslog(LOG_ERR, "Can't send reply");
                return;
        case RPCMNT_MOUNT:
                        syslog(LOG_ERR, "Can't send reply");
                return;
        case RPCMNT_MOUNT:
-               if (uid != 0 && root_only) {
+               if (sport >= IPPORT_RESERVED && resvport_only) {
                        svcerr_weakauth(transp);
                        return;
                }
                        svcerr_weakauth(transp);
                        return;
                }
-               if (!svc_getargs(transp, xdr_dir, dirpath)) {
+               if (!svc_getargs(transp, xdr_dir, rpcpath)) {
                        svcerr_decode(transp);
                        return;
                }
 
                        svcerr_decode(transp);
                        return;
                }
 
-               /* Check to see if it's a valid dirpath */
-               if (stat(dirpath, &stb) < 0 || (stb.st_mode&S_IFMT) !=
-                       S_IFDIR) {
+               /*
+                * Get the real pathname and make sure it is a file or
+                * directory that exists.
+                */
+               if (realpath(rpcpath, dirpath) == 0 ||
+                   stat(dirpath, &stb) < 0 ||
+                   (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
+                   statfs(dirpath, &fsb) < 0) {
+                       chdir("/");     /* Just in case realpath doesn't */
+                       if (debug)
+                               fprintf(stderr, "stat failed on %s\n", dirpath);
                        if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
                                syslog(LOG_ERR, "Can't send reply");
                        return;
                        if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
                                syslog(LOG_ERR, "Can't send reply");
                        return;
@@ -221,64 +321,49 @@ mntsrv(rqstp, transp)
 
                /* Check in the exports list */
                omask = sigblock(sigmask(SIGHUP));
 
                /* Check in the exports list */
                omask = sigblock(sigmask(SIGHUP));
-               ep = exphead.ex_next;
-               while (ep != NULL) {
-                       if (!strcmp(ep->ex_dirp, dirpath)) {
-                               grp = ep->ex_groups;
-                               if (grp == NULL)
-                                       break;
-
-                               /* Check for a host match */
-                               addrp = (u_long **)grp->gr_hp->h_addr_list;
-                               for (;;) {
-                                       if (**addrp == saddr)
-                                               break;
-                                       if (*++addrp == NULL)
-                                               if (grp = grp->gr_next) {
-                                                       addrp = (u_long **)
-                                                               grp->gr_hp->h_addr_list;
-                                               } else {
-                                                       bad = EACCES;
-                                                       if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
-                                                               syslog(LOG_ERR, "Can't send reply");
-                                                       sigsetmask(omask);
-                                                       return;
-                                               }
-                               }
-                               hp = grp->gr_hp;
-                               break;
+               ep = ex_search(&fsb.f_fsid);
+               defset = 0;
+               if (ep && (chk_host(ep->ex_defdir, saddr, &defset) ||
+                   ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+                    chk_host(dp, saddr, &defset)) ||
+                    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+                     scan_tree(ep->ex_dirl, saddr) == 0))) {
+                       /* Get the file handle */
+                       bzero((caddr_t)&nfh, sizeof(nfh));
+                       if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
+                               bad = errno;
+                               syslog(LOG_ERR, "Can't get fh for %s", dirpath);
+                               if (!svc_sendreply(transp, xdr_long,
+                                   (caddr_t)&bad))
+                                       syslog(LOG_ERR, "Can't send reply");
+                               sigsetmask(omask);
+                               return;
                        }
                        }
-                       ep = ep->ex_next;
-               }
-               sigsetmask(omask);
-               if (ep == NULL) {
-                       bad = EACCES;
-                       if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
+                       if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
                                syslog(LOG_ERR, "Can't send reply");
                                syslog(LOG_ERR, "Can't send reply");
-                       return;
-               }
-
-               /* Get the file handle */
-               bzero((caddr_t)&nfh, sizeof(nfh));
-               if (getfh(dirpath, (fhandle_t *)&nfh) < 0) {
-                       bad = errno;
+                       if (hp == NULL)
+                               hp = gethostbyaddr((caddr_t)&saddr,
+                                   sizeof(saddr), AF_INET);
+                       if (hp)
+                               add_mlist(hp->h_name, dirpath);
+                       else
+                               add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
+                                       dirpath);
+                       if (debug)
+                               fprintf(stderr,"Mount successfull.\n");
+               } else {
+                       bad = EACCES;
                        if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
                                syslog(LOG_ERR, "Can't send reply");
                        if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad))
                                syslog(LOG_ERR, "Can't send reply");
-                       return;
                }
                }
-               if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&nfh))
-                       syslog(LOG_ERR, "Can't send reply");
-               if (hp == NULL)
-                       hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
-               if (hp)
-                       add_mlist(hp->h_name, dirpath);
+               sigsetmask(omask);
                return;
        case RPCMNT_DUMP:
                return;
        case RPCMNT_DUMP:
-               if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
+               if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
                        syslog(LOG_ERR, "Can't send reply");
                return;
        case RPCMNT_UMOUNT:
                        syslog(LOG_ERR, "Can't send reply");
                return;
        case RPCMNT_UMOUNT:
-               if (uid != 0 && root_only) {
+               if (sport >= IPPORT_RESERVED && resvport_only) {
                        svcerr_weakauth(transp);
                        return;
                }
                        svcerr_weakauth(transp);
                        return;
                }
@@ -286,25 +371,27 @@ mntsrv(rqstp, transp)
                        svcerr_decode(transp);
                        return;
                }
                        svcerr_decode(transp);
                        return;
                }
-               if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
+               if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
                        syslog(LOG_ERR, "Can't send reply");
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
                        del_mlist(hp->h_name, dirpath);
                        syslog(LOG_ERR, "Can't send reply");
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
                        del_mlist(hp->h_name, dirpath);
+               del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
                return;
        case RPCMNT_UMNTALL:
                return;
        case RPCMNT_UMNTALL:
-               if (uid != 0 && root_only) {
+               if (sport >= IPPORT_RESERVED && resvport_only) {
                        svcerr_weakauth(transp);
                        return;
                }
                        svcerr_weakauth(transp);
                        return;
                }
-               if (!svc_sendreply(transp, xdr_void, (caddr_t)0))
+               if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
                        syslog(LOG_ERR, "Can't send reply");
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
                        syslog(LOG_ERR, "Can't send reply");
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
-                       del_mlist(hp->h_name, (char *)0);
+                       del_mlist(hp->h_name, (char *)NULL);
+               del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
                return;
        case RPCMNT_EXPORT:
                return;
        case RPCMNT_EXPORT:
-               if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
+               if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
                        syslog(LOG_ERR, "Can't send reply");
                return;
        default:
                        syslog(LOG_ERR, "Can't send reply");
                return;
        default:
@@ -316,6 +403,7 @@ mntsrv(rqstp, transp)
 /*
  * Xdr conversion for a dirpath string
  */
 /*
  * Xdr conversion for a dirpath string
  */
+int
 xdr_dir(xdrsp, dirp)
        XDR *xdrsp;
        char *dirp;
 xdr_dir(xdrsp, dirp)
        XDR *xdrsp;
        char *dirp;
@@ -326,6 +414,7 @@ xdr_dir(xdrsp, dirp)
 /*
  * Xdr routine to generate fhstatus
  */
 /*
  * Xdr routine to generate fhstatus
  */
+int
 xdr_fhs(xdrsp, nfh)
        XDR *xdrsp;
        nfsv2fh_t *nfh;
 xdr_fhs(xdrsp, nfh)
        XDR *xdrsp;
        nfsv2fh_t *nfh;
@@ -337,11 +426,12 @@ xdr_fhs(xdrsp, nfh)
        return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
 }
 
        return (xdr_opaque(xdrsp, (caddr_t)nfh, NFSX_FH));
 }
 
+int
 xdr_mlist(xdrsp, cp)
        XDR *xdrsp;
        caddr_t cp;
 {
 xdr_mlist(xdrsp, cp)
        XDR *xdrsp;
        caddr_t cp;
 {
-       register struct mountlist *mlp;
+       struct mountlist *mlp;
        int true = 1;
        int false = 0;
        char *strp;
        int true = 1;
        int false = 0;
        char *strp;
@@ -366,35 +456,24 @@ xdr_mlist(xdrsp, cp)
 /*
  * Xdr conversion for export list
  */
 /*
  * Xdr conversion for export list
  */
+int
 xdr_explist(xdrsp, cp)
        XDR *xdrsp;
        caddr_t cp;
 {
 xdr_explist(xdrsp, cp)
        XDR *xdrsp;
        caddr_t cp;
 {
-       register struct exportlist *ep;
-       register struct grouplist *grp;
-       int true = 1;
+       struct exportlist *ep;
        int false = 0;
        int false = 0;
-       char *strp;
-       int omask;
+       int omask, putdef;
 
        omask = sigblock(sigmask(SIGHUP));
 
        omask = sigblock(sigmask(SIGHUP));
-       ep = exphead.ex_next;
-       while (ep != NULL) {
-               if (!xdr_bool(xdrsp, &true))
-                       goto errout;
-               strp = &ep->ex_dirp[0];
-               if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+       ep = exphead;
+       while (ep) {
+               putdef = 0;
+               if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
                        goto errout;
                        goto errout;
-               grp = ep->ex_groups;
-               while (grp != NULL) {
-                       if (!xdr_bool(xdrsp, &true))
-                               goto errout;
-                       strp = grp->gr_hp->h_name;
-                       if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
-                               goto errout;
-                       grp = grp->gr_next;
-               }
-               if (!xdr_bool(xdrsp, &false))
+               if (ep->ex_defdir && putdef == 0 &&
+                       put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
+                       &putdef))
                        goto errout;
                ep = ep->ex_next;
        }
                        goto errout;
                ep = ep->ex_next;
        }
@@ -407,271 +486,1267 @@ errout:
        return (0);
 }
 
        return (0);
 }
 
+/*
+ * Called from xdr_explist() to traverse the tree and export the
+ * directory paths.
+ */
+int
+put_exlist(dp, xdrsp, adp, putdefp)
+       struct dirlist *dp;
+       XDR *xdrsp;
+       struct dirlist *adp;
+       int *putdefp;
+{
+       struct grouplist *grp;
+       struct hostlist *hp;
+       int true = 1;
+       int false = 0;
+       int gotalldir = 0;
+       char *strp;
+
+       if (dp) {
+               if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
+                       return (1);
+               if (!xdr_bool(xdrsp, &true))
+                       return (1);
+               strp = dp->dp_dirp;
+               if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+                       return (1);
+               if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
+                       gotalldir = 1;
+                       *putdefp = 1;
+               }
+               if ((dp->dp_flag & DP_DEFSET) == 0 &&
+                   (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
+                       hp = dp->dp_hosts;
+                       while (hp) {
+                               grp = hp->ht_grp;
+                               if (grp->gr_type == GT_HOST) {
+                                       if (!xdr_bool(xdrsp, &true))
+                                               return (1);
+                                       strp = grp->gr_ptr.gt_hostent->h_name;
+                                       if (!xdr_string(xdrsp, &strp, 
+                                           RPCMNT_NAMELEN))
+                                               return (1);
+                               } else if (grp->gr_type == GT_NET) {
+                                       if (!xdr_bool(xdrsp, &true))
+                                               return (1);
+                                       strp = grp->gr_ptr.gt_net.nt_name;
+                                       if (!xdr_string(xdrsp, &strp, 
+                                           RPCMNT_NAMELEN))
+                                               return (1);
+                               }
+                               hp = hp->ht_next;
+                               if (gotalldir && hp == (struct hostlist *)NULL) {
+                                       hp = adp->dp_hosts;
+                                       gotalldir = 0;
+                               }
+                       }
+               }
+               if (!xdr_bool(xdrsp, &false))
+                       return (1);
+               if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
+                       return (1);
+       }
+       return (0);
+}
+
 #define LINESIZ        10240
 char line[LINESIZ];
 #define LINESIZ        10240
 char line[LINESIZ];
+FILE *exp_file;
 
 /*
  * Get the export list
  */
 
 /*
  * Get the export list
  */
+void
 get_exportlist()
 {
 get_exportlist()
 {
-       register struct hostent *hp, *nhp;
-       register char **addrp, **naddrp;
-       register int i;
-       register struct grouplist *grp;
-       register struct exportlist *ep, *ep2;
-       struct ufs_args args;
-       struct stat sb;
-       FILE *inf;
-       char *cp, *endcp;
-       char savedc;
-       int len, dirplen;
-       int rootuid, exflags;
-       u_long saddr;
-       struct exportlist *fep;
+       struct exportlist *ep, *ep2;
+       struct grouplist *grp, *tgrp;
+       struct exportlist **epp;
+       struct dirlist *dirhead;
+       struct statfs fsb, *fsp;
+       struct hostent *hpe;
+       struct ucred anon;
+       char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+       int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
 
        /*
         * First, get rid of the old list
         */
 
        /*
         * First, get rid of the old list
         */
-       ep = exphead.ex_next;
-       while (ep != NULL) {
+       ep = exphead;
+       while (ep) {
                ep2 = ep;
                ep = ep->ex_next;
                free_exp(ep2);
        }
                ep2 = ep;
                ep = ep->ex_next;
                free_exp(ep2);
        }
+       exphead = (struct exportlist *)NULL;
+
+       grp = grphead;
+       while (grp) {
+               tgrp = grp;
+               grp = grp->gr_next;
+               free_grp(tgrp);
+       }
+       grphead = (struct grouplist *)NULL;
+
+       /*
+        * And delete exports that are in the kernel for all local
+        * file systems.
+        * XXX: Should know how to handle all local exportable file systems
+        *      instead of just MOUNT_UFS.
+        */
+       num = getmntinfo(&fsp, MNT_NOWAIT);
+       for (i = 0; i < num; i++) {
+               union {
+                       struct ufs_args ua;
+                       struct iso_args ia;
+                       struct mfs_args ma;
+               } targs;
+
+               switch (fsp->f_type) {
+               case MOUNT_MFS:
+               case MOUNT_UFS:
+               case MOUNT_CD9660:
+                       targs.ua.fspec = NULL;
+                       targs.ua.export.ex_flags = MNT_DELEXPORT;
+                       if (mount(fsp->f_type, fsp->f_mntonname,
+                                 fsp->f_flags | MNT_UPDATE,
+                                 (caddr_t)&targs) < 0)
+                               syslog(LOG_ERR, "Can't delete exports for %s",
+                                      fsp->f_mntonname);
+               }
+               fsp++;
+       }
 
        /*
         * Read in the exports file and build the list, calling
 
        /*
         * Read in the exports file and build the list, calling
-        * exportfs() as we go along
+        * mount() as we go along to push the export rules into the kernel.
         */
         */
-       exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
-       if ((inf = fopen(exname, "r")) == NULL) {
+       if ((exp_file = fopen(exname, "r")) == NULL) {
                syslog(LOG_ERR, "Can't open %s", exname);
                exit(2);
        }
                syslog(LOG_ERR, "Can't open %s", exname);
                exit(2);
        }
-       while (fgets(line, LINESIZ, inf)) {
-               exflags = MNT_EXPORTED;
-               rootuid = def_rootuid;
+       dirhead = (struct dirlist *)NULL;
+       while (get_line()) {
+               if (debug)
+                       fprintf(stderr,"Got line %s\n",line);
                cp = line;
                nextfield(&cp, &endcp);
                cp = line;
                nextfield(&cp, &endcp);
+               if (*cp == '#')
+                       goto nextline;
 
                /*
 
                /*
-                * Get file system devno and see if an entry for this
-                * file system already exists.
+                * Set defaults.
                 */
                 */
-               savedc = *endcp;
-               *endcp = '\0';
-               if (stat(cp, &sb) < 0 || (sb.st_mode & S_IFMT) != S_IFDIR)
-                       goto err;
-               fep = (struct exportlist *)0;
-               ep = exphead.ex_next;
-               while (ep) {
-                       if (ep->ex_dev == sb.st_dev) {
-                               fep = ep;
-                               break;
-                       }
-                       ep = ep->ex_next;
-               }
-               *endcp = savedc;
+               has_host = FALSE;
+               anon = def_anon;
+               exflags = MNT_EXPORTED;
+               got_nondir = 0;
+               opt_flags = 0;
+               ep = (struct exportlist *)NULL;
 
                /*
                 * Create new exports list entry
                 */
                len = endcp-cp;
 
                /*
                 * Create new exports list entry
                 */
                len = endcp-cp;
-               if (len <= RPCMNT_PATHLEN && len > 0) {
-                       ep = (struct exportlist *)malloc(sizeof(*ep));
-                       ep->ex_next = ep->ex_prev = (struct exportlist *)0;
-                       ep->ex_groups = (struct grouplist *)0;
-                       bcopy(cp, ep->ex_dirp, len);
-                       ep->ex_dirp[len] = '\0';
-                       dirplen = len;
-               } else
-                       goto err;
-               cp = endcp;
-               nextfield(&cp, &endcp);
-               len = endcp-cp;
+               tgrp = grp = get_grp();
                while (len > 0) {
                while (len > 0) {
-                       savedc = *endcp;
-                       *endcp = '\0';
-                       if (len <= RPCMNT_NAMELEN) {
-                               if (*cp == '-') {
-                                   do_opt(cp+1, fep, ep, &exflags, &rootuid);
+                       if (len > RPCMNT_NAMELEN) {
+                           getexp_err(ep, tgrp);
+                           goto nextline;
+                       }
+                       if (*cp == '-') {
+                           if (ep == (struct exportlist *)NULL) {
+                               getexp_err(ep, tgrp);
+                               goto nextline;
+                           }
+                           if (debug)
+                               fprintf(stderr, "doing opt %s\n", cp);
+                           got_nondir = 1;
+                           if (do_opt(&cp, &endcp, ep, grp, &has_host,
+                               &exflags, &anon)) {
+                               getexp_err(ep, tgrp);
+                               goto nextline;
+                           }
+                       } else if (*cp == '/') {
+                           savedc = *endcp;
+                           *endcp = '\0';
+                           if (check_dirpath(cp) &&
+                               statfs(cp, &fsb) >= 0) {
+                               if (got_nondir) {
+                                   syslog(LOG_ERR, "Dirs must be first");
+                                   getexp_err(ep, tgrp);
+                                   goto nextline;
+                               }
+                               if (ep) {
+                                   if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
+                                       ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
+                                       getexp_err(ep, tgrp);
+                                       goto nextline;
+                                   }
                                } else {
                                } else {
-                                   if (isdigit(*cp)) {
-                                       saddr = inet_addr(cp);
-                                       if (saddr == -1 ||
-                                           (hp = gethostbyaddr((caddr_t)&saddr,
-                                            sizeof(saddr), AF_INET)) == NULL)
-                                               goto err;
-                                   } else if ((hp = gethostbyname(cp)) == NULL)
-                                       goto err;
-                                   grp = (struct grouplist *)
-                                           malloc(sizeof(struct grouplist));
-                                   if (grp == NULL)
-                                           goto err;
-                                   nhp = grp->gr_hp = (struct hostent *)
-                                           malloc(sizeof(struct hostent));
-                                   if (nhp == NULL)
-                                           goto err;
-                                   bcopy((caddr_t)hp, (caddr_t)nhp,
-                                           sizeof(struct hostent));
-                                   i = strlen(hp->h_name)+1;
-                                   nhp->h_name = (char *)malloc(i);
-                                   if (nhp->h_name == NULL)
-                                           goto err;
-                                   bcopy(hp->h_name, nhp->h_name, i);
-                                   addrp = hp->h_addr_list;
-                                   i = 1;
-                                   while (*addrp++)
-                                           i++;
-                                   naddrp = nhp->h_addr_list = (char **)
-                                           malloc(i*sizeof(char *));
-                                   if (naddrp == NULL)
-                                           goto err;
-                                   addrp = hp->h_addr_list;
-                                   while (*addrp) {
-                                           *naddrp = (char *)
-                                               malloc(hp->h_length);
-                                           if (*naddrp == NULL)
-                                               goto err;
-                                           bcopy(*addrp, *naddrp,
-                                                   hp->h_length);
-                                           addrp++;
-                                           naddrp++;
+                                   /*
+                                    * See if this directory is already
+                                    * in the list.
+                                    */
+                                   ep = ex_search(&fsb.f_fsid);
+                                   if (ep == (struct exportlist *)NULL) {
+                                       ep = get_exp();
+                                       ep->ex_fs = fsb.f_fsid;
+                                       ep->ex_fsdir = (char *)
+                                           malloc(strlen(fsb.f_mntonname) + 1);
+                                       if (ep->ex_fsdir)
+                                           strcpy(ep->ex_fsdir,
+                                               fsb.f_mntonname);
+                                       else
+                                           out_of_mem();
+                                       if (debug)
+                                         fprintf(stderr,
+                                             "Making new ep fs=0x%x,0x%x\n",
+                                             fsb.f_fsid.val[0],
+                                             fsb.f_fsid.val[1]);
+                                   } else if (debug)
+                                       fprintf(stderr,
+                                           "Found ep fs=0x%x,0x%x\n",
+                                           fsb.f_fsid.val[0],
+                                           fsb.f_fsid.val[1]);
+                               }
+
+                               /*
+                                * Add dirpath to export mount point.
+                                */
+                               dirp = add_expdir(&dirhead, cp, len);
+                               dirplen = len;
+                           } else {
+                               getexp_err(ep, tgrp);
+                               goto nextline;
+                           }
+                           *endcp = savedc;
+                       } else {
+                           savedc = *endcp;
+                           *endcp = '\0';
+                           got_nondir = 1;
+                           if (ep == (struct exportlist *)NULL) {
+                               getexp_err(ep, tgrp);
+                               goto nextline;
+                           }
+
+                           /*
+                            * Get the host or netgroup.
+                            */
+                           setnetgrent(cp);
+                           netgrp = getnetgrent(&hst, &usr, &dom);
+                           do {
+                               if (has_host) {
+                                   grp->gr_next = get_grp();
+                                   grp = grp->gr_next;
+                               }
+                               if (netgrp) {
+                                   if (get_host(hst, grp)) {
+                                       syslog(LOG_ERR, "Bad netgroup %s", cp);
+                                       getexp_err(ep, tgrp);
+                                       goto nextline;
                                    }
                                    }
-                                   *naddrp = (char *)0;
-                                   grp->gr_next = ep->ex_groups;
-                                   ep->ex_groups = grp;
+                               } else if (get_host(cp, grp)) {
+                                   getexp_err(ep, tgrp);
+                                   goto nextline;
                                }
                                }
+                               has_host = TRUE;
+                           } while (netgrp && getnetgrent(&hst, &usr, &dom));
+                           endnetgrent();
+                           *endcp = savedc;
                        }
                        cp = endcp;
                        }
                        cp = endcp;
-                       *cp = savedc;
                        nextfield(&cp, &endcp);
                        nextfield(&cp, &endcp);
-                       len = endcp-cp;
-               }
-               if (fep == NULL) {
-                       args.fspec = 0;
-                       args.exflags = exflags;
-                       args.exroot = rootuid;
-                       cp = (char *)0;
-                       while (mount(MOUNT_UFS, ep->ex_dirp, MNT_UPDATE, &args) < 0) {
-                               if (cp == NULL)
-                                       cp = ep->ex_dirp + dirplen - 1;
-                               else
-                                       *cp = savedc;
-                               /* back up over the last component */
-                               while (*cp == '/' && cp > ep->ex_dirp)
-                                       cp--;
-                               while (*(cp - 1) != '/' && cp > ep->ex_dirp)
-                                       cp--;
-                               if (cp == ep->ex_dirp) {
-                                       syslog(LOG_WARNING,
-                                             "Can't export %s", ep->ex_dirp);
-                                       free_exp(ep);
-                                       goto nextline;
-                               }
-                               savedc = *cp;
-                               *cp = '\0';
+                       len = endcp - cp;
+               }
+               if (check_options(dirhead)) {
+                       getexp_err(ep, tgrp);
+                       goto nextline;
+               }
+               if (!has_host) {
+                       grp->gr_type = GT_HOST;
+                       if (debug)
+                               fprintf(stderr,"Adding a default entry\n");
+                       /* add a default group and make the grp list NULL */
+                       hpe = (struct hostent *)malloc(sizeof(struct hostent));
+                       if (hpe == (struct hostent *)NULL)
+                               out_of_mem();
+                       hpe->h_name = "Default";
+                       hpe->h_addrtype = AF_INET;
+                       hpe->h_length = sizeof (u_long);
+                       hpe->h_addr_list = (char **)NULL;
+                       grp->gr_ptr.gt_hostent = hpe;
+
+               /*
+                * Don't allow a network export coincide with a list of
+                * host(s) on the same line.
+                */
+               } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
+                       getexp_err(ep, tgrp);
+                       goto nextline;
+               }
+
+               /*
+                * Loop through hosts, pushing the exports into the kernel.
+                * After loop, tgrp points to the start of the list and
+                * grp points to the last entry in the list.
+                */
+               grp = tgrp;
+               do {
+                   if (do_mount(ep, grp, exflags, &anon, dirp,
+                       dirplen, &fsb)) {
+                       getexp_err(ep, tgrp);
+                       goto nextline;
+                   }
+               } while (grp->gr_next && (grp = grp->gr_next));
+
+               /*
+                * Success. Update the data structures.
+                */
+               if (has_host) {
+                       hang_dirp(dirhead, tgrp, ep, (opt_flags & OP_ALLDIRS));
+                       grp->gr_next = grphead;
+                       grphead = tgrp;
+               } else {
+                       hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+                       (opt_flags & OP_ALLDIRS));
+                       free_grp(grp);
+               }
+               dirhead = (struct dirlist *)NULL;
+               if ((ep->ex_flag & EX_LINKED) == 0) {
+                       ep2 = exphead;
+                       epp = &exphead;
+
+                       /*
+                        * Insert in the list in alphabetical order.
+                        */
+                       while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
+                               epp = &ep2->ex_next;
+                               ep2 = ep2->ex_next;
                        }
                        }
-                       if (cp)
+                       if (ep2)
+                               ep->ex_next = ep2;
+                       *epp = ep;
+                       ep->ex_flag |= EX_LINKED;
+               }
+nextline:
+               if (dirhead) {
+                       free_dir(dirhead);
+                       dirhead = (struct dirlist *)NULL;
+               }
+       }
+       fclose(exp_file);
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+       struct exportlist *ep;
+
+       ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+       if (ep == (struct exportlist *)NULL)
+               out_of_mem();
+       bzero((caddr_t)ep, sizeof (struct exportlist));
+       return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+       struct grouplist *gp;
+
+       gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+       if (gp == (struct grouplist *)NULL)
+               out_of_mem();
+       bzero((caddr_t)gp, sizeof (struct grouplist));
+       return (gp);
+}
+
+/*
+ * Clean up upon an error in get_exportlist().
+ */
+void
+getexp_err(ep, grp)
+       struct exportlist *ep;
+       struct grouplist *grp;
+{
+       struct grouplist *tgrp;
+
+       syslog(LOG_ERR, "Bad exports list line %s", line);
+       if (ep && (ep->ex_flag & EX_LINKED) == 0)
+               free_exp(ep);
+       while (grp) {
+               tgrp = grp;
+               grp = grp->gr_next;
+               free_grp(tgrp);
+       }
+}
+
+/*
+ * Search the export list for a matching fs.
+ */
+struct exportlist *
+ex_search(fsid)
+       fsid_t *fsid;
+{
+       struct exportlist *ep;
+
+       ep = exphead;
+       while (ep) {
+               if (ep->ex_fs.val[0] == fsid->val[0] &&
+                   ep->ex_fs.val[1] == fsid->val[1])
+                       return (ep);
+               ep = ep->ex_next;
+       }
+       return (ep);
+}
+
+/*
+ * Add a directory path to the list.
+ */
+char *
+add_expdir(dpp, cp, len)
+       struct dirlist **dpp;
+       char *cp;
+       int len;
+{
+       struct dirlist *dp;
+
+       dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+       dp->dp_left = *dpp;
+       dp->dp_right = (struct dirlist *)NULL;
+       dp->dp_flag = 0;
+       dp->dp_hosts = (struct hostlist *)NULL;
+       strcpy(dp->dp_dirp, cp);
+       *dpp = dp;
+       return (dp->dp_dirp);
+}
+
+/*
+ * Hang the dir list element off the dirpath binary tree as required
+ * and update the entry for host.
+ */
+void
+hang_dirp(dp, grp, ep, alldirs)
+       struct dirlist *dp;
+       struct grouplist *grp;
+       struct exportlist *ep;
+       int alldirs;
+{
+       struct hostlist *hp;
+       struct dirlist *dp2;
+
+       if (alldirs) {
+               if (ep->ex_defdir)
+                       free((caddr_t)dp);
+               else
+                       ep->ex_defdir = dp;
+               if (grp == (struct grouplist *)NULL)
+                       ep->ex_defdir->dp_flag |= DP_DEFSET;
+               else while (grp) {
+                       hp = get_ht();
+                       hp->ht_grp = grp;
+                       hp->ht_next = ep->ex_defdir->dp_hosts;
+                       ep->ex_defdir->dp_hosts = hp;
+                       grp = grp->gr_next;
+               }
+       } else {
+
+               /*
+                * Loop throught the directories adding them to the tree.
+                */
+               while (dp) {
+                       dp2 = dp->dp_left;
+                       add_dlist(&ep->ex_dirl, dp, grp);
+                       dp = dp2;
+               }
+       }
+}
+
+/*
+ * Traverse the binary tree either updating a node that is already there
+ * for the new directory or adding the new node.
+ */
+void
+add_dlist(dpp, newdp, grp)
+       struct dirlist **dpp;
+       struct dirlist *newdp;
+       struct grouplist *grp;
+{
+       struct dirlist *dp;
+       struct hostlist *hp;
+       int cmp;
+
+       dp = *dpp;
+       if (dp) {
+               cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
+               if (cmp > 0) {
+                       add_dlist(&dp->dp_left, newdp, grp);
+                       return;
+               } else if (cmp < 0) {
+                       add_dlist(&dp->dp_right, newdp, grp);
+                       return;
+               } else
+                       free((caddr_t)newdp);
+       } else {
+               dp = newdp;
+               dp->dp_left = (struct dirlist *)NULL;
+               *dpp = dp;
+       }
+       if (grp) {
+
+               /*
+                * Hang all of the host(s) off of the directory point.
+                */
+               do {
+                       hp = get_ht();
+                       hp->ht_grp = grp;
+                       hp->ht_next = dp->dp_hosts;
+                       dp->dp_hosts = hp;
+                       grp = grp->gr_next;
+               } while (grp);
+       } else
+               dp->dp_flag |= DP_DEFSET;
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirpath)
+       struct dirlist *dp;
+       char *dirpath;
+{
+       int cmp;
+
+       if (dp) {
+               cmp = strcmp(dp->dp_dirp, dirpath);
+               if (cmp > 0)
+                       return (dirp_search(dp->dp_left, dirpath));
+               else if (cmp < 0)
+                       return (dirp_search(dp->dp_right, dirpath));
+               else
+                       return (dp);
+       }
+       return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp)
+       struct dirlist *dp;
+       u_long saddr;
+       int *defsetp;
+{
+       struct hostlist *hp;
+       struct grouplist *grp;
+       u_long **addrp;
+
+       if (dp) {
+               if (dp->dp_flag & DP_DEFSET)
+                       *defsetp = 1;
+               hp = dp->dp_hosts;
+               while (hp) {
+                       grp = hp->ht_grp;
+                       switch (grp->gr_type) {
+                       case GT_HOST:
+                           addrp = (u_long **)
+                               grp->gr_ptr.gt_hostent->h_addr_list;
+                           while (*addrp) {
+                               if (**addrp == saddr)
+                                   return (1);
+                               addrp++;
+                           }
+                           break;
+                       case GT_NET:
+                           if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
+                               grp->gr_ptr.gt_net.nt_net)
+                               return (1);
+                           break;
+                       };
+                       hp = hp->ht_next;
+               }
+       }
+       return (0);
+}
+
+/*
+ * Scan tree for a host that matches the address.
+ */
+int
+scan_tree(dp, saddr)
+       struct dirlist *dp;
+       u_long saddr;
+{
+       int defset;
+
+       if (dp) {
+               if (scan_tree(dp->dp_left, saddr))
+                       return (1);
+               if (chk_host(dp, saddr, &defset))
+                       return (1);
+               if (scan_tree(dp->dp_right, saddr))
+                       return (1);
+       }
+       return (0);
+}
+
+/*
+ * Traverse the dirlist tree and free it up.
+ */
+void
+free_dir(dp)
+       struct dirlist *dp;
+{
+
+       if (dp) {
+               free_dir(dp->dp_left);
+               free_dir(dp->dp_right);
+               free_host(dp->dp_hosts);
+               free((caddr_t)dp);
+       }
+}
+
+/*
+ * Parse the option string and update fields.
+ * Option arguments may either be -<option>=<value> or
+ * -<option> <value>
+ */
+int
+do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
+       char **cpp, **endcpp;
+       struct exportlist *ep;
+       struct grouplist *grp;
+       int *has_hostp;
+       int *exflagsp;
+       struct ucred *cr;
+{
+       char *cpoptarg, *cpoptend;
+       char *cp, *endcp, *cpopt, savedc, savedc2;
+       int allflag, usedarg;
+
+       cpopt = *cpp;
+       cpopt++;
+       cp = *endcpp;
+       savedc = *cp;
+       *cp = '\0';
+       while (cpopt && *cpopt) {
+               allflag = 1;
+               usedarg = -2;
+               if (cpoptend = index(cpopt, ',')) {
+                       *cpoptend++ = '\0';
+                       if (cpoptarg = index(cpopt, '='))
+                               *cpoptarg++ = '\0';
+               } else {
+                       if (cpoptarg = index(cpopt, '='))
+                               *cpoptarg++ = '\0';
+                       else {
                                *cp = savedc;
                                *cp = savedc;
-                       ep->ex_rootuid = rootuid;
-                       ep->ex_exflags = exflags;
+                               nextfield(&cp, &endcp);
+                               **endcpp = '\0';
+                               if (endcp > cp && *cp != '-') {
+                                       cpoptarg = cp;
+                                       savedc2 = *endcp;
+                                       *endcp = '\0';
+                                       usedarg = 0;
+                               }
+                       }
+               }
+               if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+                       *exflagsp |= MNT_EXRDONLY;
+               } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
+                   !(allflag = strcmp(cpopt, "mapall")) ||
+                   !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
+                       usedarg++;
+                       parsecred(cpoptarg, cr);
+                       if (allflag == 0) {
+                               *exflagsp |= MNT_EXPORTANON;
+                               opt_flags |= OP_MAPALL;
+                       } else
+                               opt_flags |= OP_MAPROOT;
+               } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
+                       *exflagsp |= MNT_EXKERB;
+                       opt_flags |= OP_KERB;
+               } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
+                       !strcmp(cpopt, "m"))) {
+                       if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
+                               syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
+                               return (1);
+                       }
+                       usedarg++;
+                       opt_flags |= OP_MASK;
+               } else if (cpoptarg && (!strcmp(cpopt, "network") ||
+                       !strcmp(cpopt, "n"))) {
+                       if (grp->gr_type != GT_NULL) {
+                               syslog(LOG_ERR, "Network/host conflict");
+                               return (1);
+                       } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
+                               syslog(LOG_ERR, "Bad net: %s", cpoptarg);
+                               return (1);
+                       }
+                       grp->gr_type = GT_NET;
+                       *has_hostp = 1;
+                       usedarg++;
+                       opt_flags |= OP_NET;
+               } else if (!strcmp(cpopt, "alldirs")) {
+                       opt_flags |= OP_ALLDIRS;
+#ifdef ISO
+               } else if (cpoptarg && !strcmp(cpopt, "iso")) {
+                       if (get_isoaddr(cpoptarg, grp)) {
+                               syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg);
+                               return (1);
+                       }
+                       *has_hostp = 1;
+                       usedarg++;
+                       opt_flags |= OP_ISO;
+#endif /* ISO */
                } else {
                } else {
-                       ep->ex_rootuid = fep->ex_rootuid;
-                       ep->ex_exflags = fep->ex_exflags;
-               }
-               ep->ex_dev = sb.st_dev;
-               ep->ex_next = exphead.ex_next;
-               ep->ex_prev = &exphead;
-               if (ep->ex_next != NULL)
-                       ep->ex_next->ex_prev = ep;
-               exphead.ex_next = ep;
-nextline:
-               ;
+                       syslog(LOG_ERR, "Bad opt %s", cpopt);
+                       return (1);
+               }
+               if (usedarg >= 0) {
+                       *endcp = savedc2;
+                       **endcpp = savedc;
+                       if (usedarg > 0) {
+                               *cpp = cp;
+                               *endcpp = endcp;
+                       }
+                       return (0);
+               }
+               cpopt = cpoptend;
+       }
+       **endcpp = savedc;
+       return (0);
+}
+
+/*
+ * Translate a character string to the corresponding list of network
+ * addresses for a hostname.
+ */
+int
+get_host(cp, grp)
+       char *cp;
+       struct grouplist *grp;
+{
+       struct hostent *hp, *nhp;
+       char **addrp, **naddrp;
+       struct hostent t_host;
+       int i;
+       u_long saddr;
+       char *aptr[2];
+
+       if (grp->gr_type != GT_NULL)
+               return (1);
+       if ((hp = gethostbyname(cp)) == NULL) {
+               if (isdigit(*cp)) {
+                       saddr = inet_addr(cp);
+                       if (saddr == -1) {
+                               syslog(LOG_ERR, "Inet_addr failed");
+                               return (1);
+                       }
+                       if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
+                               AF_INET)) == NULL) {
+                               hp = &t_host;
+                               hp->h_name = cp;
+                               hp->h_addrtype = AF_INET;
+                               hp->h_length = sizeof (u_long);
+                               hp->h_addr_list = aptr;
+                               aptr[0] = (char *)&saddr;
+                               aptr[1] = (char *)NULL;
+                       }
+               } else {
+                       syslog(LOG_ERR, "Gethostbyname failed");
+                       return (1);
+               }
+       }
+       grp->gr_type = GT_HOST;
+       nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
+               malloc(sizeof(struct hostent));
+       if (nhp == (struct hostent *)NULL)
+               out_of_mem();
+       bcopy((caddr_t)hp, (caddr_t)nhp,
+               sizeof(struct hostent));
+       i = strlen(hp->h_name)+1;
+       nhp->h_name = (char *)malloc(i);
+       if (nhp->h_name == (char *)NULL)
+               out_of_mem();
+       bcopy(hp->h_name, nhp->h_name, i);
+       addrp = hp->h_addr_list;
+       i = 1;
+       while (*addrp++)
+               i++;
+       naddrp = nhp->h_addr_list = (char **)
+               malloc(i*sizeof(char *));
+       if (naddrp == (char **)NULL)
+               out_of_mem();
+       addrp = hp->h_addr_list;
+       while (*addrp) {
+               *naddrp = (char *)
+                   malloc(hp->h_length);
+               if (*naddrp == (char *)NULL)
+                   out_of_mem();
+               bcopy(*addrp, *naddrp,
+                       hp->h_length);
+               addrp++;
+               naddrp++;
        }
        }
-       fclose(inf);
-       return;
-err:
-       syslog(LOG_ERR, "Bad Exports File, mountd Failed");
+       *naddrp = (char *)NULL;
+       if (debug)
+               fprintf(stderr, "got host %s\n", hp->h_name);
+       return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+       struct exportlist *ep;
+{
+
+       if (ep->ex_defdir) {
+               free_host(ep->ex_defdir->dp_hosts);
+               free((caddr_t)ep->ex_defdir);
+       }
+       if (ep->ex_fsdir)
+               free(ep->ex_fsdir);
+       free_dir(ep->ex_dirl);
+       free((caddr_t)ep);
+}
+
+/*
+ * Free hosts.
+ */
+void
+free_host(hp)
+       struct hostlist *hp;
+{
+       struct hostlist *hp2;
+
+       while (hp) {
+               hp2 = hp;
+               hp = hp->ht_next;
+               free((caddr_t)hp2);
+       }
+}
+
+struct hostlist *
+get_ht()
+{
+       struct hostlist *hp;
+
+       hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+       if (hp == (struct hostlist *)NULL)
+               out_of_mem();
+       hp->ht_next = (struct hostlist *)NULL;
+       return (hp);
+}
+
+#ifdef ISO
+/*
+ * Translate an iso address.
+ */
+get_isoaddr(cp, grp)
+       char *cp;
+       struct grouplist *grp;
+{
+       struct iso_addr *isop;
+       struct sockaddr_iso *isoaddr;
+
+       if (grp->gr_type != GT_NULL)
+               return (1);
+       if ((isop = iso_addr(cp)) == NULL) {
+               syslog(LOG_ERR,
+                   "iso_addr failed, ignored");
+               return (1);
+       }
+       isoaddr = (struct sockaddr_iso *)
+           malloc(sizeof (struct sockaddr_iso));
+       if (isoaddr == (struct sockaddr_iso *)NULL)
+               out_of_mem();
+       bzero((caddr_t)isoaddr, sizeof (struct sockaddr_iso));
+       bcopy((caddr_t)isop, (caddr_t)&isoaddr->siso_addr,
+               sizeof (struct iso_addr));
+       isoaddr->siso_len = sizeof (struct sockaddr_iso);
+       isoaddr->siso_family = AF_ISO;
+       grp->gr_type = GT_ISO;
+       grp->gr_ptr.gt_isoaddr = isoaddr;
+       return (0);
+}
+#endif /* ISO */
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+       syslog(LOG_ERR, "Out of memory");
        exit(2);
 }
 
        exit(2);
 }
 
+/*
+ * Do the mount syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
+       struct exportlist *ep;
+       struct grouplist *grp;
+       int exflags;
+       struct ucred *anoncrp;
+       char *dirp;
+       int dirplen;
+       struct statfs *fsb;
+{
+       char *cp = (char *)NULL;
+       u_long **addrp;
+       int done;
+       char savedc = '\0';
+       struct sockaddr_in sin, imask;
+       union {
+               struct ufs_args ua;
+               struct iso_args ia;
+               struct mfs_args ma;
+       } args;
+       u_long net;
+
+       args.ua.fspec = 0;
+       args.ua.export.ex_flags = exflags;
+       args.ua.export.ex_anon = *anoncrp;
+       bzero((char *)&sin, sizeof(sin));
+       bzero((char *)&imask, sizeof(imask));
+       sin.sin_family = AF_INET;
+       sin.sin_len = sizeof(sin);
+       imask.sin_family = AF_INET;
+       imask.sin_len = sizeof(sin);
+       if (grp->gr_type == GT_HOST)
+               addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list;
+       else
+               addrp = (u_long **)NULL;
+       done = FALSE;
+       while (!done) {
+               switch (grp->gr_type) {
+               case GT_HOST:
+                       if (addrp) {
+                               sin.sin_addr.s_addr = **addrp;
+                               args.ua.export.ex_addrlen = sizeof(sin);
+                       } else
+                               args.ua.export.ex_addrlen = 0;
+                       args.ua.export.ex_addr = (struct sockaddr *)&sin;
+                       args.ua.export.ex_masklen = 0;
+                       break;
+               case GT_NET:
+                       if (grp->gr_ptr.gt_net.nt_mask)
+                           imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
+                       else {
+                           net = ntohl(grp->gr_ptr.gt_net.nt_net);
+                           if (IN_CLASSA(net))
+                               imask.sin_addr.s_addr = inet_addr("255.0.0.0");
+                           else if (IN_CLASSB(net))
+                               imask.sin_addr.s_addr =
+                                   inet_addr("255.255.0.0");
+                           else
+                               imask.sin_addr.s_addr =
+                                   inet_addr("255.255.255.0");
+                           grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
+                       }
+                       sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
+                       args.ua.export.ex_addr = (struct sockaddr *)&sin;
+                       args.ua.export.ex_addrlen = sizeof (sin);
+                       args.ua.export.ex_mask = (struct sockaddr *)&imask;
+                       args.ua.export.ex_masklen = sizeof (imask);
+                       break;
+#ifdef ISO
+               case GT_ISO:
+                       args.ua.export.ex_addr =
+                               (struct sockaddr *)grp->gr_ptr.gt_isoaddr;
+                       args.ua.export.ex_addrlen =
+                               sizeof(struct sockaddr_iso);
+                       args.ua.export.ex_masklen = 0;
+                       break;
+#endif /* ISO */
+               default:
+                       syslog(LOG_ERR, "Bad grouptype");
+                       if (cp)
+                               *cp = savedc;
+                       return (1);
+               };
+
+               /*
+                * XXX:
+                * Maybe I should just use the fsb->f_mntonname path instead
+                * of looping back up the dirp to the mount point??
+                * Also, needs to know how to export all types of local
+                * exportable file systems and not just MOUNT_UFS.
+                */
+               while (mount(fsb->f_type, dirp,
+                      fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+                       if (cp)
+                               *cp-- = savedc;
+                       else
+                               cp = dirp + dirplen - 1;
+                       if (errno == EPERM) {
+                               syslog(LOG_ERR,
+                                  "Can't change attributes for %s.\n", dirp);
+                               return (1);
+                       }
+                       if (opt_flags & OP_ALLDIRS) {
+                               syslog(LOG_ERR, "Not root dir");
+                               return (1);
+                       }
+                       /* back up over the last component */
+                       while (*cp == '/' && cp > dirp)
+                               cp--;
+                       while (*(cp - 1) != '/' && cp > dirp)
+                               cp--;
+                       if (cp == dirp) {
+                               if (debug)
+                                       fprintf(stderr,"mnt unsucc\n");
+                               syslog(LOG_ERR, "Can't export %s", dirp);
+                               return (1);
+                       }
+                       savedc = *cp;
+                       *cp = '\0';
+               }
+               if (addrp) {
+                       ++addrp;
+                       if (*addrp == (u_long *)NULL)
+                               done = TRUE;
+               } else
+                       done = TRUE;
+       }
+       if (cp)
+               *cp = savedc;
+       return (0);
+}
+
+/*
+ * Translate a net address.
+ */
+int
+get_net(cp, net, maskflg)
+       char *cp;
+       struct netmsk *net;
+       int maskflg;
+{
+       struct netent *np;
+       long netaddr;
+       struct in_addr inetaddr, inetaddr2;
+       char *name;
+
+       if (np = getnetbyname(cp))
+               inetaddr = inet_makeaddr(np->n_net, 0);
+       else if (isdigit(*cp)) {
+               if ((netaddr = inet_network(cp)) == -1)
+                       return (1);
+               inetaddr = inet_makeaddr(netaddr, 0);
+               /*
+                * Due to arbritrary subnet masks, you don't know how many
+                * bits to shift the address to make it into a network,
+                * however you do know how to make a network address into
+                * a host with host == 0 and then compare them.
+                * (What a pest)
+                */
+               if (!maskflg) {
+                       setnetent(0);
+                       while (np = getnetent()) {
+                               inetaddr2 = inet_makeaddr(np->n_net, 0);
+                               if (inetaddr2.s_addr == inetaddr.s_addr)
+                                       break;
+                       }
+                       endnetent();
+               }
+       } else
+               return (1);
+       if (maskflg)
+               net->nt_mask = inetaddr.s_addr;
+       else {
+               if (np)
+                       name = np->n_name;
+               else
+                       name = inet_ntoa(inetaddr);
+               net->nt_name = (char *)malloc(strlen(name) + 1);
+               if (net->nt_name == (char *)NULL)
+                       out_of_mem();
+               strcpy(net->nt_name, name);
+               net->nt_net = inetaddr.s_addr;
+       }
+       return (0);
+}
+
 /*
  * Parse out the next white space separated field
  */
 /*
  * Parse out the next white space separated field
  */
+void
 nextfield(cp, endcp)
        char **cp;
        char **endcp;
 {
 nextfield(cp, endcp)
        char **cp;
        char **endcp;
 {
-       register char *p;
+       char *p;
 
        p = *cp;
        while (*p == ' ' || *p == '\t')
                p++;
 
        p = *cp;
        while (*p == ' ' || *p == '\t')
                p++;
-       if (*p == '\n' || *p == '\0') {
+       if (*p == '\n' || *p == '\0')
                *cp = *endcp = p;
                *cp = *endcp = p;
-               return;
+       else {
+               *cp = p++;
+               while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+                       p++;
+               *endcp = p;
        }
        }
-       *cp = p++;
-       while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
-               p++;
-       *endcp = p;
 }
 
 /*
 }
 
 /*
- * Parse the option string
+ * Get an exports file line. Skip over blank lines and handle line
+ * continuations.
  */
  */
-do_opt(cpopt, fep, ep, exflagsp, rootuidp)
-       register char *cpopt;
-       struct exportlist *fep, *ep;
-       int *exflagsp, *rootuidp;
+int
+get_line()
 {
 {
-       register char *cpoptarg, *cpoptend;
+       char *p, *cp;
+       int len;
+       int totlen, cont_line;
 
 
-       while (cpopt && *cpopt) {
-               if (cpoptend = index(cpopt, ','))
-                       *cpoptend++ = '\0';
-               if (cpoptarg = index(cpopt, '='))
-                       *cpoptarg++ = '\0';
-               if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
-                       if (fep && (fep->ex_exflags & MNT_EXRDONLY) == 0)
-                               syslog(LOG_WARNING, "ro failed for %s",
-                                      ep->ex_dirp);
-                       else
-                               *exflagsp |= MNT_EXRDONLY;
-               } else if (!strcmp(cpopt, "root") || !strcmp(cpopt, "r")) {
-                       if (cpoptarg && isdigit(*cpoptarg)) {
-                               *rootuidp = atoi(cpoptarg);
-                               if (fep && fep->ex_rootuid != *rootuidp)
-                                       syslog(LOG_WARNING,
-                                              "uid failed for %s",
-                                              ep->ex_dirp);
-                       } else
-                               syslog(LOG_WARNING,
-                                      "uid failed for %s",
-                                      ep->ex_dirp);
-               } else
-                       syslog(LOG_WARNING, "opt %s ignored for %s", cpopt,
-                              ep->ex_dirp);
-               cpopt = cpoptend;
+       /*
+        * Loop around ignoring blank lines and getting all continuation lines.
+        */
+       p = line;
+       totlen = 0;
+       do {
+               if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
+                       return (0);
+               len = strlen(p);
+               cp = p + len - 1;
+               cont_line = 0;
+               while (cp >= p &&
+                   (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+                       if (*cp == '\\')
+                               cont_line = 1;
+                       cp--;
+                       len--;
+               }
+               *++cp = '\0';
+               if (len > 0) {
+                       totlen += len;
+                       if (totlen >= LINESIZ) {
+                               syslog(LOG_ERR, "Exports line too long");
+                               exit(2);
+                       }
+                       p = cp;
+               }
+       } while (totlen == 0 || cont_line);
+       return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+       char *namelist;
+       struct ucred *cr;
+{
+       char *name;
+       int cnt;
+       char *names;
+       struct passwd *pw;
+       struct group *gr;
+       int ngroups, groups[NGROUPS + 1];
+
+       /*
+        * Set up the unpriviledged user.
+        */
+       cr->cr_ref = 1;
+       cr->cr_uid = -2;
+       cr->cr_groups[0] = -2;
+       cr->cr_ngroups = 1;
+       /*
+        * Get the user's password table entry.
+        */
+       names = strsep(&namelist, " \t\n");
+       name = strsep(&names, ":");
+       if (isdigit(*name) || *name == '-')
+               pw = getpwuid(atoi(name));
+       else
+               pw = getpwnam(name);
+       /*
+        * Credentials specified as those of a user.
+        */
+       if (names == NULL) {
+               if (pw == NULL) {
+                       syslog(LOG_ERR, "Unknown user: %s", name);
+                       return;
+               }
+               cr->cr_uid = pw->pw_uid;
+               ngroups = NGROUPS + 1;
+               if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
+                       syslog(LOG_ERR, "Too many groups");
+               /*
+                * Convert from int's to gid_t's and compress out duplicate
+                */
+               cr->cr_ngroups = ngroups - 1;
+               cr->cr_groups[0] = groups[0];
+               for (cnt = 2; cnt < ngroups; cnt++)
+                       cr->cr_groups[cnt - 1] = groups[cnt];
+               return;
        }
        }
+       /*
+        * Explicit credential specified as a colon separated list:
+        *      uid:gid:gid:...
+        */
+       if (pw != NULL)
+               cr->cr_uid = pw->pw_uid;
+       else if (isdigit(*name) || *name == '-')
+               cr->cr_uid = atoi(name);
+       else {
+               syslog(LOG_ERR, "Unknown user: %s", name);
+               return;
+       }
+       cr->cr_ngroups = 0;
+       while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
+               name = strsep(&names, ":");
+               if (isdigit(*name) || *name == '-') {
+                       cr->cr_groups[cr->cr_ngroups++] = atoi(name);
+               } else {
+                       if ((gr = getgrnam(name)) == NULL) {
+                               syslog(LOG_ERR, "Unknown group: %s", name);
+                               continue;
+                       }
+                       cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+               }
+       }
+       if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
+               syslog(LOG_ERR, "Too many groups");
 }
 
 #define        STRSIZ  (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
 /*
  * Routines that maintain the remote mounttab
  */
 }
 
 #define        STRSIZ  (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
 /*
  * Routines that maintain the remote mounttab
  */
-void get_mountlist()
+void
+get_mountlist()
 {
 {
-       register struct mountlist *mlp, **mlpp;
-       register char *eos, *dirp;
+       struct mountlist *mlp, **mlpp;
+       char *eos, *dirp;
        int len;
        char str[STRSIZ];
        FILE *mlfile;
 
        if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
        int len;
        char str[STRSIZ];
        FILE *mlfile;
 
        if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
-               syslog(LOG_WARNING, "Can't open %s", _PATH_RMOUNTLIST);
+               syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
                return;
        }
        mlpp = &mlhead;
                return;
        }
        mlpp = &mlhead;
@@ -697,17 +1772,19 @@ void get_mountlist()
                        len = RPCMNT_PATHLEN;
                bcopy(dirp, mlp->ml_dirp, len);
                mlp->ml_dirp[len] = '\0';
                        len = RPCMNT_PATHLEN;
                bcopy(dirp, mlp->ml_dirp, len);
                mlp->ml_dirp[len] = '\0';
-               mlp->ml_next = (struct mountlist *)0;
+               mlp->ml_next = (struct mountlist *)NULL;
                *mlpp = mlp;
                mlpp = &mlp->ml_next;
        }
        fclose(mlfile);
 }
 
                *mlpp = mlp;
                mlpp = &mlp->ml_next;
        }
        fclose(mlfile);
 }
 
-void del_mlist(hostp, dirp)
-       register char *hostp, *dirp;
+void
+del_mlist(hostp, dirp)
+       char *hostp, *dirp;
 {
 {
-       register struct mountlist *mlp, **mlpp;
+       struct mountlist *mlp, **mlpp;
+       struct mountlist *mlp2;
        FILE *mlfile;
        int fnd = 0;
 
        FILE *mlfile;
        int fnd = 0;
 
@@ -717,15 +1794,17 @@ void del_mlist(hostp, dirp)
                if (!strcmp(mlp->ml_host, hostp) &&
                    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
                        fnd = 1;
                if (!strcmp(mlp->ml_host, hostp) &&
                    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
                        fnd = 1;
-                       *mlpp = mlp->ml_next;
-                       free((caddr_t)mlp);
+                       mlp2 = mlp;
+                       *mlpp = mlp = mlp->ml_next;
+                       free((caddr_t)mlp2);
+               } else {
+                       mlpp = &mlp->ml_next;
+                       mlp = mlp->ml_next;
                }
                }
-               mlpp = &mlp->ml_next;
-               mlp = mlp->ml_next;
        }
        if (fnd) {
                if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
        }
        if (fnd) {
                if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
-                       syslog(LOG_WARNING, "Can't update %s", _PATH_RMOUNTLIST);
+                       syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST);
                        return;
                }
                mlp = mlhead;
                        return;
                }
                mlp = mlhead;
@@ -737,10 +1816,11 @@ void del_mlist(hostp, dirp)
        }
 }
 
        }
 }
 
-void add_mlist(hostp, dirp)
-       register char *hostp, *dirp;
+void
+add_mlist(hostp, dirp)
+       char *hostp, *dirp;
 {
 {
-       register struct mountlist *mlp, **mlpp;
+       struct mountlist *mlp, **mlpp;
        FILE *mlfile;
 
        mlpp = &mlhead;
        FILE *mlfile;
 
        mlpp = &mlhead;
@@ -756,10 +1836,10 @@ void add_mlist(hostp, dirp)
        mlp->ml_host[RPCMNT_NAMELEN] = '\0';
        strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
        mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
        mlp->ml_host[RPCMNT_NAMELEN] = '\0';
        strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
        mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
-       mlp->ml_next = (struct mountlist *)0;
+       mlp->ml_next = (struct mountlist *)NULL;
        *mlpp = mlp;
        if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
        *mlpp = mlp;
        if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
-               syslog(LOG_WARNING, "Can't update %s", _PATH_RMOUNTLIST);
+               syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST);
                return;
        }
        fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
                return;
        }
        fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
@@ -770,13 +1850,15 @@ void add_mlist(hostp, dirp)
  * This function is called via. SIGTERM when the system is going down.
  * It sends a broadcast RPCMNT_UMNTALL.
  */
  * This function is called via. SIGTERM when the system is going down.
  * It sends a broadcast RPCMNT_UMNTALL.
  */
+void
 send_umntall()
 {
        (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
                xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
 send_umntall()
 {
        (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
                xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
-       exit();
+       exit(0);
 }
 
 }
 
+int
 umntall_each(resultsp, raddr)
        caddr_t resultsp;
        struct sockaddr_in *raddr;
 umntall_each(resultsp, raddr)
        caddr_t resultsp;
        struct sockaddr_in *raddr;
@@ -785,26 +1867,100 @@ umntall_each(resultsp, raddr)
 }
 
 /*
 }
 
 /*
- * Free up an exports list component
+ * Free up a group list.
  */
  */
-free_exp(ep)
-       register struct exportlist *ep;
-{
-       register struct grouplist *grp;
-       register char **addrp;
-       struct grouplist *grp2;
-
-       grp = ep->ex_groups;
-       while (grp != NULL) {
-               addrp = grp->gr_hp->h_addr_list;
-               while (*addrp)
-                       free(*addrp++);
-               free((caddr_t)grp->gr_hp->h_addr_list);
-               free(grp->gr_hp->h_name);
-               free((caddr_t)grp->gr_hp);
-               grp2 = grp;
-               grp = grp->gr_next;
-               free((caddr_t)grp2);
+void
+free_grp(grp)
+       struct grouplist *grp;
+{
+       char **addrp;
+
+       if (grp->gr_type == GT_HOST) {
+               if (grp->gr_ptr.gt_hostent->h_name) {
+                       addrp = grp->gr_ptr.gt_hostent->h_addr_list;
+                       while (addrp && *addrp)
+                               free(*addrp++);
+                       free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
+                       free(grp->gr_ptr.gt_hostent->h_name);
+               }
+               free((caddr_t)grp->gr_ptr.gt_hostent);
+       } else if (grp->gr_type == GT_NET) {
+               if (grp->gr_ptr.gt_net.nt_name)
+                       free(grp->gr_ptr.gt_net.nt_name);
        }
        }
-       free((caddr_t)ep);
+#ifdef ISO
+       else if (grp->gr_type == GT_ISO)
+               free((caddr_t)grp->gr_ptr.gt_isoaddr);
+#endif
+       free((caddr_t)grp);
+}
+
+#ifdef DEBUG
+void
+SYSLOG(int pri, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
+#endif /* DEBUG */
+
+/*
+ * Check options for consistency.
+ */
+int
+check_options(dp)
+       struct dirlist *dp;
+{
+
+       if (dp == (struct dirlist *)NULL)
+           return (1);
+       if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
+           (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
+           (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
+           syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
+           return (1);
+       }
+       if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+           syslog(LOG_ERR, "-mask requires -net");
+           return (1);
+       }
+       if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
+           syslog(LOG_ERR, "-net and -iso mutually exclusive");
+           return (1);
+       }
+       if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+           syslog(LOG_ERR, "-alldir has multiple directories");
+           return (1);
+       }
+       return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ * if no symbolic links are found.
+ */
+int
+check_dirpath(dirp)
+       char *dirp;
+{
+       char *cp;
+       int ret = 1;
+       struct stat sb;
+
+       cp = dirp + 1;
+       while (*cp && ret) {
+               if (*cp == '/') {
+                       *cp = '\0';
+                       if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+                               ret = 0;
+                       *cp = '/';
+               }
+               cp++;
+       }
+       if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+               ret = 0;
+       return (ret);
 }
 }