BSD 4_4 release
[unix-history] / usr / src / sbin / mountd / mountd.c
index a5f2d89..0841467 100644 (file)
@@ -1,59 +1,79 @@
 /*
 /*
- * 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.
  *
  *
- * Redistribution and use in source and binary forms are permitted provided
- * that: (1) source distributions retain this entire copyright notice and
- * comment, and (2) distributions including binaries display the following
- * acknowledgement:  ``This product includes software developed by the
- * University of California, Berkeley and its contributors'' in the
- * documentation or other materials provided with the distribution and in
- * all advertising materials mentioning features or use of this software.
- * Neither the name of the University nor the names of its contributors may
- * be used to endorse or promote products derived from this software without
- * specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #ifndef lint
  */
 
 #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.11 (Berkeley) 6/29/90";
+static char sccsid[] = "@(#)mountd.c   8.1 (Berkeley) 6/5/93";
 #endif not lint
 
 #endif not lint
 
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdlib.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/file.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/file.h>
+#include <sys/ucred.h>
 #include <sys/mount.h>
 #include <sys/socket.h>
 #include <sys/errno.h>
 #include <sys/signal.h>
 #include <stdio.h>
 #include <string.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 <sys/syslog.h>
 #include <netdb.h>
 #include <rpc/rpc.h>
 #include <rpc/pmap_clnt.h>
 #include <rpc/pmap_prot.h>
 #include <netdb.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 "pathnames.h"
 #include <nfs/rpcv2.h>
 #include <nfs/nfsv2.h>
 #include "pathnames.h"
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
 
 
-struct ufid {
-       u_short ufid_len;
-       ino_t   ufid_ino;
-       long    ufid_gen;
-};
 /*
  * Structures for keeping the mount list and export list
  */
 /*
  * Structures for keeping the mount list and export list
  */
@@ -63,33 +83,99 @@ 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;
+int mntsrv(), umntall_each(), xdr_fhs(), xdr_mlist(), xdr_dir(), xdr_explist();
+void get_exportlist(), send_umntall(), nextfield(), out_of_mem();
+void get_mountlist(), add_mlist(), del_mlist(), free_exp(), free_grp();
+void getexp_err(), hang_dirp(), add_dlist(), free_dir(), free_host();
+void setnetgrent(), endnetgrent();
+struct exportlist *ex_search(), *get_exp();
+struct grouplist *get_grp();
+char *realpath(), *add_expdir();
+struct in_addr inet_makeaddr();
+char *inet_ntoa();
+struct dirlist *dirp_search();
+struct hostlist *get_ht();
+#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;
+struct ucred def_anon = {
+       (u_short) 1,
+       (uid_t) -2,
+       1,
+       (gid_t) -2,
+};
 int root_only = 1;
 int root_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
+
 extern int errno;
 #ifdef DEBUG
 int debug = 1;
 extern int errno;
 #ifdef DEBUG
 int debug = 1;
+void   SYSLOG __P((int, const char *, ...));
+#define syslog SYSLOG
 #else
 int debug = 0;
 #endif
 #else
 int debug = 0;
 #endif
@@ -121,7 +207,8 @@ main(argc, argv)
                };
        argc -= optind;
        argv += optind;
                };
        argc -= optind;
        argv += optind;
-       exphead.ex_next = exphead.ex_prev = (struct exportlist *)0;
+       grphead = (struct grouplist *)0;
+       exphead = (struct exportlist *)0;
        mlhead = (struct mountlist *)0;
        if (argc == 1) {
                strncpy(exname, *argv, MAXPATHLEN-1);
        mlhead = (struct mountlist *)0;
        if (argc == 1) {
                strncpy(exname, *argv, MAXPATHLEN-1);
@@ -129,8 +216,14 @@ main(argc, argv)
        } else
                strcpy(exname, _PATH_EXPORTS);
        openlog("mountd:", LOG_PID, LOG_DAEMON);
        } 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) {
                daemon(0, 0);
                signal(SIGINT, SIG_IGN);
        if (debug == 0) {
                daemon(0, 0);
                signal(SIGINT, SIG_IGN);
@@ -149,7 +242,8 @@ 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);
        }
                syslog(LOG_ERR, "Can't register mount");
                exit(1);
        }
@@ -165,17 +259,16 @@ mntsrv(rqstp, transp)
        register struct svc_req *rqstp;
        register SVCXPRT *transp;
 {
        register struct svc_req *rqstp;
        register SVCXPRT *transp;
 {
-       register struct grouplist *grp;
-       register u_long **addrp;
        register struct exportlist *ep;
        register struct exportlist *ep;
+       register struct dirlist *dp;
        nfsv2fh_t nfh;
        struct authunix_parms *ucr;
        struct stat stb;
        nfsv2fh_t nfh;
        struct authunix_parms *ucr;
        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;
+       char rpcpath[RPCMNT_PATHLEN+1], dirpath[MAXPATHLEN];
+       int bad = ENOENT, omask, defset;
        uid_t uid = -2;
 
        /* Get authorization */
        uid_t uid = -2;
 
        /* Get authorization */
@@ -197,18 +290,26 @@ mntsrv(rqstp, transp)
                        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 ((uid != 0 && root_only) || uid == -2) {
                        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 directory
+                * that exists.
+                */
+               if (realpath(rpcpath, dirpath) == 0 ||
+                   stat(dirpath, &stb) < 0 ||
+                   (stb.st_mode & S_IFMT) != S_IFDIR ||
+                   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;
@@ -216,64 +317,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:
                if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
                        syslog(LOG_ERR, "Can't send reply");
                return;
        case RPCMNT_UMOUNT:
                return;
        case RPCMNT_DUMP:
                if (!svc_sendreply(transp, xdr_mlist, (caddr_t)0))
                        syslog(LOG_ERR, "Can't send reply");
                return;
        case RPCMNT_UMOUNT:
-               if (uid != 0 && root_only) {
+               if ((uid != 0 && root_only) || uid == -2) {
                        svcerr_weakauth(transp);
                        return;
                }
                        svcerr_weakauth(transp);
                        return;
                }
@@ -286,9 +372,10 @@ mntsrv(rqstp, transp)
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
                        del_mlist(hp->h_name, dirpath);
                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 ((uid != 0 && root_only) || uid == -2) {
                        svcerr_weakauth(transp);
                        return;
                }
                        svcerr_weakauth(transp);
                        return;
                }
@@ -297,6 +384,7 @@ mntsrv(rqstp, transp)
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
                        del_mlist(hp->h_name, (char *)0);
                hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
                if (hp)
                        del_mlist(hp->h_name, (char *)0);
+               del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)0);
                return;
        case RPCMNT_EXPORT:
                if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
                return;
        case RPCMNT_EXPORT:
                if (!svc_sendreply(transp, xdr_explist, (caddr_t)0))
@@ -366,30 +454,13 @@ xdr_explist(xdrsp, cp)
        caddr_t cp;
 {
        register struct exportlist *ep;
        caddr_t cp;
 {
        register struct exportlist *ep;
-       register struct grouplist *grp;
-       int true = 1;
        int false = 0;
        int false = 0;
-       char *strp;
        int omask;
 
        omask = sigblock(sigmask(SIGHUP));
        int omask;
 
        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))
-                       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))
+       ep = exphead;
+       while (ep) {
+               if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir))
                        goto errout;
                ep = ep->ex_next;
        }
                        goto errout;
                ep = ep->ex_next;
        }
@@ -402,199 +473,1088 @@ errout:
        return (0);
 }
 
        return (0);
 }
 
+/*
+ * Called from xdr_explist() to traverse the tree and export the
+ * directory paths.
+ */
+put_exlist(dp, xdrsp, adp)
+       register struct dirlist *dp;
+       XDR *xdrsp;
+       struct dirlist *adp;
+{
+       register struct grouplist *grp;
+       register struct hostlist *hp;
+       struct in_addr inaddr;
+       int true = 1;
+       int false = 0;
+       int gotalldir = 0;
+       char *strp;
+
+       if (dp) {
+               if (put_exlist(dp->dp_left, xdrsp, adp))
+                       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;
+               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 *)0) {
+                                       hp = adp->dp_hosts;
+                                       gotalldir = 0;
+                               }
+                       }
+               }
+               if (!xdr_bool(xdrsp, &false))
+                       return (1);
+               if (put_exlist(dp->dp_right, xdrsp, adp))
+                       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;
        register struct exportlist *ep, *ep2;
-       struct ufs_args args;
+       register struct grouplist *grp, *tgrp;
+       struct exportlist **epp;
+       struct dirlist *dirhead;
        struct stat sb;
        struct stat sb;
-       FILE *inf;
-       char *cp, *endcp;
-       char savedc;
-       int len, dirplen;
-       int rootuid, exflags;
-       u_long saddr;
-       struct exportlist *fep;
+       struct statfs fsb, *fsp;
+       struct hostent *hpe;
+       struct ucred anon;
+       struct ufs_args targs;
+       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 *)0;
+
+       grp = grphead;
+       while (grp) {
+               tgrp = grp;
+               grp = grp->gr_next;
+               free_grp(tgrp);
+       }
+       grphead = (struct grouplist *)0;
+
+       /*
+        * 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++) {
+               if (fsp->f_type == MOUNT_UFS) {
+                       targs.fspec = (char *)0;
+                       targs.exflags = MNT_DELEXPORT;
+                       if (mount(fsp->f_type, fsp->f_mntonname,
+                           fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0)
+                               syslog(LOG_ERR, "Can't del exports %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 *)0;
+       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 *)0;
 
                /*
                 * 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 *)0) {
+                               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 (stat(cp, &sb) >= 0 &&
+                               (sb.st_mode & S_IFMT) == S_IFDIR &&
+                               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 *)0) {
+                                       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 *)0) {
+                               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 *)0)
+                               out_of_mem();
+                       hpe->h_name = "Default";
+                       hpe->h_addrtype = AF_INET;
+                       hpe->h_length = sizeof (u_long);
+                       hpe->h_addr_list = (char **)0;
+                       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 *)0, ep,
+                       (opt_flags & OP_ALLDIRS));
+                       free_grp(grp);
+               }
+               dirhead = (struct dirlist *)0;
+               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 *)0;
+               }
+       }
+       fclose(exp_file);
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+       register struct exportlist *ep;
+
+       ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+       if (ep == (struct exportlist *)0)
+               out_of_mem();
+       bzero((caddr_t)ep, sizeof (struct exportlist));
+       return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+       register struct grouplist *gp;
+
+       gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+       if (gp == (struct grouplist *)0)
+               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;
+{
+       register 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;
+{
+       register struct dirlist *dp;
+
+       dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+       dp->dp_left = *dpp;
+       dp->dp_right = (struct dirlist *)0;
+       dp->dp_flag = 0;
+       dp->dp_hosts = (struct hostlist *)0;
+       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)
+       register struct dirlist *dp;
+       struct grouplist *grp;
+       struct exportlist *ep;
+       int alldirs;
+{
+       register 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 *)0)
+                       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;
+       register struct grouplist *grp;
+{
+       register struct dirlist *dp;
+       register 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 *)0;
+               *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)
+       register struct dirlist *dp;
+       char *dirpath;
+{
+       register 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.
+ */
+chk_host(dp, saddr, defsetp)
+       struct dirlist *dp;
+       u_long saddr;
+       int *defsetp;
+{
+       register struct hostlist *hp;
+       register struct grouplist *grp;
+       register 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.
+ */
+scan_tree(dp, saddr)
+       register 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)
+       register 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>
+ */
+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;
+{
+       register 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.
+ */
+get_host(cp, grp)
+       char *cp;
+       register struct grouplist *grp;
+{
+       register struct hostent *hp, *nhp;
+       register 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 *)0;
+                       }
+               } else {
+                       syslog(LOG_ERR, "Gethostbyname failed");
+                       return (1);
+               }
        }
        }
-       fclose(inf);
-       return;
-err:
-       syslog(LOG_ERR, "Bad Exports File, mountd Failed");
+       grp->gr_type = GT_HOST;
+       nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
+               malloc(sizeof(struct hostent));
+       if (nhp == (struct hostent *)0)
+               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 *)0)
+               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 **)0)
+               out_of_mem();
+       addrp = hp->h_addr_list;
+       while (*addrp) {
+               *naddrp = (char *)
+                   malloc(hp->h_length);
+               if (*naddrp == (char *)0)
+                   out_of_mem();
+               bcopy(*addrp, *naddrp,
+                       hp->h_length);
+               addrp++;
+               naddrp++;
+       }
+       *naddrp = (char *)0;
+       if (debug)
+               fprintf(stderr, "got host %s\n", hp->h_name);
+       return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+       register 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)
+       register struct hostlist *hp;
+{
+       register struct hostlist *hp2;
+
+       while (hp) {
+               hp2 = hp;
+               hp = hp->ht_next;
+               free((caddr_t)hp2);
+       }
+}
+
+struct hostlist *
+get_ht()
+{
+       register struct hostlist *hp;
+
+       hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+       if (hp == (struct hostlist *)0)
+               out_of_mem();
+       hp->ht_next = (struct hostlist *)0;
+       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 *)0)
+               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.
+ */
+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;
+{
+       register char *cp = (char *)0;
+       register u_long **addrp;
+       int done;
+       char savedc;
+       struct sockaddr_in sin, imask;
+       struct ufs_args args;
+       u_long net;
+
+       args.fspec = 0;
+       args.exflags = exflags;
+       args.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 **)0;
+       done = FALSE;
+       while (!done) {
+               switch (grp->gr_type) {
+               case GT_HOST:
+                       if (addrp)
+                               sin.sin_addr.s_addr = **addrp;
+                       else
+                               sin.sin_addr.s_addr = INADDR_ANY;
+                       args.saddr = (struct sockaddr *)&sin;
+                       args.slen = sizeof(sin);
+                       args.msklen = 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.saddr = (struct sockaddr *)&sin;
+                       args.slen = sizeof (sin);
+                       args.smask = (struct sockaddr *)&imask;
+                       args.msklen = sizeof (imask);
+                       break;
+#ifdef ISO
+               case GT_ISO:
+                       args.saddr = (struct sockaddr *)grp->gr_ptr.gt_isoaddr;
+                       args.slen = sizeof (struct sockaddr_iso);
+                       args.msklen = 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 *)0)
+                               done = TRUE;
+               } else
+                       done = TRUE;
+       }
+       if (cp)
+               *cp = savedc;
+       return (0);
+}
+
+/*
+ * Translate a net address.
+ */
+get_net(cp, net, maskflg)
+       char *cp;
+       struct netmsk *net;
+       int maskflg;
+{
+       register struct netent *np;
+       register 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 *)0)
+                       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;
@@ -604,60 +1564,143 @@ nextfield(cp, endcp)
        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;
+get_line()
 {
 {
-       register char *cpoptarg, *cpoptend;
+       register char *p, *cp;
+       register 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.
+ */
+parsecred(namelist, cr)
+       char *namelist;
+       register struct ucred *cr;
+{
+       register char *name;
+       register 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\n", 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\n");
+               /*
+                * 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\n", 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\n", 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\n");
 }
 
 #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;
 {
        register struct mountlist *mlp, **mlpp;
        register char *eos, *dirp;
@@ -665,9 +1708,8 @@ void get_mountlist()
        char str[STRSIZ];
        FILE *mlfile;
 
        char str[STRSIZ];
        FILE *mlfile;
 
-       if (((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) &&
-           ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL)) {
-               syslog(LOG_WARNING, "Can't open %s", _PATH_RMOUNTLIST);
+       if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
+               syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
                return;
        }
        mlpp = &mlhead;
                return;
        }
        mlpp = &mlhead;
@@ -700,10 +1742,12 @@ void get_mountlist()
        fclose(mlfile);
 }
 
        fclose(mlfile);
 }
 
-void del_mlist(hostp, dirp)
+void
+del_mlist(hostp, dirp)
        register char *hostp, *dirp;
 {
        register struct mountlist *mlp, **mlpp;
        register char *hostp, *dirp;
 {
        register struct mountlist *mlp, **mlpp;
+       struct mountlist *mlp2;
        FILE *mlfile;
        int fnd = 0;
 
        FILE *mlfile;
        int fnd = 0;
 
@@ -713,15 +1757,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;
@@ -733,7 +1779,8 @@ void del_mlist(hostp, dirp)
        }
 }
 
        }
 }
 
-void add_mlist(hostp, dirp)
+void
+add_mlist(hostp, dirp)
        register char *hostp, *dirp;
 {
        register struct mountlist *mlp, **mlpp;
        register char *hostp, *dirp;
 {
        register struct mountlist *mlp, **mlpp;
@@ -755,7 +1802,7 @@ void add_mlist(hostp, dirp)
        mlp->ml_next = (struct mountlist *)0;
        *mlpp = mlp;
        if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
        mlp->ml_next = (struct mountlist *)0;
        *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);
@@ -766,11 +1813,12 @@ 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);
 }
 
 umntall_each(resultsp, raddr)
 }
 
 umntall_each(resultsp, raddr)
@@ -781,26 +1829,156 @@ umntall_each(resultsp, raddr)
 }
 
 /*
 }
 
 /*
- * Free up an exports list component
+ * Free up a group list.
  */
  */
-free_exp(ep)
-       register struct exportlist *ep;
-{
+void
+free_grp(grp)
        register struct grouplist *grp;
        register struct grouplist *grp;
+{
        register char **addrp;
        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);
+
+       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);
+}
+
+/*
+ * char *realpath(const char *path, char resolved_path[MAXPATHLEN])
+ *
+ * find the real name of path, by removing all ".", ".."
+ * and symlink components.
+ *
+ * Jan-Simon Pendry, September 1991.
+ */
+char *
+realpath(path, resolved)
+       char *path;
+       char resolved[MAXPATHLEN];
+{
+       int d = open(".", O_RDONLY);
+       int rootd = 0;
+       char *p, *q;
+       struct stat stb;
+       char wbuf[MAXPATHLEN];
+
+       strcpy(resolved, path);
+
+       if (d < 0)
+               return 0;
+
+loop:;
+       q = strrchr(resolved, '/');
+       if (q) {
+               p = q + 1;
+               if (q == resolved)
+                       q = "/";
+               else {
+                       do
+                               --q;
+                       while (q > resolved && *q == '/');
+                       q[1] = '\0';
+                       q = resolved;
+               }
+               if (chdir(q) < 0)
+                       goto out;
+       } else
+               p = resolved;
+
+       if (lstat(p, &stb) == 0) {
+               if (S_ISLNK(stb.st_mode)) {
+                       int n = readlink(p, resolved, MAXPATHLEN);
+                       if (n < 0)
+                               goto out;
+                       resolved[n] = '\0';
+                       goto loop;
+               }
+               if (S_ISDIR(stb.st_mode)) {
+                       if (chdir(p) < 0)
+                               goto out;
+                       p = "";
+               }
+       }
+
+       strcpy(wbuf, p);
+       if (getcwd(resolved, MAXPATHLEN) == 0)
+               goto out;
+       if (resolved[0] == '/' && resolved[1] == '\0')
+               rootd = 1;
+
+       if (*wbuf) {
+               if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) {
+                       errno = ENAMETOOLONG;
+                       goto out;
+               }
+               if (rootd == 0)
+                       strcat(resolved, "/");
+               strcat(resolved, wbuf);
+       }
+
+       if (fchdir(d) < 0)
+               goto out;
+       (void) close(d);
+
+       return resolved;
+
+out:;
+       (void) close(d);
+       return 0;
+}
+
+#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.
+ */
+check_options(dp)
+       struct dirlist *dp;
+{
+
+       if (dp == (struct dirlist *)0)
+           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);
 }
 }