mcinglis / rpms / nfs-utils

Forked from rpms/nfs-utils 2 years ago
Clone
Blob Blame History Raw
diff --git a/.gitignore b/.gitignore
index 5164637..126d12c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,9 @@ tests/nsm_client/nlm_sm_inter_clnt.c
 tests/nsm_client/nlm_sm_inter_svc.c
 tests/nsm_client/nlm_sm_inter_xdr.c
 utils/nfsidmap/nfsidmap
+systemd/nfs-server-generator
+systemd/nfs-config.service
+systemd/rpc-gssd.service
 # cscope database files
 cscope.*
 # generic editor backup et al
diff --git a/Makefile.am b/Makefile.am
index 4a2edc6..e1f39aa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -23,7 +23,6 @@ ACLOCAL_AMFLAGS = -I aclocal
 
 install-data-hook:
 	if [ ! -d $(DESTDIR)$(statedir) ]; then mkdir -p $(DESTDIR)$(statedir); fi
-	touch $(DESTDIR)$(statedir)/xtab; chmod 644 $(DESTDIR)$(statedir)/xtab
 	touch $(DESTDIR)$(statedir)/etab; chmod 644 $(DESTDIR)$(statedir)/etab
 	touch $(DESTDIR)$(statedir)/rmtab; chmod 644 $(DESTDIR)$(statedir)/rmtab
 	mkdir -p $(DESTDIR)$(statdpath)/sm $(DESTDIR)$(statdpath)/sm.bak
@@ -32,7 +31,7 @@ install-data-hook:
 	-chown $(statduser) $(DESTDIR)$(statdpath)/sm $(DESTDIR)$(statdpath)/sm.bak $(DESTDIR)$(statdpath)/state
 
 uninstall-hook:
-	rm $(DESTDIR)$(statedir)/xtab
+	rm -f $(DESTDIR)$(statedir)/xtab
 	rm $(DESTDIR)$(statedir)/etab
 	rm $(DESTDIR)$(statedir)/rmtab
 	rm $(DESTDIR)$(statdpath)/state
diff --git a/configure.ac b/configure.ac
index 1daf5b8..8a5aa2e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,6 +24,12 @@ AC_ARG_WITH(statedir,
 	statedir=$withval,
 	statedir=/var/lib/nfs)
 	AC_SUBST(statedir)
+AC_ARG_WITH(nfsconfig,
+	[AC_HELP_STRING([--with-nfsconfig=/config/file],
+			[use general config file /config/file @<:@default=/etc/nfs.conf@:>@])],
+	nfsconfig=$withval,
+	nfsconfig=/etc/nfs.conf)
+	AC_SUBST(nfsconfig)
 AC_ARG_WITH(statdpath,
 	[AC_HELP_STRING([--with-statdpath=/foo],
 			[define the statd state dir as /foo instead of the NFS statedir @<:@default=/var/lib/nfs@:>@])],
@@ -468,6 +474,7 @@ dnl Export some path names to config.h
 dnl *************************************************************
 AC_DEFINE_UNQUOTED(NFS_STATEDIR, "$statedir", [This defines the location of the NFS state files. Warning: this must match definitions in config.mk!])
 AC_DEFINE_UNQUOTED(NSM_DEFAULT_STATEDIR, "$statdpath", [Define this to the pathname where statd keeps its state file])
+AC_DEFINE_UNQUOTED(NFS_CONFFILE, "$nfsconfig", [This defines the location of NFS daemon config file])
 
 if test "x$cross_compiling" = "xno"; then
 	CFLAGS_FOR_BUILD=${CFLAGS_FOR_BUILD-"$CFLAGS"}
@@ -511,8 +518,20 @@ AC_SUBST([AM_CFLAGS], ["$my_am_cflags"])
 # Make sure that $ACLOCAL_FLAGS are used during a rebuild
 AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \$(ACLOCAL_FLAGS)"])
 
+# make libexecdir available for substituion in config files
+# 2 "evals" needed late to expand variable names.
+AC_SUBST([_libexecdir])
+AC_CONFIG_COMMANDS_PRE([eval eval _libexecdir=$libexecdir])
+
+# make _sysconfdir available for substituion in config files
+# 2 "evals" needed late to expand variable names.
+AC_SUBST([_sysconfdir])
+AC_CONFIG_COMMANDS_PRE([eval eval _sysconfdir=$sysconfdir])
+
 AC_CONFIG_FILES([
 	Makefile
+	systemd/nfs-config.service
+	systemd/rpc-gssd.service
 	linux-nfs/Makefile
 	support/Makefile
 	support/export/Makefile
diff --git a/support/export/export.c b/support/export/export.c
index e1bebce..15e91cb 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -15,6 +15,8 @@
 #include <sys/param.h>
 #include <netinet/in.h>
 #include <stdlib.h>
+#include <dirent.h>
+#include <errno.h>
 #include "xmalloc.h"
 #include "nfslib.h"
 #include "exportfs.h"
@@ -68,11 +70,15 @@ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
 /**
  * export_read - read entries from /etc/exports
  * @fname: name of file to read from
+ * @ignore_hosts: don't check validity of host names
  *
  * Returns number of read entries.
+ * @ignore_hosts can be set when the host names won't be used
+ * and when getting delays or errors due to problems with
+ * hostname looking is not acceptable.
  */
 int
-export_read(char *fname)
+export_read(char *fname, int ignore_hosts)
 {
 	struct exportent	*eep;
 	nfs_export		*exp;
@@ -81,7 +87,7 @@ export_read(char *fname)
 
 	setexportent(fname, "r");
 	while ((eep = getexportent(0,1)) != NULL) {
-		exp = export_lookup(eep->e_hostname, eep->e_path, 0);
+		exp = export_lookup(eep->e_hostname, eep->e_path, ignore_hosts);
 		if (!exp) {
 			if (export_create(eep, 0))
 				/* possible complaints already logged */
@@ -96,6 +102,70 @@ export_read(char *fname)
 }
 
 /**
+ * export_d_read - read entries from /etc/exports.
+ * @fname: name of directory to read from
+ * @ignore_hosts: don't check validity of host names
+ *
+ * Returns number of read entries.
+ * Based on mnt_table_parse_dir() in
+ *  util-linux-ng/shlibs/mount/src/tab_parse.c
+ */
+int
+export_d_read(const char *dname, int ignore_hosts)
+{
+	int n = 0, i;
+	struct dirent **namelist = NULL;
+	int volumes = 0;
+
+
+	n = scandir(dname, &namelist, NULL, versionsort);
+	if (n < 0) {
+		if (errno == ENOENT)
+			/* Silently return */
+			return volumes;
+		xlog(L_NOTICE, "scandir %s: %s", dname, strerror(errno));
+	} else if (n == 0)
+		return volumes;
+
+	for (i = 0; i < n; i++) {
+		struct dirent *d = namelist[i];
+		size_t namesz;
+		char fname[PATH_MAX + 1];
+		int fname_len;
+
+
+		if (d->d_type != DT_UNKNOWN
+		    && d->d_type != DT_REG
+		    && d->d_type != DT_LNK)
+			continue;
+		if (*d->d_name == '.')
+			continue;
+
+#define _EXT_EXPORT_SIZ   (sizeof(_EXT_EXPORT) - 1)
+		namesz = strlen(d->d_name);
+		if (!namesz
+		    || namesz < _EXT_EXPORT_SIZ + 1
+		    || strcmp(d->d_name + (namesz - _EXT_EXPORT_SIZ),
+			      _EXT_EXPORT))
+			continue;
+
+		fname_len = snprintf(fname, PATH_MAX +1, "%s/%s", dname, d->d_name);
+		if (fname_len > PATH_MAX) {
+			xlog(L_WARNING, "Too long file name: %s in %s", d->d_name, dname);
+			continue;
+		}
+
+		volumes += export_read(fname, ignore_hosts);
+	}
+
+	for (i = 0; i < n; i++)
+		free(namelist[i]);
+	free(namelist);
+
+	return volumes;
+}
+
+/**
  * export_create - create an in-core nfs_export record from an export entry
  * @xep: export entry to lookup
  * @canonical: if set, e_hostname is known to be canonical DNS name
diff --git a/support/export/xtab.c b/support/export/xtab.c
index e953071..10d9dbc 100644
--- a/support/export/xtab.c
+++ b/support/export/xtab.c
@@ -1,7 +1,7 @@
 /*
  * support/export/xtab.c
  *
- * Interface to the xtab file.
+ * Interface to the etab/exports file.
  *
  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  */
@@ -29,7 +29,6 @@ xtab_read(char *xtab, char *lockfn, int is_export)
 {
     /* is_export == 0  => reading /proc/fs/nfs/exports - we know these things are exported to kernel
      * is_export == 1  => reading /var/lib/nfs/etab - these things are allowed to be exported
-     * is_export == 2  => reading /var/lib/nfs/xtab - these things might be known to kernel
      */
 	struct exportent	*xp;
 	nfs_export		*exp;
@@ -55,9 +54,6 @@ xtab_read(char *xtab, char *lockfn, int is_export)
 			if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
 				v4root_needed = 0;
 			break;
-		case 2:
-			exp->m_exported = -1;/* may be exported */
-			break;
 		}
 	}
 	endexportent();
@@ -79,7 +75,7 @@ xtab_mount_read(void)
 		return xtab_read(_PATH_PROC_EXPORTS_ALT,
 				 _PATH_PROC_EXPORTS_ALT, 0);
 	} else
-		return xtab_read(_PATH_XTAB, _PATH_XTABLCK, 2);
+		return 0;
 }
 
 int
@@ -135,29 +131,6 @@ xtab_export_write()
 	return xtab_write(_PATH_ETAB, _PATH_ETABTMP, _PATH_ETABLCK, 1);
 }
 
-int
-xtab_mount_write()
-{
-	return xtab_write(_PATH_XTAB, _PATH_XTABTMP, _PATH_XTABLCK, 0);
-}
-
-void
-xtab_append(nfs_export *exp)
-{
-	struct exportent xe;
-	int		lockid;
-
-	if ((lockid = xflock(_PATH_XTABLCK, "w")) < 0)
-		return;
-	setexportent(_PATH_XTAB, "a");
-	xe = exp->m_export;
-	xe.e_hostname = exp->m_client->m_hostname;
-	putexportent(&xe);
-	endexportent();
-	xfunlock(lockid);
-	exp->m_xtabent = 1;
-}
-
 /*
  * rename newfile onto oldfile unless
  * they are identical
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 4cac203..08ef30a 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -96,7 +96,7 @@ typedef struct mexport {
 	struct mexport *	m_next;
 	struct mclient *	m_client;
 	struct exportent	m_export;
-	int			m_exported;	/* known to knfsd. -1 means not sure */
+	int			m_exported;	/* known to knfsd. */
 	int			m_xtabent  : 1,	/* xtab entry exists */
 				m_mayexport: 1,	/* derived from xtabbed */
 				m_changed  : 1, /* options (may) have changed */
@@ -134,7 +134,8 @@ struct addrinfo *		client_resolve(const struct sockaddr *sap);
 int 				client_member(const char *client,
 						const char *name);
 
-int				export_read(char *fname);
+int				export_read(char *fname, int ignore_hosts);
+int				export_d_read(const char *dname, int ignore_hosts);
 void				export_reset(nfs_export *);
 nfs_export *			export_lookup(char *hname, char *path, int caconical);
 nfs_export *			export_find(const struct addrinfo *ai,
@@ -149,9 +150,7 @@ int				export_unexport(nfs_export *);
 
 int				xtab_mount_read(void);
 int				xtab_export_read(void);
-int				xtab_mount_write(void);
 int				xtab_export_write(void);
-void				xtab_append(nfs_export *);
 
 int				secinfo_addflavor(struct flav_info *, struct exportent *);
 
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index ddd71ac..777f398 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -35,15 +35,6 @@
 #ifndef _PATH_IDMAPDCONF
 #define _PATH_IDMAPDCONF	"/etc/idmapd.conf"
 #endif
-#ifndef _PATH_XTAB
-#define _PATH_XTAB		NFS_STATEDIR "/xtab"
-#endif
-#ifndef _PATH_XTABTMP
-#define _PATH_XTABTMP		NFS_STATEDIR "/xtab.tmp"
-#endif
-#ifndef _PATH_XTABLCK
-#define _PATH_XTABLCK		NFS_STATEDIR "/.xtab.lock"
-#endif
 #ifndef _PATH_ETAB
 #define _PATH_ETAB		NFS_STATEDIR "/etab"
 #endif
diff --git a/systemd/Makefile.am b/systemd/Makefile.am
index 03f96e9..49c9b8d 100644
--- a/systemd/Makefile.am
+++ b/systemd/Makefile.am
@@ -39,8 +39,16 @@ endif
 EXTRA_DIST = $(unit_files)
 
 unit_dir = /usr/lib/systemd/system
+generator_dir = /usr/lib/systemd/system-generators
+
+EXTRA_PROGRAMS	= nfs-server-generator
+genexecdir = $(generator_dir)
+nfs_server_generator_LDADD = ../support/export/libexport.a \
+			     ../support/nfs/libnfs.a \
+			     ../support/misc/libmisc.a
 
 if INSTALL_SYSTEMD
+genexec_PROGRAMS = nfs-server-generator
 install-data-hook: $(unit_files)
 	mkdir -p $(DESTDIR)/$(unitdir)
 	cp $(unit_files) $(DESTDIR)/$(unitdir)
diff --git a/systemd/nfs-config.service b/systemd/nfs-config.service
deleted file mode 100644
index bd69e84..0000000
--- a/systemd/nfs-config.service
+++ /dev/null
@@ -1,13 +0,0 @@
-[Unit]
-Description=Preprocess NFS configuration
-After=local-fs.target
-DefaultDependencies=no
-
-[Service]
-Type=oneshot
-# This service needs to run any time any nfs service
-# is started, so changes to local config files get
-# incorporated.  Having "RemainAfterExit=no" (the default)
-# ensures this happens.
-RemainAfterExit=no
-ExecStart=/usr/libexec/nfs-utils/nfs-utils_env.sh
diff --git a/systemd/nfs-config.service.in b/systemd/nfs-config.service.in
new file mode 100644
index 0000000..e89dc54
--- /dev/null
+++ b/systemd/nfs-config.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=Preprocess NFS configuration
+After=local-fs.target
+DefaultDependencies=no
+
+[Service]
+Type=oneshot
+# This service needs to run any time any nfs service
+# is started, so changes to local config files get
+# incorporated.  Having "RemainAfterExit=no" (the default)
+# ensures this happens.
+RemainAfterExit=no
+ExecStart=@_libexecdir@/nfs-utils/nfs-utils_env.sh
diff --git a/systemd/nfs-server-generator.c b/systemd/nfs-server-generator.c
new file mode 100644
index 0000000..7c40b3f
--- /dev/null
+++ b/systemd/nfs-server-generator.c
@@ -0,0 +1,150 @@
+/*
+ * nfs-server-generator:
+ *   systemd generator to create ordering dependencies between
+ *   nfs-server and various filesystem mounts
+ *
+ * 1/ nfs-server should start Before any 'nfs' mountpoints are
+ *    mounted, in case they are loop-back mounts.  This ordering is particularly
+ *    important for the shutdown side, so the nfs-server is stopped
+ *    after the filesystems are unmounted.
+ * 2/ nfs-server should start After all exported filesystems are mounted
+ *    so there is no risk of exporting the underlying directory.
+ *    This is particularly important for _net mounts which
+ *    are not caught by "local-fs.target".
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <mntent.h>
+
+#include "misc.h"
+#include "nfslib.h"
+#include "exportfs.h"
+
+/* A simple "set of strings" to remove duplicates
+ * found in /etc/exports
+ */
+struct list {
+	struct list *next;
+	char *name;
+};
+static int is_unique(struct list **lp, char *path)
+{
+	struct list *l = *lp;
+
+	while (l) {
+		if (strcmp(l->name, path) == 0)
+			return 0;
+		l = l->next;
+	}
+	l = malloc(sizeof(*l));
+	if (l == NULL)
+		return 0;
+	l->name = path;
+	l->next = *lp;
+	*lp = l;
+	return 1;
+}
+
+/* We need to convert a path name to a systemd unit
+ * name.  This requires some translation ('/' -> '-')
+ * and some escaping.
+ */
+static void systemd_escape(FILE *f, char *path)
+{
+	while (*path == '/')
+		path++;
+	if (!*path) {
+		/* "/" becomes "-", otherwise leading "/" is ignored */
+		fputs("-", f);
+		return;
+	}
+	while (*path) {
+		char c = *path++;
+
+		if (c == '/') {
+			/* multiple non-trailing slashes become '-' */
+			while (*path == '/')
+				path++;
+			if (*path)
+				fputs("-", f);
+		} else if (isalnum(c) || c == ':' || c == '.')
+			fputc(c, f);
+		else
+			fprintf(f, "\\x%02x", c & 0xff);
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	char		*path;
+	char		dirbase[] = "/nfs-server.service.d";
+	char		filebase[] = "/order-with-mounts.conf";
+	nfs_export	*exp;
+	int		i;
+	struct list	*list = NULL;
+	FILE		*f, *fstab;
+	struct mntent	*mnt;
+
+	if (argc != 4 || argv[1][0] != '/') {
+		fprintf(stderr, "nfs-server-generator: create systemd dependencies for nfs-server\n");
+		fprintf(stderr, "Usage: normal-dir early-dir late-dir\n");
+		exit(1);
+	}
+
+	path = malloc(strlen(argv[1]) + sizeof(dirbase) + sizeof(filebase));
+	if (!path)
+		exit(2);
+	if (export_read(_PATH_EXPORTS, 1) +
+	    export_d_read(_PATH_EXPORTS_D, 1) == 0)
+		/* Nothing is exported, so nothing to do */
+		exit(0);
+
+	strcat(strcpy(path, argv[1]), dirbase);
+	mkdir(path, 0755);
+	strcat(path, filebase);
+	f = fopen(path, "w");
+	if (!f)
+		exit(1);
+	fprintf(f, "# Automatically generated by nfs-server-generator\n\n[Unit]\n");
+
+	for (i = 0; i < MCL_MAXTYPES; i++) {
+		for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
+			if (!is_unique(&list, exp->m_export.e_path))
+				continue;
+			if (strchr(exp->m_export.e_path, ' '))
+				fprintf(f, "RequiresMountsFor=\"%s\"\n",
+					exp->m_export.e_path);
+			else
+				fprintf(f, "RequiresMountsFor=%s\n",
+					exp->m_export.e_path);
+		}
+	}
+
+	fstab = setmntent("/etc/fstab", "r");
+	if (!fstab)
+		exit(1);
+
+	while ((mnt = getmntent(fstab)) != NULL) {
+		if (strcmp(mnt->mnt_type, "nfs") != 0 &&
+		    strcmp(mnt->mnt_type, "nfs4") != 0)
+			continue;
+		fprintf(f, "Before= ");
+		systemd_escape(f, mnt->mnt_dir);
+		fprintf(f, ".mount\n");
+	}
+
+	fclose(fstab);
+	fclose(f);
+
+	exit(0);
+}
diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
index 2ccdc63..196c818 100644
--- a/systemd/nfs-server.service
+++ b/systemd/nfs-server.service
@@ -16,9 +16,6 @@ Before= rpc-statd-notify.service
 Wants=auth-rpcgss-module.service
 After=rpc-gssd.service gssproxy.service rpc-svcgssd.service
 
-# start/stop server before/after client
-Before=remote-fs-pre.target
-
 Wants=nfs-config.service
 After=nfs-config.service
 
diff --git a/systemd/rpc-gssd.service b/systemd/rpc-gssd.service
deleted file mode 100644
index d4a3819..0000000
--- a/systemd/rpc-gssd.service
+++ /dev/null
@@ -1,19 +0,0 @@
-[Unit]
-Description=RPC security service for NFS client and server
-DefaultDependencies=no
-Conflicts=umount.target
-Requires=var-lib-nfs-rpc_pipefs.mount
-After=var-lib-nfs-rpc_pipefs.mount
-
-ConditionPathExists=/etc/krb5.keytab
-
-PartOf=nfs-utils.service
-
-Wants=nfs-config.service
-After=nfs-config.service
-
-[Service]
-EnvironmentFile=-/run/sysconfig/nfs-utils
-
-Type=forking
-ExecStart=/usr/sbin/rpc.gssd $GSSDARGS
diff --git a/systemd/rpc-gssd.service.in b/systemd/rpc-gssd.service.in
new file mode 100644
index 0000000..1a7911c
--- /dev/null
+++ b/systemd/rpc-gssd.service.in
@@ -0,0 +1,19 @@
+[Unit]
+Description=RPC security service for NFS client and server
+DefaultDependencies=no
+Conflicts=umount.target
+Requires=var-lib-nfs-rpc_pipefs.mount
+After=var-lib-nfs-rpc_pipefs.mount
+
+ConditionPathExists=@_sysconfdir@/krb5.keytab
+
+PartOf=nfs-utils.service
+
+Wants=nfs-config.service
+After=nfs-config.service
+
+[Service]
+EnvironmentFile=-/run/sysconfig/nfs-utils
+
+Type=forking
+ExecStart=/usr/sbin/rpc.gssd $GSSDARGS
diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
index 4ca4bc4..88ccdae 100644
--- a/tools/mountstats/mountstats.py
+++ b/tools/mountstats/mountstats.py
@@ -412,6 +412,8 @@ class DeviceData:
         print('  short reads: %d  short writes: %d' % \
             (self.__nfs_data['shortreads'], self.__nfs_data['shortwrites']))
         print('  NFSERR_DELAYs from server: %d' % self.__nfs_data['delay'])
+        print('  pNFS READs: %d' % self.__nfs_data['pnfsreads'])
+        print('  pNFS WRITEs: %d' % self.__nfs_data['pnfswrites'])
 
     def display_nfs_bytes(self):
         """Pretty-print the NFS event counters
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index a00b5ea..98368a5 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -26,7 +26,6 @@
 #include <fcntl.h>
 #include <netdb.h>
 #include <errno.h>
-#include <dirent.h>
 #include <limits.h>
 #include <time.h>
 
@@ -47,7 +46,6 @@ static void	error(nfs_export *exp, int err);
 static void	usage(const char *progname, int n);
 static void	validate_export(nfs_export *exp);
 static int	matchhostname(const char *hostname1, const char *hostname2);
-static int	export_d_read(const char *dname);
 static void grab_lockfile(void);
 static void release_lockfile(void);
 
@@ -185,8 +183,8 @@ main(int argc, char **argv)
 	atexit(release_lockfile);
 
 	if (f_export && ! f_ignore) {
-		if (! (export_read(_PATH_EXPORTS) +
-		       export_d_read(_PATH_EXPORTS_D))) {
+		if (! (export_read(_PATH_EXPORTS, 0) +
+		       export_d_read(_PATH_EXPORTS_D, 0))) {
 			if (f_verbose)
 				xlog(L_WARNING, "No file systems exported!");
 		}
@@ -221,8 +219,6 @@ main(int argc, char **argv)
 	xtab_export_write();
 	if (new_cache)
 		cache_flush(force_flush);
-	if (!new_cache)
-		xtab_mount_write();
 
 	return export_errno;
 }
@@ -240,7 +236,7 @@ exports_update_one(nfs_export *exp, int verbose)
 		       exp->m_export.e_path, exp->m_export.e_mountpoint);
 		exp->m_mayexport = 0;
 	}
-	if (exp->m_mayexport && ((exp->m_exported<1) || exp->m_changed)) {
+	if (exp->m_mayexport && exp->m_changed) {
 		if (verbose)
 			printf("%sexporting %s:%s to kernel\n",
 			       exp->m_exported ?"re":"",
@@ -700,63 +696,6 @@ out:
 	return result;
 }
 
-/* Based on mnt_table_parse_dir() in
-   util-linux-ng/shlibs/mount/src/tab_parse.c */
-static int
-export_d_read(const char *dname)
-{
-	int n = 0, i;
-	struct dirent **namelist = NULL;
-	int volumes = 0;
-
-
-	n = scandir(dname, &namelist, NULL, versionsort);
-	if (n < 0) {
-		if (errno == ENOENT)
-			/* Silently return */
-			return volumes;
-		xlog(L_NOTICE, "scandir %s: %s", dname, strerror(errno));
-	} else if (n == 0)
-		return volumes;
-
-	for (i = 0; i < n; i++) {
-		struct dirent *d = namelist[i];
-		size_t namesz;
-		char fname[PATH_MAX + 1];
-		int fname_len;
-
-
-		if (d->d_type != DT_UNKNOWN
-		    && d->d_type != DT_REG
-		    && d->d_type != DT_LNK)
-			continue;
-		if (*d->d_name == '.')
-			continue;
-
-#define _EXT_EXPORT_SIZ   (sizeof(_EXT_EXPORT) - 1)
-		namesz = strlen(d->d_name);
-		if (!namesz
-		    || namesz < _EXT_EXPORT_SIZ + 1
-		    || strcmp(d->d_name + (namesz - _EXT_EXPORT_SIZ),
-			      _EXT_EXPORT))
-			continue;
-
-		fname_len = snprintf(fname, PATH_MAX +1, "%s/%s", dname, d->d_name);
-		if (fname_len > PATH_MAX) {
-			xlog(L_WARNING, "Too long file name: %s in %s", d->d_name, dname);
-			continue;
-		}
-
-		volumes += export_read(fname);
-	}
-
-	for (i = 0; i < n; i++)
-		free(namelist[i]);
-	free(namelist);
-
-	return volumes;
-}
-
 static char
 dumpopt(char c, char *fmt, ...)
 {
diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man
index b9200c7..d4ab894 100644
--- a/utils/idmapd/idmapd.man
+++ b/utils/idmapd/idmapd.man
@@ -23,6 +23,29 @@ is the NFSv4 ID <-> name mapping daemon.  It provides functionality to
 the NFSv4 kernel client and server, to which it communicates via
 upcalls, by translating user and group IDs to names, and vice versa.
 .Pp
+The system derives the
+.I user
+part of the string by performing a password or group lookup.
+The lookup mechanism is configured in
+.Pa /etc/idmapd.conf
+.Pp
+By default, the
+.I domain
+part of the string is the system's DNS domain name.
+It can also be specified in
+.Pa /etc/idmapd.conf
+if the system is multi-homed,
+or if the system's DNS domain name does
+not match the name of the system's Kerberos realm.
+.Pp
+When the domain is not specified in /etc/idmapd.conf
+the local DNS server will be queried for the 
+.Sy _nfsv4idmapdomain 
+text record. If the record exists
+that will be used as the domain. When the record
+does not exist, the domain part of the DNS domain
+will used. 
+.Pp
 Note that on more recent kernels only the NFSv4 server uses
 .Nm .
 The NFSv4 client instead uses
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 9de6794..d5dfb5e 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -948,6 +948,7 @@ static int nfs_is_permanent_error(int error)
 	case ETIMEDOUT:
 	case ECONNREFUSED:
 	case EHOSTUNREACH:
+	case EOPNOTSUPP:	/* aka RPC_PROGNOTREGISTERED */
 	case EAGAIN:
 		return 0;	/* temporary */
 	default:
@@ -1019,8 +1020,7 @@ static int nfsmount_parent(struct nfsmount_info *mi)
 	if (nfs_try_mount(mi))
 		return EX_SUCCESS;
 
-	/* retry background mounts when the server is not up */
-	if (nfs_is_permanent_error(errno) && errno != EOPNOTSUPP) {
+	if (nfs_is_permanent_error(errno)) {
 		mount_error(mi->spec, mi->node, errno);
 		return EX_FAIL;
 	}
@@ -1055,8 +1055,7 @@ static int nfsmount_child(struct nfsmount_info *mi)
 		if (nfs_try_mount(mi))
 			return EX_SUCCESS;
 
-		/* retry background mounts when the server is not up */
-		if (nfs_is_permanent_error(errno) && errno != EOPNOTSUPP)
+		if (nfs_is_permanent_error(errno))
 			break;
 
 		if (time(NULL) > timeout)
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
index 0881d9a..b612d88 100644
--- a/utils/mountd/auth.c
+++ b/utils/mountd/auth.c
@@ -46,7 +46,6 @@ void
 auth_init(void)
 {
 	auth_reload();
-	xtab_mount_write();
 }
 
 /*
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 7a51b09..981abd4 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -107,7 +107,6 @@ unregister_services (void)
 static void
 cleanup_lockfiles (void)
 {
-	unlink(_PATH_XTABLCK);
 	unlink(_PATH_ETABLCK);
 	unlink(_PATH_RMTABLCK);
 }
@@ -289,7 +288,7 @@ mount_umntall_1_svc(struct svc_req *rqstp, void *UNUSED(argp),
 	xlog(D_CALL, "Received UMNTALL request from %s",
 		host_ntop(sap, buf, sizeof(buf)));
 
-	/* Reload /etc/xtab if necessary */
+	/* Reload /etc/exports if necessary */
 	auth_reload();
 
 	mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt));
@@ -350,7 +349,7 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
 	if (*p == '\0')
 		p = "/";
 
-	/* Reload /etc/xtab if necessary */
+	/* Reload /etc/exports if necessary */
 	auth_reload();
 
 	/* Resolve symlinks */
@@ -531,12 +530,6 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 	} else {
 		int did_export = 0;
 	retry:
-		if (exp->m_exported<1) {
-			export_export(exp);
-			did_export = 1;
-		}
-		if (!exp->m_xtabent)
-			xtab_append(exp);
 
 		if (v3)
 			fh = getfh_size((struct sockaddr_in *)sap, p, 64);
diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
index fcdda7f..e6e514b 100644
--- a/utils/nfsdcltrack/nfsdcltrack.c
+++ b/utils/nfsdcltrack/nfsdcltrack.c
@@ -43,6 +43,7 @@
 #include <sys/capability.h>
 #endif
 
+#include "conffile.h"
 #include "xlog.h"
 #include "sqlite.h"
 
@@ -55,6 +56,8 @@
 /* defined by RFC 3530 */
 #define NFS4_OPAQUE_LIMIT	1024
 
+char *conf_path = NFS_CONFFILE;
+
 /* private data structures */
 struct cltrack_cmd {
 	char *name;
@@ -553,6 +556,7 @@ int
 main(int argc, char **argv)
 {
 	char arg;
+	char *val;
 	int rc = 0;
 	char *progname, *cmdarg = NULL;
 	struct cltrack_cmd *cmd;
@@ -562,6 +566,14 @@ main(int argc, char **argv)
 	xlog_syslog(1);
 	xlog_stderr(0);
 
+	conf_init();
+	val = conf_get_str("nfsdcltrack", "storagedir");
+	if (val)
+		storagedir = val;
+	rc = conf_get_num("nfsdcltrack", "debug", 0);
+	if (rc > 0)
+		xlog_config(D_ALL, 1);
+
 	/* process command-line options */
 	while ((arg = getopt_long(argc, argv, "hdfs:", longopts,
 				  NULL)) != EOF) {
diff --git a/utils/nfsdcltrack/nfsdcltrack.man b/utils/nfsdcltrack/nfsdcltrack.man
index 4b8f4d7..cc24b7a 100644
--- a/utils/nfsdcltrack/nfsdcltrack.man
+++ b/utils/nfsdcltrack/nfsdcltrack.man
@@ -67,6 +67,20 @@ Check to see if a nfs_client_id4 is allowed to reclaim. This command requires a
 .IP "\fBgracedone\fR" 4
 .IX Item "gracedone"
 Remove any unreclaimed client records from the database. This command requires a epoch boot time as an argument.
+.SH "EXTERNAL CONFIGURATION"
+The directory for stable storage information can be set via the file
+.B /etc/nfs.conf
+by setting the
+.B storagedir
+value in the
+.B nfsdcltrack
+section.  For example:
+.in +5
+[nfsdcltrack]
+.br
+  storagedir = /shared/nfs/nfsdcltrack
+.in -5
+Debuging to syslog can also be enabled by setting "debug = 1" in this file.
 .SH "LEGACY TRANSITION MECHANISM"
 .IX Header "LEGACY TRANSITION MECHANISM"
 The Linux kernel NFSv4 server has historically tracked this information
diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man
index 2f17cf2..2af16f3 100644
--- a/utils/nfsidmap/nfsidmap.man
+++ b/utils/nfsidmap/nfsidmap.man
@@ -39,6 +39,15 @@ if the system is multi-homed,
 or if the system's DNS domain name does
 not match the name of the system's Kerberos realm.
 .PP
+When the domain is not specified in 
+.I /etc/idmapd.conf
+the local DNS server will be queried for the 
+.I _nfsv4idmapdomain 
+text record. If the record exists
+that will be used as the domain. When the record
+does not exist, the domain part of the DNS domain
+will used. 
+.PP
 The
 .I /usr/sbin/nfsidmap
 program performs translations on behalf of the kernel.