summaryrefslogtreecommitdiffstats
path: root/utils/mountd
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mountd')
-rw-r--r--utils/mountd/Makefile12
-rw-r--r--utils/mountd/auth.c215
-rw-r--r--utils/mountd/mount_dispatch.c70
-rw-r--r--utils/mountd/mount_xdr.c79
-rw-r--r--utils/mountd/mountd.c489
-rw-r--r--utils/mountd/mountd.h54
-rw-r--r--utils/mountd/mountd.man92
-rw-r--r--utils/mountd/rmtab.c173
8 files changed, 1184 insertions, 0 deletions
diff --git a/utils/mountd/Makefile b/utils/mountd/Makefile
new file mode 100644
index 0000000..93529a0
--- /dev/null
+++ b/utils/mountd/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for rpc.mountd
+#
+
+PROGRAM = mountd
+PREFIX = rpc.
+OBJS = mountd.o mount_dispatch.o auth.o rmtab.o
+LIBDEPS = $(TOP)support/lib/libexport.a $(TOP)/support/lib/libnfs.a
+LIBS = -lexport -lnfs
+MAN8 = mountd
+
+include $(TOP)rules.mk
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
new file mode 100644
index 0000000..9f63120
--- /dev/null
+++ b/utils/mountd/auth.c
@@ -0,0 +1,215 @@
+/*
+ * utils/mountd/auth.c
+ *
+ * Authentication procedures for mountd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include "misc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+#include "mountd.h"
+
+enum auth_error
+{
+ bad_path,
+ unknown_host,
+ no_entry,
+ not_exported,
+ illegal_port,
+ faked_hostent,
+ success
+};
+
+static void auth_fixpath(char *path);
+static nfs_export* auth_authenticate_internal
+ (char *what, struct sockaddr_in *caller, char *path,
+ struct hostent **hpp, enum auth_error *error);
+static char *export_file = NULL;
+
+void
+auth_init(char *exports)
+{
+
+ export_file = exports;
+ auth_reload();
+ xtab_mount_write();
+}
+
+int
+auth_reload()
+{
+ struct stat stb;
+ static time_t last_modified = 0;
+
+ if (stat(_PATH_ETAB, &stb) < 0)
+ xlog(L_FATAL, "couldn't stat %s", _PATH_ETAB);
+ if (stb.st_mtime == last_modified)
+ return 0;
+ last_modified = stb.st_mtime;
+
+ export_freeall();
+ // export_read(export_file);
+ xtab_export_read();
+
+ return 1;
+}
+
+static nfs_export *
+auth_authenticate_internal(char *what, struct sockaddr_in *caller,
+ char *path, struct hostent **hpp,
+ enum auth_error *error)
+{
+ struct in_addr addr = caller->sin_addr;
+ nfs_export *exp;
+
+ if (path[0] != '/') {
+ *error = bad_path;
+ return NULL;
+ }
+ auth_fixpath(path);
+
+ if (!(*hpp = gethostbyaddr((const char *)&addr, sizeof(addr), AF_INET)))
+ *hpp = get_hostent((const char *)&addr, sizeof(addr),
+ AF_INET);
+ else {
+ /* must make sure the hostent is authorative. */
+ char *name = strdup((*hpp)->h_name);
+ char **sp;
+ *hpp = gethostbyname(name);
+ /* now make sure the "addr" is in the list */
+ for (sp = (*hpp)->h_addr_list ; *sp ; sp++) {
+ if (memcmp(*sp, &addr, (*hpp)->h_length)==0)
+ break;
+ }
+
+ if (!*sp) {
+ free(name);
+ /* it was a FAKE */
+ *error = faked_hostent;
+ *hpp = NULL;
+ return NULL;
+ }
+ *hpp = hostent_dup (*hpp);
+ free(name);
+ }
+
+ if (!(exp = export_find(*hpp, path))) {
+ *error = no_entry;
+ return NULL;
+ }
+ if (!exp->m_mayexport) {
+ *error = not_exported;
+ return NULL;
+ }
+
+ if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) &&
+ (ntohs(caller->sin_port) < IPPORT_RESERVED/2 ||
+ ntohs(caller->sin_port) >= IPPORT_RESERVED)) {
+ *error = illegal_port;
+ return NULL;
+ }
+
+ *error = success;
+
+ return exp;
+}
+
+nfs_export *
+auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
+{
+ nfs_export *exp = NULL;
+ char epath[MAXPATHLEN+1];
+ char *p = NULL;
+ struct hostent *hp = NULL;
+ struct in_addr addr = caller->sin_addr;
+ enum auth_error error;
+
+ if (path [0] != '/') return exp;
+
+ strncpy(epath, path, sizeof (epath) - 1);
+ epath[sizeof (epath) - 1] = '\0';
+
+ /* Try the longest matching exported pathname. */
+ while (1) {
+ if (hp) {
+ free (hp);
+ hp = NULL;
+ }
+ exp = auth_authenticate_internal(what, caller, epath,
+ &hp, &error);
+ if (exp || (error != not_exported && error != no_entry))
+ break;
+ /* We have to treat the root, "/", specially. */
+ if (p == &epath[1]) break;
+ p = strrchr(epath, '/');
+ if (p == epath) p++;
+ *p = '\0';
+ }
+
+ switch (error) {
+ case bad_path:
+ xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
+ what, inet_ntoa(addr), path);
+ break;
+
+ case unknown_host:
+ xlog(L_WARNING, "%s request from unknown host %s for %s (%s)",
+ what, inet_ntoa(addr), path, epath);
+ break;
+
+ case no_entry:
+ xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry",
+ what, hp->h_name, path, epath);
+ break;
+
+ case not_exported:
+ xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported",
+ what, hp->h_name, path, epath);
+ break;
+
+ case illegal_port:
+ xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
+ what, hp->h_name, path, epath, ntohs(caller->sin_port));
+ break;
+
+ case faked_hostent:
+ xlog(L_WARNING, "refused %s request from %s for %s (%s): faked hostent",
+ what, inet_ntoa(addr), path, epath);
+ break;
+
+ case success:
+ xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
+ what, hp->h_name, ntohs(caller->sin_port), path, epath);
+ break;
+ default:
+ xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
+ what, hp->h_name, ntohs(caller->sin_port), path, epath, error);
+ }
+
+ if (hp)
+ free (hp);
+
+ return exp;
+}
+
+static void
+auth_fixpath(char *path)
+{
+ char *sp, *cp;
+
+ for (sp = cp = path; *sp; sp++) {
+ if (*sp != '/' || sp[1] != '/')
+ *cp++ = *sp;
+ }
+ while (cp > path+1 && cp[-1] == '/')
+ cp--;
+ *cp = '\0';
+}
diff --git a/utils/mountd/mount_dispatch.c b/utils/mountd/mount_dispatch.c
new file mode 100644
index 0000000..cee1981
--- /dev/null
+++ b/utils/mountd/mount_dispatch.c
@@ -0,0 +1,70 @@
+/*
+ * mount_dispatch This file contains the function dispatch table.
+ *
+ * Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include "mountd.h"
+#include "rpcmisc.h"
+
+/*
+ * Procedures for MNTv1
+ */
+static struct rpc_dentry mnt_1_dtable[] = {
+ dtable_ent(mount_null,1,void,void), /* NULL */
+ dtable_ent(mount_mnt,1,dirpath,fhstatus), /* MNT */
+ dtable_ent(mount_dump,1,void,mountlist), /* DUMP */
+ dtable_ent(mount_umnt,1,dirpath,void), /* UMNT */
+ dtable_ent(mount_umntall,1,void,void), /* UMNTALL */
+ dtable_ent(mount_export,1,void,exports), /* EXPORT */
+ dtable_ent(mount_exportall,1,void,exports), /* EXPORTALL */
+};
+
+/*
+ * Procedures for MNTv2
+ */
+static struct rpc_dentry mnt_2_dtable[] = {
+ dtable_ent(mount_null,1,void,void), /* NULL */
+ dtable_ent(mount_mnt,1,dirpath,fhstatus), /* MNT */
+ dtable_ent(mount_dump,1,void,mountlist), /* DUMP */
+ dtable_ent(mount_umnt,1,dirpath,void), /* UMNT */
+ dtable_ent(mount_umntall,1,void,void), /* UMNTALL */
+ dtable_ent(mount_export,1,void,exports), /* EXPORT */
+ dtable_ent(mount_exportall,1,void,exports), /* EXPORTALL */
+ dtable_ent(mount_pathconf,2,dirpath,ppathcnf), /* PATHCONF */
+};
+
+/*
+ * Procedures for MNTv3
+ */
+static struct rpc_dentry mnt_3_dtable[] = {
+ dtable_ent(mount_null,1,void,void), /* NULL */
+ dtable_ent(mount_mnt,3,dirpath,mountres3), /* MNT */
+ dtable_ent(mount_dump,1,void,mountlist), /* DUMP */
+ dtable_ent(mount_umnt,1,dirpath,void), /* UMNT */
+ dtable_ent(mount_umntall,1,void,void), /* UMNTALL */
+ dtable_ent(mount_export,1,void,exports), /* EXPORT */
+};
+
+#define number_of(x) (sizeof(x)/sizeof(x[0]))
+
+static struct rpc_dtable dtable[] = {
+ { mnt_1_dtable, number_of(mnt_1_dtable) },
+ { mnt_2_dtable, number_of(mnt_2_dtable) },
+ { mnt_3_dtable, number_of(mnt_3_dtable) },
+};
+
+/*
+ * The main dispatch routine.
+ */
+void
+mount_dispatch(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union mountd_arguments argument;
+ union mountd_results result;
+
+ rpc_dispatch(rqstp, transp, dtable, number_of(dtable),
+ &argument, &result);
+}
diff --git a/utils/mountd/mount_xdr.c b/utils/mountd/mount_xdr.c
new file mode 100644
index 0000000..87adfa6
--- /dev/null
+++ b/utils/mountd/mount_xdr.c
@@ -0,0 +1,79 @@
+/*
+ * mount_xdr XDR procedures for mountd.
+ *
+ * Originally generated by rpcgen; edited to get rid of warnings.
+ */
+
+#include "config.h"
+
+#include "mount.h"
+
+inline bool_t
+xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+ return xdr_opaque(xdrs, objp, FHSIZE);
+}
+
+bool_t
+xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+ return xdr_u_int(xdrs, &objp->fhs_status) &&
+ (objp->fhs_status != 0 ||
+ xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle));
+}
+
+bool_t
+xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+ return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+inline bool_t
+xdr_name(XDR *xdrs, name *objp)
+{
+ return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+bool_t
+xdr_mountlist(XDR *xdrs, mountlist *objp)
+{
+ return xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody),
+ (xdrproc_t)xdr_mountbody);
+}
+
+bool_t
+xdr_mountbody(XDR *xdrs, mountbody *objp)
+{
+ return xdr_name(xdrs, &objp->ml_hostname) &&
+ xdr_dirpath(xdrs, &objp->ml_directory) &&
+ xdr_mountlist(xdrs, &objp->ml_next);
+}
+
+bool_t
+xdr_groups(XDR *xdrs, groups *objp)
+{
+ return xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode),
+ (xdrproc_t)xdr_groupnode);
+}
+
+bool_t
+xdr_groupnode(XDR *xdrs, groupnode *objp)
+{
+ return xdr_name(xdrs, &objp->gr_name) &&
+ xdr_groups(xdrs, &objp->gr_next);
+}
+
+bool_t
+xdr_exports(XDR *xdrs, exports *objp)
+{
+ return xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode),
+ (xdrproc_t)xdr_exportnode);
+}
+
+bool_t
+xdr_exportnode(XDR *xdrs, exportnode *objp)
+{
+ return xdr_dirpath(xdrs, &objp->ex_dir) &&
+ xdr_groups(xdrs, &objp->ex_groups) &&
+ xdr_exports(xdrs, &objp->ex_next);
+}
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
new file mode 100644
index 0000000..9f467d1
--- /dev/null
+++ b/utils/mountd/mountd.c
@@ -0,0 +1,489 @@
+/*
+ * utils/mountd/mountd.c
+ *
+ * Authenticate mount requests and retrieve file handle.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <signal.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "mountd.h"
+#include "rpcmisc.h"
+
+static void usage(const char *, int exitcode);
+static exports get_exportlist(void);
+static struct knfs_fh * get_rootfh(struct svc_req *, dirpath *, int *);
+
+static struct option longopts[] =
+{
+ { "foreground", 0, 0, 'F' },
+ { "debug", 1, 0, 'd' },
+ { "help", 0, 0, 'h' },
+ { "exports-file", 1, 0, 'f' },
+ { "nfs-version", 1, 0, 'V' },
+ { "no-nfs-version", 1, 0, 'N' },
+ { "version", 0, 0, 'v' },
+ { "port", 1, 0, 'p' },
+ { NULL, 0, 0, 0 }
+};
+
+static int nfs_version = -1;
+
+/*
+ * Signal handler.
+ */
+static void
+killer (int sig)
+{
+ if (nfs_version & 0x1)
+ pmap_unset (MOUNTPROG, MOUNTVERS);
+ if (nfs_version & (0x1 << 1))
+ pmap_unset (MOUNTPROG, MOUNTVERS_POSIX);
+ if (nfs_version & (0x1 << 2))
+ pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3);
+ xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
+}
+
+bool_t
+mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp)
+{
+ return 1;
+}
+
+bool_t
+mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
+{
+ struct knfs_fh *fh;
+
+ xlog(D_CALL, "MNT1(%s) called", *path);
+ if ((fh = get_rootfh(rqstp, path, &res->fhs_status)) != NULL)
+ memcpy(&res->fhstatus_u.fhs_fhandle, fh, 32);
+ return 1;
+}
+
+bool_t
+mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
+{
+ xlog(L_NOTICE, "dump request from %s",
+ inet_ntoa(svc_getcaller(rqstp->rq_xprt)->sin_addr));
+
+ *res = mountlist_list();
+ return 1;
+}
+
+bool_t
+mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
+{
+ struct sockaddr_in *sin = svc_getcaller(rqstp->rq_xprt);
+ nfs_export *exp;
+ char *p = *argp;
+ char rpath[MAXPATHLEN+1];
+
+ if (*p == '\0')
+ p = "/";
+
+ if (realpath(p, rpath) != NULL) {
+ rpath[sizeof (rpath) - 1] = '\0';
+ p = rpath;
+ }
+
+ if (!(exp = auth_authenticate("unmount", sin, p))) {
+ return 1;
+ }
+ mountlist_del(exp, p);
+ export_reset (exp);
+ return 1;
+}
+
+bool_t
+mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
+{
+ /* Reload /etc/xtab if necessary */
+ auth_reload();
+
+ mountlist_del_all(svc_getcaller(rqstp->rq_xprt));
+ return 1;
+}
+
+bool_t
+mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+{
+ xlog(L_NOTICE, "export request from %s",
+ inet_ntoa(svc_getcaller(rqstp->rq_xprt)->sin_addr));
+ *resp = get_exportlist();
+ return 1;
+}
+
+bool_t
+mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+{
+ xlog(L_NOTICE, "exportall request from %s",
+ inet_ntoa(svc_getcaller(rqstp->rq_xprt)->sin_addr));
+ *resp = get_exportlist();
+ return 1;
+}
+
+/*
+ * MNTv2 pathconf procedure
+ *
+ * The protocol doesn't include a status field, so Sun apparently considers
+ * it good practice to let anyone snoop on your system, even if it's
+ * pretty harmless data such as pathconf. We don't.
+ *
+ * Besides, many of the pathconf values don't make much sense on NFS volumes.
+ * FIFOs and tty device files represent devices on the *client*, so there's
+ * no point in getting the server's buffer sizes etc.
+ */
+bool_t
+mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
+{
+ struct sockaddr_in *sin = svc_getcaller(rqstp->rq_xprt);
+ struct stat stb;
+ nfs_export *exp;
+ char rpath[MAXPATHLEN+1];
+ char *p = *path;
+
+ memset(res, 0, sizeof(*res));
+
+ if (*p == '\0')
+ p = "/";
+
+ /* Reload /etc/xtab if necessary */
+ auth_reload();
+
+ /* Resolve symlinks */
+ if (realpath(p, rpath) != NULL) {
+ rpath[sizeof (rpath) - 1] = '\0';
+ p = rpath;
+ }
+
+ /* Now authenticate the intruder... */
+ if (!(exp = auth_authenticate("mount", sin, p))) {
+ return 1;
+ } else if (stat(p, &stb) < 0) {
+ xlog(L_WARNING, "can't stat exported dir %s: %s",
+ p, strerror(errno));
+ export_reset (exp);
+ return 1;
+ }
+
+ res->pc_link_max = pathconf(p, _PC_LINK_MAX);
+ res->pc_max_canon = pathconf(p, _PC_MAX_CANON);
+ res->pc_max_input = pathconf(p, _PC_MAX_INPUT);
+ res->pc_name_max = pathconf(p, _PC_NAME_MAX);
+ res->pc_path_max = pathconf(p, _PC_PATH_MAX);
+ res->pc_pipe_buf = pathconf(p, _PC_PIPE_BUF);
+ res->pc_vdisable = pathconf(p, _PC_VDISABLE);
+
+ /* Can't figure out what to do with pc_mask */
+ res->pc_mask[0] = 0;
+ res->pc_mask[1] = 0;
+
+ export_reset (exp);
+
+ return 1;
+}
+
+/*
+ * NFSv3 MOUNT procedure
+ */
+bool_t
+mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
+{
+ static int flavors[] = { AUTH_NULL, AUTH_UNIX };
+ struct knfs_fh *fh;
+
+ xlog(D_CALL, "MNT3(%s) called", *path);
+ if ((fh = get_rootfh(rqstp, path, (int *) &res->fhs_status)) != NULL) {
+ struct mountres3_ok *ok = &res->mountres3_u.mountinfo;
+
+ ok->fhandle.fhandle3_len = 32;
+ ok->fhandle.fhandle3_val = (char *) fh;
+ ok->auth_flavors.auth_flavors_len = 2;
+ ok->auth_flavors.auth_flavors_val = flavors;
+ }
+ return 1;
+}
+
+static struct knfs_fh *
+get_rootfh(struct svc_req *rqstp, dirpath *path, int *error)
+{
+ struct sockaddr_in *sin = svc_getcaller(rqstp->rq_xprt);
+ struct stat stb;
+ nfs_export *exp;
+ char rpath[MAXPATHLEN+1];
+ char *p = *path;
+
+ if (*p == '\0')
+ p = "/";
+
+ /* Reload /var/lib/nfs/etab if necessary */
+ auth_reload();
+
+ /* Resolve symlinks */
+ if (realpath(p, rpath) != NULL) {
+ rpath[sizeof (rpath) - 1] = '\0';
+ p = rpath;
+ }
+
+ /* Now authenticate the intruder... */
+ if (!(exp = auth_authenticate("mount", sin, p))) {
+ *error = NFSERR_ACCES;
+ } else if (stat(p, &stb) < 0) {
+ xlog(L_WARNING, "can't stat exported dir %s: %s",
+ p, strerror(errno));
+ if (errno == ENOENT)
+ *error = NFSERR_NOENT;
+ else
+ *error = NFSERR_ACCES;
+ } else if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
+ xlog(L_WARNING, "%s is not a directory or regular file", p);
+ *error = NFSERR_NOTDIR;
+ } else {
+ struct knfs_fh *fh;
+
+ if (!exp->m_exported)
+ export_export(exp);
+ if (!exp->m_xtabent)
+ xtab_append(exp);
+
+ /* We first try the new nfs syscall. */
+ fh = getfh ((struct sockaddr *) sin, p);
+ if (fh == NULL && errno == EINVAL)
+ /* Let's try the old one. */
+ fh = getfh_old ((struct sockaddr *) sin,
+ stb.st_dev, stb.st_ino);
+ if (fh != NULL) {
+ mountlist_add(exp, p);
+ *error = NFS_OK;
+ export_reset (exp);
+ return fh;
+ }
+ xlog(L_WARNING, "getfh failed: %s", strerror(errno));
+ *error = NFSERR_ACCES;
+ }
+ export_reset (exp);
+ return NULL;
+}
+
+static exports
+get_exportlist(void)
+{
+ static exports elist = NULL;
+ struct exportnode *e, *ne;
+ struct groupnode *g, *ng, *c, **cp;
+ nfs_export *exp;
+ int i;
+
+ if (!auth_reload() && elist)
+ return elist;
+
+ for (e = elist; e != NULL; e = ne) {
+ ne = e->ex_next;
+ for (g = e->ex_groups; g != NULL; g = ng) {
+ ng = g->gr_next;
+ xfree(g->gr_name);
+ xfree(g);
+ }
+ xfree(e->ex_dir);
+ xfree(e);
+ }
+ elist = NULL;
+
+ for (i = 0; i < MCL_MAXTYPES; i++) {
+ for (exp = exportlist[i]; exp; exp = exp->m_next) {
+ for (e = elist; e != NULL; e = e->ex_next) {
+ if (!strcmp(exp->m_export.m_path, e->ex_dir))
+ break;
+ }
+ if (!e) {
+ e = (struct exportnode *) xmalloc(sizeof(*e));
+ e->ex_next = elist;
+ e->ex_groups = NULL;
+ e->ex_dir = strdup(exp->m_export.m_path);
+ elist = e;
+ }
+
+ /* We need to check if we should remove
+ previous ones. */
+ if (i == MCL_ANONYMOUS && e->ex_groups) {
+ for (g = e->ex_groups; g; g = ng) {
+ ng = g->gr_next;
+ xfree(g->gr_name);
+ xfree(g);
+ }
+ e->ex_groups = NULL;
+ continue;
+ }
+
+ if (i != MCL_FQDN && e->ex_groups) {
+ struct hostent *hp;
+
+ cp = &e->ex_groups;
+ while ((c = *cp) != NULL) {
+ if (client_gettype (c->gr_name) == MCL_FQDN
+ && (hp = gethostbyname(c->gr_name))) {
+ hp = hostent_dup (hp);
+ if (client_check(exp->m_client, hp)) {
+ *cp = c->gr_next;
+ xfree(c->gr_name);
+ xfree(c);
+ xfree (hp);
+ if ((c = *cp) == NULL)
+ break;
+ }
+ else
+ xfree (hp);
+ }
+ cp = &(c->gr_next);
+ }
+ }
+
+ if (exp->m_export.e_hostname [0] != '\0') {
+ for (g = e->ex_groups; g; g = g->gr_next)
+ if (strcmp (exp->m_export.e_hostname,
+ g->gr_name) == 0)
+ break;
+ if (g)
+ continue;
+ g = (struct groupnode *) xmalloc(sizeof(*g));
+ g->gr_name = xstrdup(exp->m_export.e_hostname);
+ g->gr_next = e->ex_groups;
+ e->ex_groups = g;
+ }
+ }
+ }
+
+ return elist;
+}
+
+int
+main(int argc, char **argv)
+{
+ char *export_file = _PATH_EXPORTS;
+ int foreground = 0;
+ int port = 0;
+ int c;
+ struct sigaction sa;
+
+ /* Parse the command line options and arguments. */
+ opterr = 0;
+ while ((c = getopt_long(argc, argv, "Fd:f:p:P:hN:V:v", longopts, NULL)) != EOF)
+ switch (c) {
+ case 'F':
+ foreground = 1;
+ break;
+ case 'd':
+ xlog_sconfig(optarg, 1);
+ break;
+ case 'f':
+ export_file = optarg;
+ break;
+ case 'h':
+ usage(argv [0], 0);
+ break;
+ case 'P': /* XXX for nfs-server compatibility */
+ case 'p':
+ port = atoi(optarg);
+ if (port <= 0 || port > 65535) {
+ fprintf(stderr, "%s: bad port number: %s\n",
+ argv [0], optarg);
+ usage(argv [0], 1);
+ }
+ break;
+ case 'N':
+ nfs_version &= ~(1 << (atoi (optarg) - 1));
+ break;
+ case 'V':
+ nfs_version |= 1 << (atoi (optarg) - 1);
+ break;
+ case 'v':
+ printf("kmountd %s\n", VERSION);
+ exit(0);
+ case 0:
+ break;
+ case '?':
+ default:
+ usage(argv [0], 1);
+ }
+
+ /* No more arguments allowed. */
+ if (optind != argc || !(nfs_version & 0x7))
+ usage(argv [0], 1);
+
+ /* Initialize logging. */
+ xlog_open("mountd");
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ if (nfs_version & 0x1)
+ rpc_init("mountd", MOUNTPROG, MOUNTVERS,
+ mount_dispatch, port, 0);
+ if (nfs_version & (0x1 << 1))
+ rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX,
+ mount_dispatch, port, 0);
+ if (nfs_version & (0x1 << 2))
+ rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3,
+ mount_dispatch, port, 0);
+
+ sa.sa_handler = killer;
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+
+ auth_init(export_file);
+
+ if (!foreground) {
+ /* We first fork off a child. */
+ if ((c = fork()) > 0)
+ exit(0);
+ if (c < 0) {
+ xlog(L_FATAL, "mountd: cannot fork: %s\n",
+ strerror(errno));
+ }
+ /* Now we remove ourselves from the foreground.
+ Redirect stdin/stdout/stderr first. */
+ {
+ int fd = open("/dev/null", O_RDWR);
+ (void) dup2(fd, 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ if (fd > 2) (void) close(fd);
+ }
+ setsid();
+ xlog_background();
+ }
+
+ svc_run();
+
+ xlog(L_ERROR, "Ack! Gack! svc_run returned!\n");
+ exit(1);
+}
+
+static void
+usage(const char *prog, int n)
+{
+ fprintf(stderr,
+"Usage: %s [-Fhnv] [-d kind] [-f exports-file] [-V version]\n"
+" [-N version] [--debug kind] [-p|--port port] [--help] [--version]\n"
+" [--exports-file=file] [--nfs-version version]\n"
+" [--no-nfs-version version]\n", prog);
+ exit(n);
+}
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
new file mode 100644
index 0000000..9f9bc1f
--- /dev/null
+++ b/utils/mountd/mountd.h
@@ -0,0 +1,54 @@
+/*
+ * utils/mountd/mountd.h
+ *
+ * Declarations for mountd.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#ifndef MOUNTD_H
+#define MOUNTD_H
+
+#include <rpc/rpc.h>
+#include <rpc/svc.h>
+#include "nfslib.h"
+#include "exportfs.h"
+#include "mount.h"
+
+union mountd_arguments {
+ dirpath dirpath;
+};
+
+union mountd_results {
+ fhstatus fstatus;
+ mountlist mountlist;
+ exports exports;
+};
+
+/*
+ * Global Function prototypes.
+ */
+bool_t mount_null_1_svc(struct svc_req *, void *, void *);
+bool_t mount_mnt_1_svc(struct svc_req *, dirpath *, fhstatus *);
+bool_t mount_dump_1_svc(struct svc_req *, void *, mountlist *);
+bool_t mount_umnt_1_svc(struct svc_req *, dirpath *, void *);
+bool_t mount_umntall_1_svc(struct svc_req *, void *, void *);
+bool_t mount_export_1_svc(struct svc_req *, void *, exports *);
+bool_t mount_exportall_1_svc(struct svc_req *, void *, exports *);
+bool_t mount_pathconf_2_svc(struct svc_req *, dirpath *, ppathcnf *);
+bool_t mount_mnt_3_svc(struct svc_req *, dirpath *, mountres3 *);
+
+void mount_dispatch(struct svc_req *, SVCXPRT *);
+void auth_init(char *export_file);
+int auth_reload(void);
+nfs_export * auth_authenticate(char *what, struct sockaddr_in *sin,
+ char *path);
+void auth_export(nfs_export *exp);
+
+void mountlist_add(nfs_export *exp, const char *path);
+void mountlist_del(nfs_export *exp, const char *path);
+void mountlist_del_all(struct sockaddr_in *sin);
+mountlist mountlist_list(void);
+
+
+#endif /* MOUNTD_H */
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
new file mode 100644
index 0000000..593037b
--- /dev/null
+++ b/utils/mountd/mountd.man
@@ -0,0 +1,92 @@
+.\"
+.\" mountd(8)
+.\"
+.\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
+.TH rpc.mountd 8 "31 May 1999"
+.SH NAME
+rpc.mountd \- NFS mount daemon
+.SH SYNOPSIS
+.BI "/usr/sbin/rpc.mountd [" options "]"
+.SH DESCRIPTION
+The
+.B rpc.mountd
+program implements the NFS mount protocol. When receiving a MOUNT
+request from an NFS client, it checks the request against the list of
+currently exported file systems. If the client is permitted to mount
+the file system,
+.B rpc.mountd
+obtains a file handle for requested directory and returns it to
+the client.
+.SS Exporting NFS File Systems
+Making file systems available to NFS clients is called
+.IR exporting .
+.P
+Usually, a file system and the hosts it should be made available to
+are listed in the
+.B /etc/exports
+file, and invoking
+.B exportfs -a
+whenever the system is booted. The
+.BR exportfs (8)
+command makes export information available to both the kernel NFS
+server module and the
+.B rpc.mountd
+daemon.
+.P
+Alternatively, you can export individual directories temporarily
+using
+.BR exportfs 's
+.IB host : /directory
+syntax.
+.SS The rmtab File
+For every mount request received from an NFS client,
+.B rpc.mountd
+adds an entry to the
+.B /var/state/nfs/rmtab
+file. When receiving an unmount request, that entry is removed.
+user level part of the NFS service.
+.P
+However, this file is mostly ornamental. One, the client can continue
+to use the file handle even after calling
+.BR rpc.mountd 's
+UMOUNT procedure. And two, if a client reboots without notifying
+.BR rpc.mountd ,
+a stale entry will remain in
+.BR rmtab .
+.SH OPTIONS
+.TP
+.\" This file isn't touched by mountd at all--even though it
+.\" accepts the option.
+.\" .BR \-f " or " \-\-exports-file
+.\" This option specifies the exports file, listing the clients that this
+.\" server is prepared to serve and parameters to apply to each
+.\" such mount (see
+.\" .BR exports (5)).
+.\" By default, export information is read from
+.\" .IR /etc/exports .
+.TP
+.BR \-N " or " \-\-no-nfs-version
+This option can be used to request that
+.B rpc.mountd
+does not offer certain versions of NFS. The current version of
+.B rpc.mountd
+can support both NFS version 2 and the newer version 3. If the
+NFS kernel module was compiled without support for NFSv3,
+.B rpc.mountd
+must be invoked with the option
+.BR "\-\-no-nfs-version 3" .
+.TP
+.BR \-v " or " \-\-version
+Print the version of
+.B rpc.mountd
+and exit.
+.SH SEE ALSO
+.BR rpc.nfsd (8),
+.BR exportfs (8),
+.BR exports (5),
+.BR rpc.rquotad (8).
+.SH FILES
+.BR /etc/exports ,
+.BR /var/state/nfs/xtab .
+.SH AUTHOR
+Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
new file mode 100644
index 0000000..289a42e
--- /dev/null
+++ b/utils/mountd/rmtab.c
@@ -0,0 +1,173 @@
+/*
+ * utils/mountd/rmtab.c
+ *
+ * Manage the rmtab file for mountd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include "config.h"
+
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include "xmalloc.h"
+#include "misc.h"
+#include "exportfs.h"
+#include "xio.h"
+#include "mountd.h"
+
+void
+mountlist_add(nfs_export *exp, const char *path)
+{
+ struct rmtabent xe;
+ struct rmtabent *rep;
+ int lockid;
+
+ if ((lockid = xflock(_PATH_RMTAB, "a")) < 0)
+ return;
+ setrmtabent("r");
+ while ((rep = getrmtabent(1)) != NULL) {
+ if (strcmp (rep->r_client,
+ exp->m_client->m_hostname) == 0
+ && strcmp(rep->r_path, path) == 0) {
+ endrmtabent();
+ xfunlock(lockid);
+ return;
+ }
+ }
+ endrmtabent();
+ strncpy(xe.r_client, exp->m_client->m_hostname,
+ sizeof (xe.r_client) - 1);
+ xe.r_client [sizeof (xe.r_client) - 1] = '\0';
+ strncpy(xe.r_path, path, sizeof (xe.r_path) - 1);
+ xe.r_path [sizeof (xe.r_path) - 1] = '\0';
+ if (setrmtabent("a")) {
+ putrmtabent(&xe);
+ endrmtabent();
+ }
+ xfunlock(lockid);
+}
+
+void
+mountlist_del(nfs_export *exp, const char *path)
+{
+ struct rmtabent *rep;
+ FILE *fp;
+ char *hname = exp->m_client->m_hostname;
+ int lockid;
+
+ if ((lockid = xflock(_PATH_RMTAB, "w")) < 0)
+ return;
+ if (!setrmtabent("r")) {
+ xfunlock(lockid);
+ return;
+ }
+ if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
+ endrmtabent();
+ xfunlock(lockid);
+ return;
+ }
+ while ((rep = getrmtabent(1)) != NULL) {
+ if (strcmp (rep->r_client, hname)
+ || strcmp(rep->r_path, path))
+ fputrmtabent(fp, rep);
+ }
+ if (rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
+ xlog(L_ERROR, "couldn't rename %s to %s",
+ _PATH_RMTABTMP, _PATH_RMTAB);
+ }
+ endrmtabent(); /* close & unlink */
+ fendrmtabent(fp);
+ xfunlock(lockid);
+}
+
+void
+mountlist_del_all(struct sockaddr_in *sin)
+{
+ struct in_addr addr = sin->sin_addr;
+ struct hostent *hp;
+ struct rmtabent *rep;
+ nfs_export *exp;
+ FILE *fp;
+ int lockid;
+
+ if ((lockid = xflock(_PATH_RMTAB, "w")) < 0)
+ return;
+ if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) {
+ xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr));
+ xfunlock(lockid);
+ return;
+ }
+ else
+ hp = hostent_dup (hp);
+
+ if (!setrmtabent("r")) {
+ xfunlock(lockid);
+ free (hp);
+ return;
+ }
+ if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
+ endrmtabent();
+ xfunlock(lockid);
+ free (hp);
+ return;
+ }
+ while ((rep = getrmtabent(1)) != NULL) {
+ if (strcmp(rep->r_client, hp->h_name) == 0 &&
+ (exp = auth_authenticate("umountall", sin, rep->r_path))) {
+ export_reset(exp);
+ continue;
+ }
+ fputrmtabent(fp, rep);
+ }
+ if (rename(_PATH_RMTABTMP, _PATH_RMTAB) < 0) {
+ xlog(L_ERROR, "couldn't rename %s to %s",
+ _PATH_RMTABTMP, _PATH_RMTAB);
+ }
+ endrmtabent(); /* close & unlink */
+ fendrmtabent(fp);
+ xfunlock(lockid);
+ free (hp);
+}
+
+mountlist
+mountlist_list(void)
+{
+ static mountlist mlist = NULL;
+ static time_t last_mtime = 0;
+ mountlist m;
+ struct rmtabent *rep;
+ struct stat stb;
+ int lockid;
+
+ if ((lockid = xflock(_PATH_RMTAB, "r")) < 0)
+ return NULL;
+ if (stat(_PATH_RMTAB, &stb) < 0) {
+ xlog(L_ERROR, "can't stat %s", _PATH_RMTAB);
+ return NULL;
+ }
+ if (stb.st_mtime != last_mtime) {
+ while (mlist) {
+ mlist = (m = mlist)->ml_next;
+ xfree(m->ml_hostname);
+ xfree(m->ml_directory);
+ xfree(m);
+ }
+ last_mtime = stb.st_mtime;
+
+ setrmtabent("r");
+ while ((rep = getrmtabent(1)) != NULL) {
+ m = (mountlist) xmalloc(sizeof(*m));
+ m->ml_hostname = xstrdup(rep->r_client);
+ m->ml_directory = xstrdup(rep->r_path);
+ m->ml_next = mlist;
+ mlist = m;
+ }
+ endrmtabent();
+ }
+ xfunlock(lockid);
+
+ return mlist;
+}