walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
Blob Blame History Raw
diff --git a/.gitignore b/.gitignore
index 4bff9e3..f5b5cf0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,6 +64,7 @@ tests/nsm_client/nlm_sm_inter.h
 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
 # cscope database files
 cscope.*
 # generic editor backup et al
diff --git a/aclocal/keyutils.m4 b/aclocal/keyutils.m4
new file mode 100644
index 0000000..84bc112
--- /dev/null
+++ b/aclocal/keyutils.m4
@@ -0,0 +1,11 @@
+dnl Checks for keyutils library and headers
+dnl
+AC_DEFUN([AC_KEYUTILS], [
+
+  dnl Check for libkeyutils; do not add to LIBS if found
+  AC_CHECK_LIB([keyutils], [keyctl_instantiate], [LIBKEYUTILS=-lkeyutils], ,)
+  AC_SUBST(LIBKEYUTILS)
+
+  AC_CHECK_HEADERS([keyutils.h], ,
+  		   [AC_MSG_ERROR([keyutils.h header not found.])])
+])dnl
diff --git a/aclocal/libnfsidmap.m4 b/aclocal/libnfsidmap.m4
index cfcde2f..4faa923 100644
--- a/aclocal/libnfsidmap.m4
+++ b/aclocal/libnfsidmap.m4
@@ -14,4 +14,8 @@ AC_DEFUN([AC_LIBNFSIDMAP], [
                [AC_DEFINE([HAVE_NFS4_SET_DEBUG], 1,
                           [Define to 1 if you have the `nfs4_set_debug' function.])])
 
+  dnl only enable nfsidmap when libnfsidmap supports it
+  AC_CHECK_LIB([nfsidmap], [nfs4_owner_to_uid], [enable_nfsidmap=yes],
+               [enable_nfsidmap=no])
+
 ])dnl
diff --git a/configure.ac b/configure.ac
index 3058be6..92833e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -136,7 +136,7 @@ AC_ARG_ENABLE(tirpc,
 	[AC_HELP_STRING([--enable-tirpc],
 			[enable use of TI-RPC @<:@default=yes@:>@])],
 	enable_tirpc=$enableval,
-	enable_tirpc='yes')
+	enable_tirpc='')
 AC_ARG_ENABLE(ipv6,
 	[AC_HELP_STRING([--enable-ipv6],
                         [enable support for IPv6 @<:@default=no@:>@])],
@@ -247,6 +247,12 @@ if test "$enable_nfsv4" = yes; then
   dnl check for nfsidmap libraries and headers
   AC_LIBNFSIDMAP
 
+  dnl enable nfsidmap when its support by libnfsidmap
+  AM_CONDITIONAL(CONFIG_NFSIDMAP, [test "$enable_nfsidmap" = "yes"])
+
+  dnl check for the keyutils libraries and headers
+  AC_KEYUTILS
+
   dnl librpcsecgss already has a dependency on libgssapi,
   dnl but we need to make sure we get the right version
   if test "$enable_gss" = yes; then
@@ -435,6 +441,7 @@ AC_CONFIG_FILES([
 	utils/mountd/Makefile
 	utils/nfsd/Makefile
 	utils/nfsstat/Makefile
+	utils/nfsidmap/Makefile
 	utils/showmount/Makefile
 	utils/statd/Makefile
 	tests/Makefile
diff --git a/support/export/client.c b/support/export/client.c
index dbfc2b1..ba2db8f 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -178,6 +178,7 @@ out_badprefix:
 static int
 init_netmask6(nfs_client *UNUSED(clp), const char *UNUSED(slash))
 {
+	return 0;
 }
 #endif	/* IPV6_SUPPORTED */
 
diff --git a/support/export/export.c b/support/export/export.c
index f528603..4fda30a 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -38,6 +38,7 @@ export_free(nfs_export *exp)
 	xfree(exp->m_export.e_sqgids);
 	free(exp->m_export.e_mountpoint);
 	free(exp->m_export.e_fslocdata);
+	free(exp->m_export.e_uuid);
 
 	xfree(exp->m_export.e_hostname);
 	xfree(exp);
diff --git a/support/export/hostname.c b/support/export/hostname.c
index 3c55ce7..efcb75c 100644
--- a/support/export/hostname.c
+++ b/support/export/hostname.c
@@ -30,10 +30,6 @@
 #include "sockaddr.h"
 #include "exportfs.h"
 
-#ifndef HAVE_DECL_AI_ADDRCONFIG
-#define AI_ADDRCONFIG	0
-#endif
-
 /**
  * host_ntop - generate presentation address given a sockaddr
  * @sap: pointer to socket address
@@ -170,7 +166,7 @@ host_addrinfo(const char *hostname)
 #endif
 		/* don't return duplicates */
 		.ai_protocol	= (int)IPPROTO_UDP,
-		.ai_flags	= AI_ADDRCONFIG | AI_CANONNAME,
+		.ai_flags	= AI_CANONNAME,
 	};
 	int error;
 
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index 3db5bec..cee826b 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -25,6 +25,12 @@
 #ifndef _PATH_EXPORTS
 #define _PATH_EXPORTS		"/etc/exports"
 #endif
+#ifndef _PATH_EXPORTS_D
+#define _PATH_EXPORTS_D         "/etc/exports.d"
+#endif
+#ifndef _EXT_EXPORT
+#define _EXT_EXPORT             ".exports"
+#endif
 #ifndef _PATH_IDMAPDCONF
 #define _PATH_IDMAPDCONF	"/etc/idmapd.conf"
 #endif
@@ -163,6 +169,12 @@ void closeall(int min);
 int			svctcp_socket (u_long __number, int __reuse);
 int			svcudp_socket (u_long __number);
 
+/* Misc shared code prototypes */
+size_t  strlcat(char *, const char *, size_t);
+size_t  strlcpy(char *, const char *, size_t);
+ssize_t atomicio(ssize_t (*f) (int, void*, size_t),
+		 int, void *, size_t);
+
 
 #define UNUSED(x) UNUSED_ ## x __attribute__((unused))
 
diff --git a/support/include/rpcmisc.h b/support/include/rpcmisc.h
index c5847fa..0b06457 100644
--- a/support/include/rpcmisc.h
+++ b/support/include/rpcmisc.h
@@ -31,7 +31,7 @@ struct rpc_dentry {
 
 struct rpc_dtable {
 	struct rpc_dentry *entries;
-	int		nproc;
+	rpcproc_t		nproc;
 };
 
 #define dtable_ent(func, vers, arg_type, res_type) \
diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
index 60400b2..05c2fc4 100644
--- a/support/nfs/Makefile.am
+++ b/support/nfs/Makefile.am
@@ -5,7 +5,7 @@ libnfs_a_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
 		   xlog.c xcommon.c wildmat.c nfsclient.c \
 		   nfsexport.c getfh.c nfsctl.c rpc_socket.c getport.c \
 		   svc_socket.c cacheio.c closeall.c nfs_mntent.c conffile.c \
-		   svc_create.c
+		   svc_create.c atomicio.c strlcpy.c strlcat.c
 
 MAINTAINERCLEANFILES = Makefile.in
 
diff --git a/support/nfs/atomicio.c b/support/nfs/atomicio.c
new file mode 100644
index 0000000..5e760e6
--- /dev/null
+++ b/support/nfs/atomicio.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2002 Marius Aamodt Eriksen <marius@monkey.org>
+ * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t atomicio(ssize_t(*f) (int, void *, size_t), int fd, void *_s, size_t n)
+{
+	char *s = _s;
+	ssize_t res, pos = 0;
+
+	while ((ssize_t)n > pos) {
+		res = (f) (fd, s + pos, n - pos);
+		switch (res) {
+		case -1:
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+		case 0:
+			if (pos != 0)
+				return pos;
+			return res;
+		default:
+			pos += res;
+		}
+	}
+	return pos;
+}
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index 24640f4..798e5f3 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -271,9 +271,9 @@ conf_parse_line(int trans, char *line, size_t sz)
 		if (ptr == NULL)
 			return;
 		line = ++ptr;
-		while (*ptr && *ptr != '"')
+		while (*ptr && *ptr != '"' && *ptr != ']')
 			ptr++;
-		if (*ptr == '\0') {
+		if (*ptr == '\0' || *ptr == ']') {
 			xlog_warn("config file error: line %d: "
  				"non-matched '\"', ignoring until next section", ln);
 		}  else {
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index a93941c..1744ed6 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -332,6 +332,8 @@ dupexportent(struct exportent *dst, struct exportent *src)
 		dst->e_mountpoint = strdup(src->e_mountpoint);
 	if (src->e_fslocdata)
 		dst->e_fslocdata = strdup(src->e_fslocdata);
+	if (src->e_uuid)
+		dst->e_uuid = strdup(src->e_uuid);
 	dst->e_hostname = NULL;
 }
 
diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c
index 984c646..f7c27c9 100644
--- a/support/nfs/rpcdispatch.c
+++ b/support/nfs/rpcdispatch.c
@@ -32,7 +32,12 @@ rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp,
 		return;
 	}
 	dtable += (rqstp->rq_vers - 1);
-	if (((int)rqstp->rq_proc) > dtable->nproc) {
+	if (rqstp->rq_proc > dtable->nproc) {
+		svcerr_noproc(transp);
+		return;
+	}
+
+	if (dtable->nproc <= rqstp->rq_proc) {
 		svcerr_noproc(transp);
 		return;
 	}
diff --git a/support/nfs/strlcat.c b/support/nfs/strlcat.c
new file mode 100644
index 0000000..daedd7a
--- /dev/null
+++ b/support/nfs/strlcat.c
@@ -0,0 +1,76 @@
+/*	$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst,
+	const char *src,
+	size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
diff --git a/support/nfs/strlcpy.c b/support/nfs/strlcpy.c
new file mode 100644
index 0000000..a2653ee
--- /dev/null
+++ b/support/nfs/strlcpy.c
@@ -0,0 +1,72 @@
+/*	$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst,
+	const char *src,
+	size_t siz)
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
index 59ba505..b3f75ed 100644
--- a/support/nfs/svc_create.c
+++ b/support/nfs/svc_create.c
@@ -27,6 +27,7 @@
 #include <memory.h>
 #include <signal.h>
 #include <unistd.h>
+#include <errno.h>
 #include <netdb.h>
 
 #include <netinet/in.h>
@@ -41,11 +42,68 @@
 #include "tcpwrapper.h"
 #endif
 
+#include "sockaddr.h"
 #include "rpcmisc.h"
 #include "xlog.h"
 
 #ifdef HAVE_LIBTIRPC
 
+#define SVC_CREATE_XPRT_CACHE_SIZE	(8)
+static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
+
+/*
+ * Cache an SVC xprt, in case there are more programs or versions to
+ * register against it.
+ */
+static void
+svc_create_cache_xprt(SVCXPRT *xprt)
+{
+	unsigned int i;
+
+	/* Check if we've already got this one... */
+	for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
+		if (svc_create_xprt_cache[i] == xprt)
+			return;
+
+	/* No, we don't.  Cache it. */
+	for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
+		if (svc_create_xprt_cache[i] == NULL) {
+			svc_create_xprt_cache[i] = xprt;
+			return;
+		}
+
+	xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
+}
+
+/*
+ * Find a previously cached SVC xprt structure with the given bind address
+ * and transport semantics.
+ *
+ * Returns pointer to a cached SVC xprt.
+ *
+ * If no matching SVC XPRT can be found, NULL is returned.
+ */
+static SVCXPRT *
+svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
+{
+	unsigned int i;
+
+	for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
+		SVCXPRT *xprt = svc_create_xprt_cache[i];
+		struct sockaddr *sap;
+
+		if (xprt == NULL)
+			continue;
+		if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
+			continue;
+		sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
+		if (!nfs_compare_sockaddr(bindaddr, sap))
+			continue;
+		return xprt;
+	}
+	return NULL;
+}
+
 /*
  * Set up an appropriate bind address, given @port and @nconf.
  *
@@ -98,17 +156,113 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
 	return ai;
 }
 
+/*
+ * Create a listener socket on a specific bindaddr, and set
+ * special socket options to allow it to share the same port
+ * as other listeners.
+ *
+ * Returns an open, bound, and possibly listening network
+ * socket on success.
+ *
+ * Otherwise returns -1 if some error occurs.
+ */
+static int
+svc_create_sock(const struct sockaddr *sap, socklen_t salen,
+		struct netconfig *nconf)
+{
+	int fd, type, protocol;
+	int one = 1;
+
+	switch(nconf->nc_semantics) {
+	case NC_TPI_CLTS:
+		type = SOCK_DGRAM;
+		break;
+	case NC_TPI_COTS_ORD:
+		type = SOCK_STREAM;
+		break;
+	default:
+		xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u",
+			__func__, nconf->nc_semantics);
+		return -1;
+	}
+
+	if (strcmp(nconf->nc_proto, NC_UDP) == 0)
+		protocol = (int)IPPROTO_UDP;
+	else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
+		protocol = (int)IPPROTO_TCP;
+	else {
+		xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
+			__func__, nconf->nc_proto);
+		return -1;
+	}
+
+	fd = socket((int)sap->sa_family, type, protocol);
+	if (fd == -1) {
+		xlog(L_ERROR, "Could not make a socket: (%d) %m",
+			errno);
+		return -1;
+	}
+
+#ifdef IPV6_SUPPORTED
+	if (sap->sa_family == AF_INET6) {
+		if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
+				&one, sizeof(one)) == -1) {
+			xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m",
+				errno);
+			(void)close(fd);
+			return -1;
+		}
+	}
+#endif	/* IPV6_SUPPORTED */
+
+	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+		       &one, sizeof(one)) == -1) {
+		xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
+			errno);
+		(void)close(fd);
+		return -1;
+	}
+
+	if (bind(fd, sap, salen) == -1) {
+		xlog(L_ERROR, "Could not bind socket: (%d) %m",
+			errno);
+		(void)close(fd);
+		return -1;
+	}
+
+	if (nconf->nc_semantics == NC_TPI_COTS_ORD)
+		if (listen(fd, SOMAXCONN) == -1) {
+			xlog(L_ERROR, "Could not listen on socket: (%d) %m",
+				errno);
+			(void)close(fd);
+			return -1;
+		}
+
+	return fd;
+}
+
+/*
+ * The simple case is allowing the TI-RPC library to create a
+ * transport itself, given just the bind address and transport
+ * semantics.
+ *
+ * Our local xprt cache is ignored in this path, since the
+ * caller is not interested in sharing listeners or ports, and
+ * the library automatically avoids ports already in use.
+ *
+ * Returns the count of started listeners (one or zero).
+ */
 static unsigned int
-svc_create_nconf(const char *name, const rpcprog_t program,
+svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
 		const rpcvers_t version,
 		void (*dispatch)(struct svc_req *, SVCXPRT *),
-		const uint16_t port, struct netconfig *nconf)
+		struct netconfig *nconf)
 {
 	struct t_bind bindaddr;
 	struct addrinfo *ai;
 	SVCXPRT	*xprt;
 
-	ai = svc_create_bindaddr(nconf, port);
+	ai = svc_create_bindaddr(nconf, 0);
 	if (ai == NULL)
 		return 0;
 
@@ -119,7 +273,7 @@ svc_create_nconf(const char *name, const rpcprog_t program,
 	freeaddrinfo(ai);
 	if (xprt == NULL) {
 		xlog(D_GENERAL, "Failed to create listener xprt "
-				"(%s, %u, %s)", name, version, nconf->nc_netid);
+			"(%s, %u, %s)", name, version, nconf->nc_netid);
 		return 0;
 	}
 
@@ -133,6 +287,93 @@ svc_create_nconf(const char *name, const rpcprog_t program,
 	return 1;
 }
 
+/*
+ * If a port is specified on the command line, that port value will be
+ * the same for all listeners created here.  Create each listener
+ * socket in advance and set SO_REUSEADDR, rather than allowing the
+ * RPC library to create the listeners for us on a randomly chosen
+ * port via svc_tli_create(RPC_ANYFD).
+ *
+ * Some callers want to listen for more than one RPC version using the
+ * same port number.  For example, mountd could want to listen for MNT
+ * version 1, 2, and 3 requests.  This means mountd must use the same
+ * set of listener sockets for multiple RPC versions, since, on one
+ * system, you can't have two listener sockets with the exact same
+ * bind address (and port) and transport protocol.
+ *
+ * To accomplish this, this function caches xprts as they are created.
+ * This cache is checked to see if a previously created xprt can be
+ * used, before creating a new xprt for this [program, version].  If
+ * there is a cached xprt with the same bindaddr and transport
+ * semantics, we simply register the new version with that xprt,
+ * rather than creating a fresh xprt for it.
+ *
+ * The xprt cache implemented here is local to a process.  Two
+ * separate RPC daemons can not share a set of listeners.
+ *
+ * Returns the count of started listeners (one or zero).
+ */
+static unsigned int
+svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
+		const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port, struct netconfig *nconf)
+{
+	struct addrinfo *ai;
+	SVCXPRT	*xprt;
+
+	ai = svc_create_bindaddr(nconf, port);
+	if (ai == NULL)
+		return 0;
+
+	xprt = svc_create_find_xprt(ai->ai_addr, nconf);
+	if (xprt == NULL) {
+		int fd;
+
+		fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
+		if (fd == -1)
+			goto out_free;
+
+		xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
+		if (xprt == NULL) {
+			xlog(D_GENERAL, "Failed to create listener xprt "
+				"(%s, %u, %s)", name, version, nconf->nc_netid);
+			(void)close(fd);
+			goto out_free;
+		}
+	}
+
+	if (!svc_reg(xprt, program, version, dispatch, nconf)) {
+		/* svc_reg(3) destroys @xprt in this case */
+		xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
+				name, version, nconf->nc_netid);
+		goto out_free;
+	}
+
+	svc_create_cache_xprt(xprt);
+
+	freeaddrinfo(ai);
+	return 1;
+
+out_free:
+	freeaddrinfo(ai);
+	return 0;
+}
+
+static unsigned int
+svc_create_nconf(const char *name, const rpcprog_t program,
+		const rpcvers_t version,
+		void (*dispatch)(struct svc_req *, SVCXPRT *),
+		const uint16_t port, struct netconfig *nconf)
+{
+	if (port != 0)
+		return svc_create_nconf_fixed_port(name, program,
+			version, dispatch, port, nconf);
+
+	return svc_create_nconf_rand_port(name, program,
+			version, dispatch, nconf);
+}
+
 /**
  * nfs_svc_create - start up RPC svc listeners
  * @name: C string containing name of new service
@@ -145,8 +386,7 @@ svc_create_nconf(const char *name, const rpcprog_t program,
  * the RPC dispatcher.  Returns the number of started network transports.
  */
 unsigned int
-nfs_svc_create(__attribute__((unused)) char *name,
-		const rpcprog_t program, const rpcvers_t version,
+nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
 		void (*dispatch)(struct svc_req *, SVCXPRT *),
 		const uint16_t port)
 {
diff --git a/support/nsm/file.c b/support/nsm/file.c
index f4baeb9..98b47bf 100644
--- a/support/nsm/file.c
+++ b/support/nsm/file.c
@@ -126,7 +126,7 @@ exact_error_check(const ssize_t len, const size_t buflen)
  * containing an appropriate pathname, or NULL if an error
  * occurs.  Caller must free the returned result with free(3).
  */
-__attribute_malloc__
+__attribute__((__malloc__))
 static char *
 nsm_make_record_pathname(const char *directory, const char *hostname)
 {
@@ -174,7 +174,7 @@ nsm_make_record_pathname(const char *directory, const char *hostname)
  * containing an appropriate pathname, or NULL if an error
  * occurs.  Caller must free the returned result with free(3).
  */
-__attribute_malloc__
+__attribute__((__malloc__))
 static char *
 nsm_make_pathname(const char *directory)
 {
@@ -204,7 +204,7 @@ nsm_make_pathname(const char *directory)
  * containing an appropriate pathname, or NULL if an error
  * occurs.  Caller must free the returned result with free(3).
  */
-__attribute_malloc__
+__attribute__((__malloc__))
 static char *
 nsm_make_temp_pathname(const char *pathname)
 {
@@ -421,7 +421,7 @@ nsm_drop_privileges(const int pidfd)
 	 */
         if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
                 xlog(L_ERROR, "prctl(PR_SET_KEEPCAPS) failed: %m");
-		return 0;
+		return false;
 	}
 
 	if (setgroups(0, NULL) == -1) {
@@ -568,9 +568,8 @@ nsm_retire_monitored_hosts(void)
 
 	while ((de = readdir(dir)) != NULL) {
 		char *src, *dst;
+		struct stat stb;
 
-		if (de->d_type != (unsigned char)DT_REG)
-			continue;
 		if (de->d_name[0] == '.')
 			continue;
 
@@ -580,6 +579,20 @@ nsm_retire_monitored_hosts(void)
 			continue;
 		}
 
+		/* NB: not all file systems fill in d_type correctly */
+		if (lstat(src, &stb) == -1) {
+			xlog_warn("Bad monitor file %s, skipping: %m",
+					de->d_name);
+			free(src);
+			continue;
+		}
+		if (!S_ISREG(stb.st_mode)) {
+			xlog(D_GENERAL, "Skipping non-regular file %s",
+					de->d_name);
+			free(src);
+			continue;
+		}
+
 		dst = nsm_make_record_pathname(NSM_NOTIFY_DIR, de->d_name);
 		if (dst == NULL) {
 			free(src);
@@ -634,7 +647,7 @@ nsm_priv_to_hex(const char *priv, char *buf, const size_t buflen)
 /*
  * Returns the length in bytes of the created record.
  */
-__attribute_noinline__
+__attribute__((__noinline__))
 static size_t
 nsm_create_monitor_record(char *buf, const size_t buflen,
 		const struct sockaddr *sap, const struct mon *m)
@@ -784,7 +797,7 @@ out:
 	return result;
 }
 
-__attribute_noinline__
+__attribute__((__noinline__))
 static _Bool
 nsm_parse_line(char *line, struct sockaddr_in *sin, struct mon *m)
 {
@@ -846,7 +859,7 @@ nsm_read_line(const char *hostname, const time_t timestamp, char *line,
 }
 
 /*
- * Given a filename, reads data from a file under NSM_MONITOR_DIR
+ * Given a filename, reads data from a file under "directory"
  * and invokes @func so caller can populate their in-core
  * database with this data.
  */
@@ -863,10 +876,15 @@ nsm_load_host(const char *directory, const char *filename, nsm_populate_t func)
 	if (path == NULL)
 		goto out_err;
 
-	if (stat(path, &stb) == -1) {
+	if (lstat(path, &stb) == -1) {
 		xlog(L_ERROR, "Failed to stat %s: %m", path);
 		goto out_freepath;
 	}
+	if (!S_ISREG(stb.st_mode)) {
+		xlog(D_GENERAL, "Skipping non-regular file %s",
+				path);
+		goto out_freepath;
+	}
 
 	f = fopen(path, "r");
 	if (f == NULL) {
@@ -913,8 +931,6 @@ nsm_load_dir(const char *directory, nsm_populate_t func)
 	}
 
 	while ((de = readdir(dir)) != NULL) {
-		if (de->d_type != (unsigned char)DT_REG)
-			continue;
 		if (de->d_name[0] == '.')
 			continue;
 
diff --git a/tests/nsm_client/nsm_client.c b/tests/nsm_client/nsm_client.c
index 0d1159a..0fa3422 100644
--- a/tests/nsm_client/nsm_client.c
+++ b/tests/nsm_client/nsm_client.c
@@ -205,7 +205,7 @@ nsm_client_get_rpcclient(const char *node)
 {
 	unsigned short		port;
 	struct addrinfo		*ai;
-	struct addrinfo		hints = { .ai_flags	= AI_ADDRCONFIG };
+	struct addrinfo		hints = { };
 	int			err;
 	CLIENT			*client = NULL;
 
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 8665183..a0ea116 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -4,6 +4,9 @@ OPTDIRS =
 
 if CONFIG_NFSV4
 OPTDIRS += idmapd
+if CONFIG_NFSIDMAP
+OPTDIRS += nfsidmap
+endif
 endif
 
 if CONFIG_GSS
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index b78957f..26d0504 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -25,6 +25,7 @@
 #include <fcntl.h>
 #include <netdb.h>
 #include <errno.h>
+#include <dirent.h>
 
 #include "sockaddr.h"
 #include "misc.h"
@@ -41,6 +42,7 @@ static void	error(nfs_export *exp, int err);
 static void	usage(const char *progname);
 static void	validate_export(nfs_export *exp);
 static int	matchhostname(const char *hostname1, const char *hostname2);
+static void	export_d_read(const char *dname);
 
 int
 main(int argc, char **argv)
@@ -127,8 +129,10 @@ main(int argc, char **argv)
 			return 0;
 		}
 	}
-	if (f_export && ! f_ignore)
+	if (f_export && ! f_ignore) {
 		export_read(_PATH_EXPORTS);
+		export_d_read(_PATH_EXPORTS_D);
+	}
 	if (f_export) {
 		if (f_all)
 			export_all(f_verbose);
@@ -485,6 +489,59 @@ out:
 	return result;
 }
 
+/* Based on mnt_table_parse_dir() in
+   util-linux-ng/shlibs/mount/src/tab_parse.c */
+static void
+export_d_read(const char *dname)
+{
+	int n = 0, i;
+	struct dirent **namelist = NULL;
+
+
+	n = scandir(dname, &namelist, NULL, versionsort);
+	if (n < 0)
+		xlog(L_NOTICE, "scandir %s: %s\n", dname, strerror(errno));
+	else if (n == 0)
+		return;
+
+	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\n", d->d_name, dname);
+			continue;
+		}
+
+		export_read(fname);
+	}
+		
+	for (i = 0; i < n; i++)
+		free(namelist[i]);
+	free(namelist);
+
+	return;
+}
+
 static char
 dumpopt(char c, char *fmt, ...)
 {
diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
index 089f75b..364f247 100644
--- a/utils/exportfs/exportfs.man
+++ b/utils/exportfs/exportfs.man
@@ -37,11 +37,15 @@ when a client sends an NFS MOUNT request.
 .PP
 Normally the master export table is initialized with the contents of
 .I /etc/exports
+and files under
+.I /etc/exports.d
 by invoking
 .BR "exportfs -a" .
 However, a system administrator can choose to add or delete
 exports without modifying
 .I /etc/exports
+or files under
+.I /etc/exports.d
 by using the
 .B exportfs
 command.
@@ -92,17 +96,24 @@ Specify a list of export options in the same manner as in
 .B -i
 Ignore the
 .I /etc/exports
-file.  Only default options and options given on the command line are used.
+file and files under
+.I /etc/exports.d
+directory.  Only default options and options given on the command line are used.
 .TP
 .B -r
 Reexport all directories, synchronizing
 .I /var/lib/nfs/etab
 with
-.IR /etc/exports .
+.IR /etc/exports 
+and files under 
+.IR /etc/exports.d .
 This option removes entries in
 .I /var/lib/nfs/etab
 which have been deleted from
-.I /etc/exports, and removes any entries from the
+.I /etc/exports
+or files under
+.IR /etc/exports.d , 
+and removes any entries from the
 kernel export table which are no longer valid.
 .TP
 .B -u
@@ -130,6 +141,8 @@ when adding new entries to the export table.  When using
 .BR "exportfs -a" ,
 all exports listed in
 .I /etc/exports
+and files under
+.I /etc/exports.d
 are added to
 .IR /var/lib/nfs/etab .
 The kernel's export table is also updated as needed.
@@ -149,7 +162,9 @@ several sources.
 The default export options are
 .BR sync,ro,root_squash,wdelay .
 These can be overridden by entries in
-.IR /etc/exports .
+.IR /etc/exports 
+or files under
+.IR /etc/exports.d .
 .PP
 A system administrator may override options from these sources using the
 .B -o
@@ -188,6 +203,8 @@ to display the export options for each export.
 .SH EXAMPLES
 The following adds all directories listed in
 .I /etc/exports
+and files under
+.I /etc/exports.d
 to
 .I /var/lib/nfs/etab
 and pushes the resulting export entries into the kernel:
@@ -215,7 +232,9 @@ directory:
 .fi
 .PP
 To unexport all exports listed in
-.IR /etc/exports :
+.IR /etc/exports 
+and files under
+.IR /etc/exports.d :
 .PP
 .nf
 .B "# exportfs -au
@@ -238,6 +257,13 @@ if they themselves are no longer valid they will be removed.
 .I /etc/exports
 input file listing exports, export options, and access control lists
 .TP 2.5i
+.I /etc/exports.d
+directory where extra input files are stored.
+.B Note:
+only files that end with 
+.I .exports
+are used.
+.TP 2.5i
 .I /var/lib/nfs/etab
 master table of exports
 .TP 2.5i
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index c726dd9..85e25d4 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -145,7 +145,9 @@ storage (see
 .IR async
 above).
 
-In releases of nfs-utils up to and including 1.0.0, this option was the
+In releases of nfs-utils up to and including 1.0.0, the
+.I async 
+option was the
 default.  In all releases after 1.0.0,
 .I sync
 is the default, and
@@ -375,20 +377,6 @@ If the client asks for alternative locations for the export point, it
 will be given this list of alternatives. (Note that actual replication
 of the filesystem must be handled elsewhere.)
 
-.TP
-.IR refer= path@host[+host][:path@host[+host]]
-A client referencing the export point will be directed to choose from
-the given list an alternative location for the filesystem.
-(Note that the server must have a mountpoint here, though a different
-filesystem is not required; so, for example,
-.IR "mount --bind" " /path /path"
-is sufficient.)
-.TP
-.IR replicas= path@host[+host][:path@host[+host]]
-If the client asks for alternative locations for the export point, it
-will be given this list of alternatives. (Note that actual replication
-of the filesystem must be handled elsewhere.)
-
 .SS User ID Mapping
 .PP
 .B nfsd
@@ -456,6 +444,24 @@ export entry for
 .B /home/joe
 in the example section below, which maps all requests to uid 150 (which
 is supposedly that of user joe).
+.SS Extra Export Tables
+After reading 
+.I /etc/exports 
+.B exportfs
+reads files under 
+.I /etc/exports.d. 
+directory as extra export tables.
+.B exportfs
+regards only a file which name is ended with 
+.I .exports
+and
+not started with 
+.I . 
+as an extra export file. A file which name
+is not met this condition is just ignored.
+The format for extra export tables is the same as 
+.I /etc/exports
+.
 .IP
 .SH EXAMPLE
 .PP
@@ -501,6 +507,7 @@ all three mounts with the `sync' option enabled.
 '''entry.
 .SH FILES
 /etc/exports
+/etc/exports.d
 .SH SEE ALSO
 .BR exportfs (8),
 .BR netgroup (5),
diff --git a/utils/gssd/gss_util.c b/utils/gssd/gss_util.c
index 8fe1e9b..ee304cc 100644
--- a/utils/gssd/gss_util.c
+++ b/utils/gssd/gss_util.c
@@ -138,6 +138,83 @@ display_status_1(char *m, u_int32_t code, int type, const gss_OID mech)
 	}
 }
 #endif
+static char *
+gss_display_error(OM_uint32 status)
+{
+		char *error = NULL;
+
+		switch(status) {
+		case GSS_S_COMPLETE: 
+			error = "GSS_S_COMPLETE";
+			break;
+		case GSS_S_CALL_INACCESSIBLE_READ: 
+			error = "GSS_S_CALL_INACCESSIBLE_READ";
+			break;
+		case GSS_S_CALL_INACCESSIBLE_WRITE:
+			error = "GSS_S_CALL_INACCESSIBLE_WRITE";
+			break;
+		case GSS_S_CALL_BAD_STRUCTURE:
+			error = "GSS_S_CALL_BAD_STRUCTURE";
+			break;
+		case  GSS_S_BAD_MECH:
+			error = "GSS_S_BAD_MECH";
+			break;
+		case  GSS_S_BAD_NAME:
+			error = "GSS_S_BAD_NAME";
+			break;
+		case  GSS_S_BAD_NAMETYPE:
+			error = "GSS_S_BAD_NAMETYPE";
+			break;
+		case  GSS_S_BAD_BINDINGS:
+			error = "GSS_S_BAD_BINDINGS";
+			break;
+		case  GSS_S_BAD_STATUS:
+			error = "GSS_S_BAD_STATUS";
+			break;
+		case  GSS_S_BAD_SIG:
+			error = "GSS_S_BAD_SIG";
+			break;
+		case  GSS_S_NO_CRED:
+			error = "GSS_S_NO_CRED";
+			break;
+		case  GSS_S_NO_CONTEXT:
+			error = "GSS_S_NO_CONTEXT";
+			break;
+		case  GSS_S_DEFECTIVE_TOKEN:
+			error = "GSS_S_DEFECTIVE_TOKEN";
+			break;
+		case  GSS_S_DEFECTIVE_CREDENTIAL:
+			error = "GSS_S_DEFECTIVE_CREDENTIAL";
+			break;
+		case  GSS_S_CREDENTIALS_EXPIRED:
+			error = "GSS_S_CREDENTIALS_EXPIRED";
+			break;
+		case  GSS_S_CONTEXT_EXPIRED:
+			error = "GSS_S_CONTEXT_EXPIRED";
+			break;
+		case  GSS_S_FAILURE:
+			error = "GSS_S_FAILURE";
+			break;
+		case  GSS_S_BAD_QOP:
+			error = "GSS_S_BAD_QOP";
+			break;
+		case  GSS_S_UNAUTHORIZED:
+			error = "GSS_S_UNAUTHORIZED";
+			break;
+		case  GSS_S_UNAVAILABLE:
+			error = "GSS_S_UNAVAILABLE";
+			break;
+		case  GSS_S_DUPLICATE_ELEMENT:
+			error = "GSS_S_DUPLICATE_ELEMENT";
+			break;
+		case  GSS_S_NAME_NOT_MN:
+			error = "GSS_S_NAME_NOT_MN";
+			break;
+		default:
+			error = "Not defined";
+		}
+	return error;
+}
 
 static void
 display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
@@ -175,8 +252,8 @@ display_status_2(char *m, u_int32_t major, u_int32_t minor, const gss_OID mech)
 
 	if (major == GSS_S_CREDENTIALS_EXPIRED)
 		msg_verbosity = 1;
-	printerr(msg_verbosity, "ERROR: GSS-API: error in %s(): %s - %s\n",
-		 m, maj, min);
+	printerr(msg_verbosity, "ERROR: GSS-API: error in %s(): %s (%s) - %s(%s)\n",
+		 m, gss_display_error(major), maj, min);
 
 	if (maj_gss_buf.length != 0)
 		(void) gss_release_buffer(&min_stat1, &maj_gss_buf);
diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man
index 0a23cd6..073379d 100644
--- a/utils/gssd/gssd.man
+++ b/utils/gssd/gssd.man
@@ -53,6 +53,8 @@ To be more consistent with other implementations, we now look for
 specific keytab entries.  The search order for keytabs to be used
 for "machine credentials" is now:
 .br
+  <HOSTNAME>$@<REALM>
+.br
   root/<hostname>@<REALM>
 .br
   nfs/<hostname>@<REALM>
@@ -64,6 +66,9 @@ for "machine credentials" is now:
   nfs/<anyname>@<REALM>
 .br
   host/<anyname>@<REALM>
+.IP
+If this search order does not use the correct key then provide a
+keytab file that contains only correct keys.
 .TP
 .B -p path
 Tells
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index c301d46..41328c9 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -1245,7 +1245,7 @@ handle_gssd_upcall(struct clnt_info *clp)
 			goto out;
 		if (sscanf(p, "enctypes=%s", enctypes) != 1) {
 			printerr(0, "WARNING: handle_gssd_upcall: "
-				    "failed to parse target name "
+				    "failed to parse encryption types "
 				    "in upcall string '%s'\n", lbuf);
 			goto out;
 		}
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index f071600..4b13fa1 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -768,6 +768,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
 	krb5_error_code code;
 	char **realmnames = NULL;
 	char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST];
+	char myhostad[NI_MAXHOST+1];
 	int i, j, retval;
 	char *default_realm = NULL;
 	char *realm;
@@ -789,6 +790,14 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
 		printerr(1, "%s while getting local hostname\n", k5err);
 		goto out;
 	}
+
+	/* Compute the active directory machine name HOST$ */
+	strcpy(myhostad, myhostname);
+	for (i = 0; myhostad[i] != 0; ++i)
+		myhostad[i] = toupper(myhostad[i]);
+	myhostad[i] = '$';
+	myhostad[i+1] = 0;
+
 	retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
 	if (retval)
 		goto out;
@@ -833,32 +842,47 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
 		if (strcmp(realm, default_realm) == 0)
 			tried_default = 1;
 		for (j = 0; svcnames[j] != NULL; j++) {
-			code = krb5_build_principal_ext(context, &princ,
-							strlen(realm),
-							realm,
-							strlen(svcnames[j]),
-							svcnames[j],
-							strlen(myhostname),
-							myhostname,
-							NULL);
+			char spn[300];
+
+			/*
+			 * The special svcname "$" means 'try the active
+			 * directory machine account'
+			 */
+			if (strcmp(svcnames[j],"$") == 0) {
+				snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm);
+				code = krb5_build_principal_ext(context, &princ,
+								strlen(realm),
+								realm,
+								strlen(myhostad),
+								myhostad,
+								NULL);
+			} else {
+				snprintf(spn, sizeof(spn), "%s/%s@%s",
+					 svcnames[j], myhostname, realm);
+				code = krb5_build_principal_ext(context, &princ,
+								strlen(realm),
+								realm,
+								strlen(svcnames[j]),
+								svcnames[j],
+								strlen(myhostname),
+								myhostname,
+								NULL);
+			}
+
 			if (code) {
 				k5err = gssd_k5_err_msg(context, code);
-				printerr(1, "%s while building principal for "
-					 "'%s/%s@%s'\n", k5err, svcnames[j],
-					 myhostname, realm);
+				printerr(1, "%s while building principal for '%s'\n",
+					 k5err, spn);
 				continue;
 			}
 			code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte);
 			krb5_free_principal(context, princ);
 			if (code) {
 				k5err = gssd_k5_err_msg(context, code);
-				printerr(3, "%s while getting keytab entry for "
-					 "'%s/%s@%s'\n", k5err, svcnames[j],
-					 myhostname, realm);
+				printerr(3, "%s while getting keytab entry for '%s'\n",
+					 k5err, spn);
 			} else {
-				printerr(3, "Success getting keytab entry for "
-					 "'%s/%s@%s'\n",
-					 svcnames[j], myhostname, realm);
+				printerr(3, "Success getting keytab entry for '%s'\n",spn);
 				retval = 0;
 				goto out;
 			}
@@ -870,6 +894,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname,
 		 */
 		for (j = 0; svcnames[j] != NULL; j++) {
 			int found = 0;
+			if (strcmp(svcnames[j],"$") == 0)
+				continue;
 			code = gssd_search_krb5_keytab(context, kt, realm,
 						       svcnames[j], &found, kte);
 			if (!code && found) {
@@ -1160,7 +1186,7 @@ gssd_refresh_krb5_machine_credential(char *hostname,
 	krb5_keytab kt = NULL;;
 	int retval = 0;
 	char *k5err = NULL;
-	const char *svcnames[4] = { "root", "nfs", "host", NULL };
+	const char *svcnames[5] = { "$", "root", "nfs", "host", NULL };
 
 	/*
 	 * If a specific service name was specified, use it.
diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c
index 9b463f3..17af2da 100644
--- a/utils/gssd/svcgssd.c
+++ b/utils/gssd/svcgssd.c
@@ -267,6 +267,7 @@ main(int argc, char *argv[])
 	if (!fg)
 		release_parent();
 
+	nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
 	gssd_run();
 	printerr(0, "gssd_run returned!\n");
 	abort();
diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
index 3894078..0ecbab6 100644
--- a/utils/gssd/svcgssd_proc.c
+++ b/utils/gssd/svcgssd_proc.c
@@ -241,7 +241,7 @@ get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred)
 			"file for name '%s'\n", sname);
 		goto out_free;
 	}
-	nfs4_init_name_mapping(NULL); /* XXX: should only do this once */
+
 	res = nfs4_gss_princ_to_ids(secname, sname, &uid, &gid);
 	if (res < 0) {
 		/*
diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am
index 4218048..4328e41 100644
--- a/utils/idmapd/Makefile.am
+++ b/utils/idmapd/Makefile.am
@@ -11,12 +11,8 @@ EXTRA_DIST = \
 	idmapd.conf
 
 idmapd_SOURCES = \
-	atomicio.c \
 	idmapd.c \
-	strlcat.c \
-	strlcpy.c \
 	\
-	cfg.h \
 	nfs_idmap.h \
 	queue.h
 
diff --git a/utils/idmapd/atomicio.c b/utils/idmapd/atomicio.c
deleted file mode 100644
index 1fb1ff9..0000000
--- a/utils/idmapd/atomicio.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2002 Marius Aamodt Eriksen <marius@monkey.org>
- * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
- * All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <errno.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-/*
- * ensure all of data on socket comes through. f==read || f==write
- */
-ssize_t
-atomicio(
-	ssize_t (*f) (int, void*, size_t),
-	int fd,
-	void *_s,
-	size_t n)
-{
-	char *s = _s;
-	ssize_t res;
-	size_t pos = 0;
-
-	while (n > pos) {
-		res = (f) (fd, s + pos, n - pos);
-		switch (res) {
-		case -1:
-			if (errno == EINTR || errno == EAGAIN)
-				continue;
-		case 0:
-			if (pos != 0)
-				return (pos);
-			return (res);
-		default:
-			pos += res;
-		}
-	}
-	return (pos);
-}
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
index b76607a..76a56ef 100644
--- a/utils/idmapd/idmapd.c
+++ b/utils/idmapd/idmapd.c
@@ -158,10 +158,6 @@ static int nfsdopenone(struct idmap_client *);
 static void nfsdreopen_one(struct idmap_client *);
 static void nfsdreopen(void);
 
-size_t  strlcat(char *, const char *, size_t);
-size_t  strlcpy(char *, const char *, size_t);
-ssize_t atomicio(ssize_t (*f) (int, void*, size_t),
-		 int, void *, size_t);
 void    mydaemon(int, int);
 void    release_parent(void);
 
diff --git a/utils/idmapd/strlcat.c b/utils/idmapd/strlcat.c
deleted file mode 100644
index daedd7a..0000000
--- a/utils/idmapd/strlcat.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*	$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $	*/
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
- *
- * 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. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char *rcsid = "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $";
-#endif /* LIBC_SCCS and not lint */
-
-#include <sys/types.h>
-#include <string.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-/*
- * Appends src to string dst of size siz (unlike strncat, siz is the
- * full size of dst, not space left).  At most siz-1 characters
- * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
- * Returns strlen(src) + MIN(siz, strlen(initial dst)).
- * If retval >= siz, truncation occurred.
- */
-size_t
-strlcat(char *dst,
-	const char *src,
-	size_t siz)
-{
-	register char *d = dst;
-	register const char *s = src;
-	register size_t n = siz;
-	size_t dlen;
-
-	/* Find the end of dst and adjust bytes left but don't go past end */
-	while (n-- != 0 && *d != '\0')
-		d++;
-	dlen = d - dst;
-	n = siz - dlen;
-
-	if (n == 0)
-		return(dlen + strlen(s));
-	while (*s != '\0') {
-		if (n != 1) {
-			*d++ = *s;
-			n--;
-		}
-		s++;
-	}
-	*d = '\0';
-
-	return(dlen + (s - src));	/* count does not include NUL */
-}
diff --git a/utils/idmapd/strlcpy.c b/utils/idmapd/strlcpy.c
deleted file mode 100644
index a2653ee..0000000
--- a/utils/idmapd/strlcpy.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*	$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $	*/
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- * All rights reserved.
- *
- * 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. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char *rcsid = "$OpenBSD: strlcpy.c,v 1.5 2001/05/13 15:40:16 deraadt Exp $";
-#endif /* LIBC_SCCS and not lint */
-
-#include <sys/types.h>
-#include <string.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-/*
- * Copy src to string dst of size siz.  At most siz-1 characters
- * will be copied.  Always NUL terminates (unless siz == 0).
- * Returns strlen(src); if retval >= siz, truncation occurred.
- */
-size_t
-strlcpy(char *dst,
-	const char *src,
-	size_t siz)
-{
-	register char *d = dst;
-	register const char *s = src;
-	register size_t n = siz;
-
-	/* Copy as many bytes as will fit */
-	if (n != 0 && --n != 0) {
-		do {
-			if ((*d++ = *s++) == 0)
-				break;
-		} while (--n != 0);
-	}
-
-	/* Not enough room in dst, add NUL and traverse rest of src */
-	if (n == 0) {
-		if (siz != 0)
-			*d = '\0';		/* NUL-terminate dst */
-		while (*s++)
-			;
-	}
-
-	return(s - src - 1);	/* count does not include NUL */
-}
diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c
index 051fa38..a742e64 100644
--- a/utils/mount/fstab.c
+++ b/utils/mount/fstab.c
@@ -364,19 +364,22 @@ lock_mtab (void) {
 	/* Repeat until it was us who made the link */
 	while (!we_created_lockfile) {
 		struct flock flock;
-		int errsv, j;
+		int j;
 
 		j = link(linktargetfile, MOUNTED_LOCK);
-		errsv = errno;
 
-		if (j == 0)
-			we_created_lockfile = 1;
+		{
+			int errsv = errno;
 
-		if (j < 0 && errsv != EEXIST) {
-			(void) unlink(linktargetfile);
-			die (EX_FILEIO, _("can't link lock file %s: %s "
-			     "(use -n flag to override)"),
-			     MOUNTED_LOCK, strerror (errsv));
+			if (j == 0)
+				we_created_lockfile = 1;
+
+			if (j < 0 && errsv != EEXIST) {
+				(void) unlink(linktargetfile);
+				die (EX_FILEIO, _("can't link lock file %s: %s "
+				     "(use -n flag to override)"),
+				     MOUNTED_LOCK, strerror (errsv));
+			}
 		}
 
 		lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
@@ -414,7 +417,7 @@ lock_mtab (void) {
 			}
 			(void) unlink(linktargetfile);
 		} else {
-			static int tries = 0;
+			static int retries = 0;
 
 			/* Someone else made the link. Wait. */
 			alarm(LOCK_TIMEOUT);
@@ -428,10 +431,10 @@ lock_mtab (void) {
 			alarm(0);
 			/* Limit the number of iterations - maybe there
 			   still is some old /etc/mtab~ */
-			++tries;
-			if (tries % 200 == 0)
+			++retries;
+			if (retries % 200 == 0)
 			   usleep(30);
-			if (tries > 100000) {
+			if (retries > 100000) {
 				(void) unlink(linktargetfile);
 				close(lockfile_fd);
 				die (EX_FILEIO, _("Cannot create link %s\n"
diff --git a/utils/mount/mount.c b/utils/mount/mount.c
index 82b9169..a19af53 100644
--- a/utils/mount/mount.c
+++ b/utils/mount/mount.c
@@ -209,7 +209,7 @@ static char *fix_opts_string(int flags, const char *extra_opts)
 	}
 	if (flags & MS_USERS)
 		new_opts = xstrconcat3(new_opts, ",users", "");
-	
+
 	for (om = opt_map; om->opt != NULL; om++) {
 		if (om->skip)
 			continue;
@@ -224,6 +224,20 @@ static char *fix_opts_string(int flags, const char *extra_opts)
 	return new_opts;
 }
 
+static void
+init_mntent(struct mntent *mnt, char *fsname, char *dir, char *type,
+		int flags, char *opts)
+{
+	mnt->mnt_fsname	= fsname;
+	mnt->mnt_dir	= dir;
+	mnt->mnt_type	= type;
+	mnt->mnt_opts	= fix_opts_string(flags & ~MS_NOMTAB, opts);
+
+	/* these are always zero for NFS */
+	mnt->mnt_freq	= 0;
+	mnt->mnt_passno	= 0;
+}
+
 /* Create mtab with a root entry.  */
 static void
 create_mtab (void) {
@@ -245,11 +259,8 @@ create_mtab (void) {
 	if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
 		char *extra_opts;
 		parse_opts (fstab->m.mnt_opts, &flags, &extra_opts);
-		mnt.mnt_dir = "/";
-		mnt.mnt_fsname = xstrdup(fstab->m.mnt_fsname);
-		mnt.mnt_type = fstab->m.mnt_type;
-		mnt.mnt_opts = fix_opts_string (flags, extra_opts);
-		mnt.mnt_freq = mnt.mnt_passno = 0;
+		init_mntent(&mnt, xstrdup(fstab->m.mnt_fsname), "/",
+				fstab->m.mnt_type, flags, extra_opts);
 		free(extra_opts);
 
 		if (nfs_addmntent (mfp, &mnt) == 1) {
@@ -273,17 +284,12 @@ create_mtab (void) {
 }
 
 static int add_mtab(char *spec, char *mount_point, char *fstype,
-			int flags, char *opts, int freq, int pass)
+			int flags, char *opts)
 {
 	struct mntent ment;
 	int result = EX_SUCCESS;
 
-	ment.mnt_fsname = spec;
-	ment.mnt_dir = mount_point;
-	ment.mnt_type = fstype;
-	ment.mnt_opts = fix_opts_string(flags, opts);
-	ment.mnt_freq = freq;
-	ment.mnt_passno = pass;
+	init_mntent(&ment, spec, mount_point, fstype, flags, opts);
 
 	if (!nomtab && mtab_does_not_exist()) {
 		if (verbose > 1)
@@ -321,7 +327,7 @@ static int add_mtab(char *spec, char *mount_point, char *fstype,
 	return result;
 }
 
-void mount_usage(void)
+static void mount_usage(void)
 {
 	printf(_("usage: %s remotetarget dir [-rvVwfnsih] [-o nfsoptions]\n"),
 		progname);
@@ -337,7 +343,7 @@ void mount_usage(void)
 	printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n"));
 }
 
-static void parse_opt(const char *opt, int *mask, char *extra_opts, int len)
+static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
 {
 	const struct opt_map *om;
 
@@ -371,7 +377,7 @@ static void parse_opts(const char *options, int *flags, char **extra_opts)
 	if (options != NULL) {
 		char *opts = xstrdup(options);
 		char *opt, *p;
-		int len = strlen(opts) + 1;	/* include room for a null */
+		size_t len = strlen(opts) + 1;	/* include room for a null */
 		int open_quote = 0;
 
 		*extra_opts = xmalloc(len);
@@ -441,9 +447,7 @@ static int try_mount(char *spec, char *mount_point, int flags,
 	if (!fake)
 		print_one(spec, mount_point, fs_type, mount_opts);
 
-	ret = add_mtab(spec, mount_point, fs_type, flags, *extra_opts,
-			0, 0 /* these are always zero for NFS */ );
-	return ret;
+	return add_mtab(spec, mount_point, fs_type, flags, *extra_opts);
 }
 
 int main(int argc, char *argv[])
diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h
index 3023306..69ffd1e 100644
--- a/utils/mount/mount_config.h
+++ b/utils/mount/mount_config.h
@@ -1,7 +1,7 @@
-#ifndef _LINUX_MOUNT__CONFIG_H
-#define _LINUX_MOUNT_CONFIG__H
+#ifndef _LINUX_MOUNT_CONFIG_H
+#define _LINUX_MOUNT_CONFIG_H
 /*
- * mount_config.h -- mount configuration file routines 
+ * mount_config.h -- mount configuration file routines
  * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -16,15 +16,13 @@
  *
  */
 
-inline void mount_config_init(char *);
-
 #ifdef MOUNT_CONFIG
 #include "conffile.h"
 #include "xlog.h"
 
 extern char *conf_get_mntopts(char *, char *, char *);
 
-inline void mount_config_init(char *program)
+static inline void mount_config_init(char *program)
 {
 	xlog_open(program);
 	/*
@@ -32,19 +30,22 @@ inline void mount_config_init(char *program)
 	 */
 	conf_init();
 }
-inline char *mount_config_opts(char *spec, 
+
+static inline char *mount_config_opts(char *spec,
 		char *mount_point, char *mount_opts)
 {
 	return conf_get_mntopts(spec, mount_point, mount_opts);
 }
+
 #else /* MOUNT_CONFIG */
 
-inline void mount_config_init(char *program) { }
+static inline void mount_config_init(__attribute__ ((unused)) char *program) { }
 
-inline char *mount_config_opts(char *spec, 
-		char *mount_point, char *mount_opts)
+static inline char *mount_config_opts(__attribute__ ((unused)) char *spec,
+		__attribute__ ((unused)) char *mount_point, char *mount_opts)
 {
 	return mount_opts;
 }
 #endif /* MOUNT_CONFIG */
-#endif
+
+#endif	/* _LINUX_MOUNT_CONFIG_H */
diff --git a/utils/mount/mount_constants.h b/utils/mount/mount_constants.h
index cbfb099..4d050d8 100644
--- a/utils/mount/mount_constants.h
+++ b/utils/mount/mount_constants.h
@@ -64,4 +64,8 @@ if we have a stack or plain mount - mount atop of it, forming a stack. */
 #define MS_MGC_MSK 0xffff0000	/* magic flag number mask */
 #endif
 
+/* Generic options that are prevented from appearing
+ * in the options field in /etc/mtab. */
+#define MS_NOMTAB	(MS_REMOUNT)
+
 #endif	/* _NFS_UTILS_MOUNT_CONSTANTS_H */
diff --git a/utils/mount/network.c b/utils/mount/network.c
index d612427..9b6504d 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -59,6 +59,8 @@
 #define CONNECT_TIMEOUT	(20)
 #define MOUNT_TIMEOUT	(30)
 
+#define SAFE_SOCKADDR(x)	(struct sockaddr *)(char *)(x)
+
 extern int nfs_mount_data_version;
 extern char *progname;
 extern int verbose;
@@ -208,9 +210,6 @@ int nfs_lookup(const char *hostname, const sa_family_t family,
 {
 	struct addrinfo *gai_results;
 	struct addrinfo gai_hint = {
-#ifdef HAVE_DECL_AI_ADDRCONFIG
-		.ai_flags	= AI_ADDRCONFIG,
-#endif	/* HAVE_DECL_AI_ADDRCONFIG */
 		.ai_family	= family,
 	};
 	socklen_t len = *salen;
@@ -428,12 +427,12 @@ static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
 		if (bindresvport(so, &laddr) < 0)
 			goto err_bindresvport;
 	} else {
-		cc = bind(so, (struct sockaddr *)&laddr, namelen);
+		cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
 		if (cc < 0)
 			goto err_bind;
 	}
 	if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
-		cc = connect_to(so, (struct sockaddr *)saddr, namelen,
+		cc = connect_to(so, SAFE_SOCKADDR(saddr), namelen,
 				timeout);
 		if (cc < 0)
 			goto err_connect;
@@ -756,11 +755,12 @@ int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
  */
 int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
 {
-	return nfs_probe_bothports((struct sockaddr *)&mnt_server->saddr,
-					sizeof(mnt_server->saddr),
+	struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr);
+	struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr);
+
+	return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr),
 					&mnt_server->pmap,
-					(struct sockaddr *)&nfs_server->saddr,
-					sizeof(nfs_server->saddr),
+					nfs_addr, sizeof(nfs_server->saddr),
 					&nfs_server->pmap);
 }
 
@@ -772,7 +772,7 @@ static int nfs_probe_statd(void)
 	};
 	rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
 
-	return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr),
+	return nfs_getport_ping(SAFE_SOCKADDR(&addr), sizeof(addr),
 				program, (rpcvers_t)1, IPPROTO_UDP);
 }
 
@@ -901,7 +901,7 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
  */
 int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
 {
-	struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
+	struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr);
 	socklen_t salen = sizeof(mnt_server->saddr);
 	struct pmap *pmap = &mnt_server->pmap;
 	CLIENT *clnt;
@@ -1011,11 +1011,11 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
 		struct sockaddr_in *caddr)
 {
 	CLIENT *clnt = NULL;
-	int sock, stat;
+	int sock, status;
 	static char clnt_res;
 	struct sockaddr dissolve;
 
-	rpc_createerr.cf_stat = stat = 0;
+	rpc_createerr.cf_stat = status = 0;
 	sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
 	if (sock == RPC_ANYSOCK) {
 		if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
@@ -1058,18 +1058,18 @@ int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
 		return 0;
 	}
 	memset(&clnt_res, 0, sizeof(clnt_res));
-	stat = clnt_call(clnt, NULLPROC,
+	status = clnt_call(clnt, NULLPROC,
 			 (xdrproc_t)xdr_void, (caddr_t)NULL,
 			 (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
 			 TIMEOUT);
-	if (stat) {
+	if (status) {
 		clnt_geterr(clnt, &rpc_createerr.cf_error);
-		rpc_createerr.cf_stat = stat;
+		rpc_createerr.cf_stat = status;
 	}
 	clnt_destroy(clnt);
 	close(sock);
 
-	if (stat == RPC_SUCCESS)
+	if (status == RPC_SUCCESS)
 		return 1;
 	else
 		return 0;
@@ -1103,13 +1103,13 @@ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen,
 
 	switch (sap->sa_family) {
 	case AF_INET:
-		if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+		if (bind(sock, SAFE_SOCKADDR(&sin), sizeof(sin)) < 0) {
 			close(sock);
 			return 0;
 		}
 		break;
 	case AF_INET6:
-		if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
+		if (bind(sock, SAFE_SOCKADDR(&sin6), sizeof(sin6)) < 0) {
 			close(sock);
 			return 0;
 		}
@@ -1346,7 +1346,7 @@ nfs_nfs_port(struct mount_options *options, unsigned long *port)
 	case PO_NOT_FOUND:
 		break;
 	case PO_FOUND:
-		if (tmp >= 1 && tmp <= 65535) {
+		if (tmp >= 0 && tmp <= 65535) {
 			*port = tmp;
 			return 1;
 		}
@@ -1518,7 +1518,11 @@ nfs_mount_protocol(struct mount_options *options, unsigned long *protocol)
 	 * set @protocol to zero.  The pmap protocol value will
 	 * be filled in later by an rpcbind query in this case.
 	 */
-	return nfs_nfs_protocol(options, protocol);
+	if (!nfs_nfs_protocol(options, protocol))
+		return 0;
+	if (*protocol == NFSPROTO_RDMA)
+		*protocol = IPPROTO_TCP;
+	return 1;
 }
 
 /*
@@ -1534,7 +1538,7 @@ nfs_mount_port(struct mount_options *options, unsigned long *port)
 	case PO_NOT_FOUND:
 		break;
 	case PO_FOUND:
-		if (tmp >= 1 && tmp <= 65535) {
+		if (tmp >= 0 && tmp <= 65535) {
 			*port = tmp;
 			return 1;
 		}
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index 55d4b55..be91a25 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -69,10 +69,9 @@ for details on specifying raw IPv6 addresses.
 .P
 The
 .I fstype
-field contains "nfs", for whatever version of the protocol.
-The
-.B nfs
-allow several mount options, which are described below.
+field contains "nfs".  Use of the "nfs4" fstype in
+.I /etc/fstab
+is deprecated.
 .SH "MOUNT OPTIONS"
 Refer to
 .BR mount (8)
@@ -464,9 +463,9 @@ by other clients, but can impact application and server performance.
 .IP
 The DATA AND METADATA COHERENCE section contains a
 detailed discussion of these trade-offs.
-.SS "Options for versions 2 and 3 only"
+.SS "Options for NFS versions 2 and 3 only"
 Use these options, along with the options in the above subsection,
-for NFSv2/v3 only. They will be ignored for newer versions.
+for NFS versions 2 and 3 only.
 .TP 1.5i
 .BI proto= netid
 The transport protocol name and protocol family the NFS client uses
@@ -619,7 +618,7 @@ in such cases.
 .BI nfsvers= n
 The NFS protocol version number used to contact the server's NFS service.
 If the server does not support the requested version, the mount request fails.
-If this option is not specified, the client negociate a suitable version with
+If this option is not specified, the client negotiates a suitable version with
 the server, trying version 4 first, version 3 second, and version 2 last.
 .TP 1.5i
 .BI vers= n
@@ -717,9 +716,53 @@ If this option is not specified, the NFS client uses READDIRPLUS requests
 on NFS version 3 mounts to read small directories.
 Some applications perform better if the client uses only READDIR requests
 for all directories.
-.SS "Options for version 4 only"
+.TP 1.5i
+.BR local_lock= mechanism
+Specifies whether to use local locking for any or both of the flock and the
+POSIX locking mechanisms.
+.I mechanism
+can be one of
+.BR all ,
+.BR flock ,
+.BR posix ,
+or
+.BR none .
+This option is supported in kernels 2.6.37 and later.
+.IP
+The Linux NFS client provides a way to make locks local. This means, the
+applications can lock files, but such locks provide exclusion only against
+other applications running on the same client. Remote applications are not
+affected by these locks.
+.IP
+If this option is not specified, or if
+.B none
+is specified, the client assumes that the locks are not local.
+.IP
+If
+.BR all
+is specified, the client assumes that both flock and POSIX locks are local.
+.IP
+If
+.BR flock
+is specified, the client assumes that only flock locks are local and uses
+NLM sideband protocol to lock files when POSIX locks are used.
+.IP
+If
+.BR posix
+is specified, the client assumes that POSIX locks are local and uses NLM
+sideband protocol to lock files when flock locks are used.
+.IP
+To support legacy flock behavior similar to that of NFS clients < 2.6.12, use
+'local_lock=flock'. This option is required when exporting NFS mounts via
+Samba as Samba maps Windows share mode locks as flock. Since NFS clients >
+2.6.12 implement flock by emulating POSIX locks, this will result in
+conflicting locks.
+.IP
+NOTE: When used together, the 'local_lock' mount option will be overridden
+by 'nolock'/'lock' mount option.
+.SS "Options for NFS version 4 only"
 Use these options, along with the options in the first subsection above,
-for NFSv4 only. They will be ignored with older versions.
+for NFS version 4 and newer.
 .TP 1.5i
 .BI proto= netid
 The transport protocol name and protocol family the NFS client uses
@@ -1480,32 +1523,54 @@ of Access Control Lists that are semantically richer than POSIX ACLs.
 NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such,
 some translation between the two is required
 in an environment that mixes POSIX ACLs and NFS version 4.
-.SH FILES
-.TP 1.5i
-.I /etc/fstab
-file system table
-.SH BUGS
-The generic
-.B remount
-option is not fully supported.
-Generic options, such as
-.BR rw " and " ro
-can be modified using the
-.B remount
-option,
-but NFS-specific options are not all supported.
+.SH "THE REMOUNT OPTION"
+Generic mount options such as
+.BR rw " and " sync
+can be modified on NFS mount points using the
+.BR remount
+option.
+See
+.BR mount (8)
+for more information on generic mount options.
+.P
+With few exceptions, NFS-specific options
+are not able to be modified during a remount.
 The underlying transport or NFS version
 cannot be changed by a remount, for example.
+.P
 Performing a remount on an NFS file system mounted with the
 .B noac
 option may have unintended consequences.
 The
 .B noac
-option is a mixture of a generic option,
+option is a combination of the generic option
 .BR sync ,
-and an NFS-specific option
+and the NFS-specific option
 .BR actimeo=0 .
+.SS "Unmounting after a remount"
+For mount points that use NFS versions 2 or 3, the NFS umount subcommand
+depends on knowing the original set of mount options used to perform the
+MNT operation.
+These options are stored on disk by the NFS mount subcommand,
+and can be erased by a remount.
 .P
+To ensure that the saved mount options are not erased during a remount,
+specify either the local mount directory, or the server hostname and
+export pathname, but not both, during a remount.  For example,
+.P
+.NF
+.TA 2.5i
+	mount -o remount,ro /mnt
+.FI
+.P
+merges the mount option
+.B ro
+with the mount options already saved on disk for the NFS server mounted at /mnt.
+.SH FILES
+.TP 1.5i
+.I /etc/fstab
+file system table
+.SH BUGS
 Before 2.4.7, the Linux NFS client did not support NFS over TCP.
 .P
 Before 2.4.20, the Linux NFS client used a heuristic
diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
index 1514340..02d40ff 100644
--- a/utils/mount/nfsumount.c
+++ b/utils/mount/nfsumount.c
@@ -31,12 +31,16 @@
 #include "nls.h"
 
 #include "mount_constants.h"
+#include "nfs_mount.h"
 #include "mount.h"
 #include "error.h"
 #include "network.h"
 #include "parse_opt.h"
 #include "parse_dev.h"
 
+#define MOUNTSFILE	"/proc/mounts"
+#define LINELEN		(4096)
+
 #if !defined(MNT_FORCE)
 /* dare not try to include <linux/mount.h> -- lots of errors */
 #define MNT_FORCE 1
@@ -109,7 +113,7 @@ static int del_mtab(const char *spec, const char *node)
 			res = try_remount(spec, node);
 			if (res)
 				goto writemtab;
-			return 0;
+			return EX_SUCCESS;
 		} else
 			umnt_err = errno;
 	}
@@ -127,7 +131,7 @@ static int del_mtab(const char *spec, const char *node)
 	}
 
 	if (res >= 0)
-		return 0;
+		return EX_SUCCESS;
 
 	if (umnt_err)
 		umount_error(umnt_err, node);
@@ -241,6 +245,91 @@ static int nfs_umount23(const char *devname, char *string)
 	return result;
 }
 
+/*
+ * Detect NFSv4 mounts.
+ *
+ * Consult /proc/mounts to determine if the mount point
+ * is an NFSv4 mount.  The kernel is authoritative about
+ * what type of mount this is.
+ *
+ * Returns 1 if "mc" is an NFSv4 mount, zero if not, and
+ * -1 if some error occurred.
+ */
+static int nfs_umount_is_vers4(const struct mntentchn *mc)
+{
+	char buffer[LINELEN], *next;
+	int retval;
+	FILE *f;
+
+	if ((f = fopen(MOUNTSFILE, "r")) == NULL) {
+		fprintf(stderr, "%s: %s\n",
+			MOUNTSFILE, strerror(errno));
+		return -1;
+	}
+
+	retval = -1;
+	while (fgets(buffer, sizeof(buffer), f) != NULL) {
+		char *device, *mntdir, *type, *flags;
+		struct mount_options *options;
+		char *line = buffer;
+
+		next = strchr(line, '\n');
+		if (next != NULL)
+			*next = '\0';
+
+		device = strtok(line, " \t");
+		if (device == NULL)
+			continue;
+		mntdir = strtok(NULL, " \t");
+		if (mntdir == NULL)
+			continue;
+		if (strcmp(device, mc->m.mnt_fsname) != 0 &&
+		    strcmp(mntdir, mc->m.mnt_dir) != 0)
+			continue;
+
+		type = strtok(NULL, " \t");
+		if (type == NULL)
+			continue;
+		if (strcmp(type, "nfs4") == 0)
+			goto out_nfs4;
+
+		flags = strtok(NULL, " \t");
+		if (flags == NULL)
+			continue;
+		options = po_split(flags);
+		if (options != NULL) {
+			unsigned long version;
+			int rc;
+
+			rc = nfs_nfs_version(options, &version);
+			po_destroy(options);
+			if (rc && version == 4)
+				goto out_nfs4;
+		}
+
+		goto out_nfs;
+	}
+	if (retval == -1)
+		fprintf(stderr, "%s was not found in %s\n",
+			mc->m.mnt_dir, MOUNTSFILE);
+
+out:
+	fclose(f);
+	return retval;
+
+out_nfs4:
+	if (verbose)
+		fprintf(stderr, "NFSv4 mount point detected\n");
+	retval = 1;
+	goto out;
+
+out_nfs:
+	if (verbose)
+		fprintf(stderr, "Legacy NFS mount point detected\n");
+	retval = 0;
+	goto out;
+}
+
 static struct option umount_longopts[] =
 {
   { "force", 0, 0, 'f' },
@@ -362,16 +451,25 @@ int nfsumount(int argc, char *argv[])
 		}
 	}
 
-	ret = 0;
+	ret = EX_SUCCESS;
 	if (mc) {
-		if (!lazy && strcmp(mc->m.mnt_type, "nfs4") != 0)
-			/* We ignore the error from nfs_umount23.
-			 * If the actual umount succeeds (in del_mtab),
-			 * we don't want to signal an error, as that
-			 * could cause /sbin/mount to retry!
-			 */
-			nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts);
-		ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir) ?: ret;
+		if (!lazy) {
+			switch (nfs_umount_is_vers4(mc)) {
+			case 0:
+				/* We ignore the error from nfs_umount23.
+				 * If the actual umount succeeds (in del_mtab),
+				 * we don't want to signal an error, as that
+				 * could cause /sbin/mount to retry!
+				 */
+				nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts);
+				break;
+			case 1:
+				break;
+			default:
+				return EX_FAIL;
+			}
+		}
+		ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir);
 	} else if (*spec != '/') {
 		if (!lazy)
 			ret = nfs_umount23(spec, "tcp,v3");
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
index f0918f7..ab869d9 100644
--- a/utils/mount/parse_opt.c
+++ b/utils/mount/parse_opt.c
@@ -508,7 +508,7 @@ po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *va
 int po_rightmost(struct mount_options *options, const char *keys[])
 {
 	struct mount_option *option;
-	unsigned int i;
+	int i;
 
 	if (options) {
 		for (option = options->tail; option; option = option->prev) {
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index 50a1a2a..f1aa503 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -49,10 +49,6 @@
 #include "parse_dev.h"
 #include "conffile.h"
 
-#ifndef HAVE_DECL_AI_ADDRCONFIG
-#define AI_ADDRCONFIG	0
-#endif
-
 #ifndef NFS_PROGRAM
 #define NFS_PROGRAM	(100003)
 #endif
@@ -114,7 +110,7 @@ static void nfs_default_version(struct nfsmount_info *mi)
 	}
 }
 #else
-inline void nfs_default_version(struct nfsmount_info *mi) {}
+inline void nfs_default_version(__attribute__ ((unused)) struct nfsmount_info *mi) {}
 #endif /* MOUNT_CONFIG */
 
 /*
@@ -123,10 +119,12 @@ inline void nfs_default_version(struct nfsmount_info *mi) {}
  * Returns a time_t timeout timestamp, in seconds.
  */
 static time_t nfs_parse_retry_option(struct mount_options *options,
-				     unsigned int timeout_minutes)
+				     const time_t default_timeout)
 {
+	time_t timeout_minutes;
 	long tmp;
 
+	timeout_minutes = default_timeout;
 	switch (po_get_numeric(options, "retry", &tmp)) {
 	case PO_NOT_FOUND:
 		break;
@@ -135,6 +133,7 @@ static time_t nfs_parse_retry_option(struct mount_options *options,
 			timeout_minutes = tmp;
 			break;
 		}
+		/*FALLTHROUGH*/
 	case PO_BAD_VALUE:
 		if (verbose)
 			nfs_error(_("%s: invalid retry timeout was specified; "
@@ -142,7 +141,7 @@ static time_t nfs_parse_retry_option(struct mount_options *options,
 		break;
 	}
 
-	return time(NULL) + (time_t)(timeout_minutes * 60);
+	return time(NULL) + (timeout_minutes * 60);
 }
 
 /*
@@ -343,7 +342,6 @@ static int nfs_validate_options(struct nfsmount_info *mi)
 {
 	struct addrinfo hint = {
 		.ai_protocol	= (int)IPPROTO_UDP,
-		.ai_flags	= AI_ADDRCONFIG,
 	};
 	sa_family_t family;
 	int error;
@@ -570,16 +568,18 @@ static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
 	char *options = NULL;
 	int result;
 
+	if (mi->fake)
+		return 1;
+
 	if (po_join(opts, &options) == PO_FAILED) {
 		errno = EIO;
 		return 0;
 	}
 
-	if (mi->fake)
-		return 1;
-
 	result = mount(mi->spec, mi->node, mi->type,
 			mi->flags & ~(MS_USER|MS_USERS), options);
+	free(options);
+
 	if (verbose && result) {
 		int save = errno;
 		nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
@@ -650,7 +650,7 @@ out_fail:
 static int nfs_try_mount_v3v2(struct nfsmount_info *mi)
 {
 	struct addrinfo *ai;
-	int ret;
+	int ret = 0;
 
 	for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
 		ret = nfs_do_mount_v3v2(mi, ai->ai_addr, ai->ai_addrlen);
@@ -737,7 +737,7 @@ out_fail:
 static int nfs_try_mount_v4(struct nfsmount_info *mi)
 {
 	struct addrinfo *ai;
-	int ret;
+	int ret = 0;
 
 	for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
 		ret = nfs_do_mount_v4(mi, ai->ai_addr, ai->ai_addrlen);
diff --git a/utils/mount/version.h b/utils/mount/version.h
index 46552a1..af61a6f 100644
--- a/utils/mount/version.h
+++ b/utils/mount/version.h
@@ -42,9 +42,9 @@ static inline unsigned int linux_version_code(void)
 	if (uname(&my_utsname))
 		return 0;
 
-	p = atoi(strtok(my_utsname.release, "."));
-	q = atoi(strtok(NULL, "."));
-	r = atoi(strtok(NULL, "."));
+	p = (unsigned int)atoi(strtok(my_utsname.release, "."));
+	q = (unsigned int)atoi(strtok(NULL, "."));
+	r = (unsigned int)atoi(strtok(NULL, "."));
 	return MAKE_VERSION(p, q, r);
 }
 
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index d309950..035624c 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -99,12 +99,9 @@ static int version_any(void)
 static void
 unregister_services (void)
 {
-	if (version2()) {
-		nfs_svc_unregister(MOUNTPROG, MOUNTVERS);
-		nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX);
-	}
-	if (version3())
-		nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3);
+	nfs_svc_unregister(MOUNTPROG, MOUNTVERS);
+	nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX);
+	nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3);
 }
 
 static void
@@ -840,6 +837,7 @@ main(int argc, char **argv)
 	if (new_cache)
 		cache_open();
 
+	unregister_services();
 	if (version2()) {
 		listeners += nfs_svc_create("mountd", MOUNTPROG,
 					MOUNTVERS, mount_dispatch, port);
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
index 4bb96e8..016a357 100644
--- a/utils/mountd/mountd.man
+++ b/utils/mountd/mountd.man
@@ -106,11 +106,11 @@ This option can be used to request that
 .B rpc.mountd
 do 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,
+can support both NFS version 2, 3 and 4. If the
+either one of these version should not be offered,
 .B rpc.mountd
 must be invoked with the option
-.B "\-\-no-nfs-version 3" .
+.B "\-\-no-nfs-version <vers>" .
 .TP
 .B \-n " or " \-\-no-tcp
 Don't advertise TCP for mount.
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index d339296..527377f 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -205,6 +205,7 @@ mountlist_list(void)
 	}
 	if (stb.st_mtime != last_mtime) {
 		mountlist_freeall(mlist);
+		mlist = NULL;
 		last_mtime = stb.st_mtime;
 
 		setrmtabent("r");
diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am
new file mode 100644
index 0000000..f837b91
--- /dev/null
+++ b/utils/nfsidmap/Makefile.am
@@ -0,0 +1,9 @@
+## Process this file with automake to produce Makefile.in
+
+man8_MANS = nfsidmap.man
+
+sbin_PROGRAMS	= nfsidmap
+nfsidmap_SOURCES = nfsidmap.c
+nfsidmap_LDADD = -lnfsidmap -lkeyutils
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c
new file mode 100644
index 0000000..2d87381
--- /dev/null
+++ b/utils/nfsidmap/nfsidmap.c
@@ -0,0 +1,118 @@
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <keyutils.h>
+#include <nfsidmap.h>
+
+#include <syslog.h>
+
+/* gcc nfsidmap.c -o nfsidmap -l nfsidmap -l keyutils */
+
+#define MAX_ID_LEN   11
+#define IDMAP_NAMESZ 128
+#define USER  1
+#define GROUP 0
+
+
+/*
+ * Find either a user or group id based on the name@domain string
+ */
+int id_lookup(char *name_at_domain, key_serial_t key, int type)
+{
+	char id[MAX_ID_LEN];
+	uid_t uid = 0;
+	gid_t gid = 0;
+	int rc;
+
+	if (type == USER) {
+		rc = nfs4_owner_to_uid(name_at_domain, &uid);
+		sprintf(id, "%u", uid);
+	} else {
+		rc = nfs4_group_owner_to_gid(name_at_domain, &gid);
+		sprintf(id, "%u", gid);
+	}
+
+	if (rc == 0)
+		rc = keyctl_instantiate(key, id, strlen(id) + 1, 0);
+
+	return rc;
+}
+
+/*
+ * Find the name@domain string from either a user or group id
+ */
+int name_lookup(char *id, key_serial_t key, int type)
+{
+	char name[IDMAP_NAMESZ];
+	char domain[NFS4_MAX_DOMAIN_LEN];
+	uid_t uid;
+	gid_t gid;
+	int rc;
+
+	rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN);
+	if (rc != 0) {
+		rc = -1;
+		goto out;
+	}
+
+	if (type == USER) {
+		uid = atoi(id);
+		rc = nfs4_uid_to_name(uid, domain, name, IDMAP_NAMESZ);
+	} else {
+		gid = atoi(id);
+		rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ);
+	}
+
+	if (rc == 0)
+		rc = keyctl_instantiate(key, &name, strlen(name), 0);
+
+out:
+	return rc;
+}
+
+int main(int argc, char **argv)
+{
+	char *arg;
+	char *value;
+	char *type;
+	int rc = 1;
+	int timeout = 600;
+	key_serial_t key;
+
+	if (argc < 3)
+		return 1;
+
+	arg = malloc(sizeof(char) * strlen(argv[2]) + 1);
+	strcpy(arg, argv[2]);
+	type = strtok(arg, ":");
+	value = strtok(NULL, ":");
+
+	if (argc == 4) {
+		timeout = atoi(argv[3]);
+		if (timeout < 0)
+			timeout = 0;
+	}
+
+	key = strtol(argv[1], NULL, 10);
+
+	if (strcmp(type, "uid") == 0)
+		rc = id_lookup(value, key, USER);
+	else if (strcmp(type, "gid") == 0)
+		rc = id_lookup(value, key, GROUP);
+	else if (strcmp(type, "user") == 0)
+		rc = name_lookup(value, key, USER);
+	else if (strcmp(type, "group") == 0)
+		rc = name_lookup(value, key, GROUP);
+
+	/* Set timeout to 5 (600 seconds) minutes */
+	if (rc == 0)
+		keyctl_set_timeout(key, timeout);
+
+	free(arg);
+	return rc;
+}
diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man
new file mode 100644
index 0000000..6c1a2d4
--- /dev/null
+++ b/utils/nfsidmap/nfsidmap.man
@@ -0,0 +1,60 @@
+.\"
+.\"@(#)nfsidmap(8) - The NFS idmapper upcall program
+.\"
+.\" Copyright (C) 2010 Bryan Schumaker <bjschuma@netapp.com>
+.TH nfsidmap 5 "1 October 2010"
+.SH NAME
+nfsidmap \- The NFS idmapper upcall program
+.SH DESCRIPTION
+The file
+.I /usr/sbin/nfsidmap
+is used by the NFS idmapper to translate user and group ids into names, and to
+translate user and group names into ids. Idmapper uses request-key to perform
+the upcall and cache the result.
+.I /usr/sbin/nfsidmap
+should only be called by request-key, and will perform the translation and
+initialize a key with the resulting information.
+.PP
+NFS_USE_NEW_IDMAPPER must be selected when configuring the kernel to use this
+feature.
+.SH CONFIGURING
+The file
+.I /etc/request-key.conf
+will need to be modified so
+.I /sbin/request-key
+can properly direct the upcall. The following line should be added before a call
+to keyctl negate:
+.PP
+create	nfs_idmap	*	*	/usr/sbin/nfsidmap %k %d 600
+.PP
+This will direct all nfs_idmap requests to the program
+.I /usr/sbin/nfsidmap
+The last parameter, 600, defines how many seconds into the future the key will
+expire.  This is an optional parameter for
+.I /usr/sbin/nfsidmap
+and will default to 600 seconds when not specified.
+.PP
+The idmapper system uses four key descriptions:
+.PP
+	  uid: Find the UID for the given user
+.br
+	  gid: Find the GID for the given group
+.br
+	 user: Find the user name for the given UID
+.br
+	group: Find the group name for the given GID
+.PP
+You can choose to handle any of these individually, rather than using the
+generic upcall program.  If you would like to use your own program for a uid
+lookup then you would edit your request-key.conf so it looks similar to this:
+.PP
+create	nfs_idmap	uid:*	*	/some/other/program %k %d 600
+.br
+create	nfs_idmap	*		*	/usr/sbin/nfsidmap %k %d 600
+.PP
+Notice that the new line was added above the line for the generic program.
+request-key will find the first matching line and run the corresponding program.
+In this case, /some/other/program will handle all uid lookups, and
+/usr/sbin/nfsidmap will handle gid, user, and group lookups.
+.SH AUTHOR
+Bryan Schumaker, <bjschuma@netapp.com>
diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
index bacef8e..f31bb81 100644
--- a/utils/nfsstat/nfsstat.c
+++ b/utils/nfsstat/nfsstat.c
@@ -46,7 +46,7 @@ static unsigned int	cltproc3info[CLTPROC3_SZ+2],
 static unsigned int	srvproc4info[SRVPROC4_SZ+2],
 			srvproc4info_old[SRVPROC4_SZ+2];	/* NFSv4 call counts ([0] == 2) */
 static unsigned int	cltproc4info[CLTPROC4_SZ+2],
-			cltproc4info_old[CLTPROC4_SZ+2];	/* NFSv4 call counts ([0] == 48) */
+			cltproc4info_old[CLTPROC4_SZ+2];	/* NFSv4 call counts ([0] == 49) */
 static unsigned int	srvproc4opsinfo[SRVPROC4OPS_SZ+2],
 			srvproc4opsinfo_old[SRVPROC4OPS_SZ+2];	/* NFSv4 call counts ([0] == 59) */
 static unsigned int	srvnetinfo[5], srvnetinfo_old[5];	/* 0  # of received packets
@@ -221,8 +221,8 @@ DECLARE_CLT(cltinfo);
 DECLARE_CLT(cltinfo, _old);
 
 static void		print_all_stats(int, int, int);
-static void		print_server_stats(int, int);
-static void		print_client_stats(int, int);
+static void		print_server_stats(int);
+static void		print_client_stats(int);
 static void		print_stats_list(int, int, int);
 static void		print_numbers(const char *, unsigned int *,
 					unsigned int);
@@ -239,7 +239,7 @@ static int		mounts(const char *);
 
 static void		get_stats(const char *, struct statinfo *, int *, int,
 					int);
-static int		has_stats(const unsigned int *);
+static int		has_stats(const unsigned int *, int);
 static int		has_rpcstats(const unsigned int *, int);
 static void 		diff_stats(struct statinfo *, struct statinfo *, int);
 static void 		unpause(int);
@@ -468,7 +468,7 @@ main(int argc, char **argv)
 		pause();
 	}
 
-	if (opt_since || opt_sleep) {
+	if (opt_since || (opt_sleep && !sleep_time)) {
 		if (opt_srv) {
 			get_stats(NFSSRVSTAT, serverinfo_tmp, &opt_srv, opt_clt, 1);
 			diff_stats(serverinfo_tmp, serverinfo, 1);
@@ -516,16 +516,16 @@ main(int argc, char **argv)
 static void
 print_all_stats (int opt_srv, int opt_clt, int opt_prt)
 {
-	print_server_stats(opt_srv, opt_prt);
-	print_client_stats(opt_clt, opt_prt);
+	if (opt_srv)
+		print_server_stats(opt_prt);
+
+	if (opt_clt)
+		print_client_stats(opt_prt);
 }
 
 static void 
-print_server_stats(int opt_srv, int opt_prt) 
+print_server_stats(int opt_prt) 
 {
-	if (!opt_srv)
-		return;
-
 	if (opt_prt & PRNT_NET) {
 		if (opt_sleep && !has_rpcstats(srvnetinfo, 4)) {
 		} else {
@@ -582,31 +582,29 @@ print_server_stats(int opt_srv, int opt_prt)
 		printf("\n");
 	}
 	if (opt_prt & PRNT_CALLS) {
+		int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2);
+		int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2);
+		int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2);
+
 		if ((opt_prt & PRNT_V2) || 
-				((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
-			if (opt_sleep && !has_stats(srvproc2info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+			if (!opt_sleep || has_v2_stats) {
 				print_callstats(LABEL_srvproc2,
 					nfsv2name, srvproc2info + 1, 
 					sizeof(nfsv2name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V3) || 
-				((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
-			if (opt_sleep && !has_stats(srvproc3info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+			if (!opt_sleep || has_v3_stats) {
 				print_callstats(LABEL_srvproc3,
 					nfsv3name, srvproc3info + 1, 
 					sizeof(nfsv3name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V4) || 
-				((opt_prt & PRNT_AUTO) && has_stats(srvproc4info))) {
-			if (opt_sleep && !has_stats(srvproc4info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+			if (!opt_sleep || has_v4_stats) {
 				print_callstats( LABEL_srvproc4,
 					nfssrvproc4name, srvproc4info + 1, 
 					sizeof(nfssrvproc4name)/sizeof(char *));
@@ -618,11 +616,8 @@ print_server_stats(int opt_srv, int opt_prt)
 	}
 }
 static void
-print_client_stats(int opt_clt, int opt_prt) 
+print_client_stats(int opt_prt) 
 {
-	if (!opt_clt)
-		return;
-
 	if (opt_prt & PRNT_NET) {
 		if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) {
 			;
@@ -644,31 +639,28 @@ print_client_stats(int opt_clt, int opt_prt)
 		}
 	}
 	if (opt_prt & PRNT_CALLS) {
+		int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2);
+		int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2);
+		int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2);
 		if ((opt_prt & PRNT_V2) || 
-				((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
-			if (opt_sleep && !has_stats(cltproc2info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+			if (!opt_sleep || has_v2_stats) {
 				print_callstats(LABEL_cltproc2,
 					nfsv2name, cltproc2info + 1,  
 					sizeof(nfsv2name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V3) || 
-				((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
-			if (opt_sleep && !has_stats(cltproc3info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+			if (!opt_sleep || has_v3_stats) {
 				print_callstats(LABEL_cltproc3,
 					nfsv3name, cltproc3info + 1, 
 					sizeof(nfsv3name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V4) || 
-				((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
-			if (opt_sleep && !has_stats(cltproc4info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+			if (!opt_sleep || has_v4_stats) {
 				print_callstats(LABEL_cltproc4,
 					nfscltproc4name, cltproc4info + 1,  
 					sizeof(nfscltproc4name)/sizeof(char *));
@@ -681,34 +673,28 @@ static void
 print_clnt_list(int opt_prt) 
 {
 	if (opt_prt & PRNT_CALLS) {
+		int has_v2_stats = has_stats(cltproc2info, CLTPROC2_SZ+2);
+		int has_v3_stats = has_stats(cltproc3info, CLTPROC3_SZ+2);
+		int has_v4_stats = has_stats(cltproc4info, CLTPROC4_SZ+2);
 		if ((opt_prt & PRNT_V2) || 
-				((opt_prt & PRNT_AUTO) && has_stats(cltproc2info))) {
-			if (opt_sleep && !has_stats(cltproc2info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+			if (!opt_sleep || has_v2_stats) {
 				print_callstats_list("nfs v2 client",
 					nfsv2name, cltproc2info + 1,  
 					sizeof(nfsv2name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V3) || 
-				((opt_prt & PRNT_AUTO) && has_stats(cltproc3info))) {
-			if (opt_sleep && !has_stats(cltproc3info)) {
-				;
-			} else { 
+				((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+			if (!opt_sleep || has_v3_stats) {
 				print_callstats_list("nfs v3 client",
 					nfsv3name, cltproc3info + 1, 
 					sizeof(nfsv3name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V4) || 
-				((opt_prt & PRNT_AUTO) && has_stats(cltproc4info))) {
-			if (opt_sleep && !has_stats(cltproc4info)) {
-				;
-			} else {
-				print_callstats_list("nfs v4 ops",
-					nfssrvproc4opname, srvproc4opsinfo + 1, 
-					sizeof(nfssrvproc4opname)/sizeof(char *));
+				((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+			if (!opt_sleep || has_v4_stats) {
 				print_callstats_list("nfs v4 client",
 					nfscltproc4name, cltproc4info + 1,  
 					sizeof(nfscltproc4name)/sizeof(char *));
@@ -720,32 +706,32 @@ static void
 print_serv_list(int opt_prt) 
 {
 	if (opt_prt & PRNT_CALLS) {
+		int has_v2_stats = has_stats(srvproc2info, SRVPROC2_SZ+2);
+		int has_v3_stats = has_stats(srvproc3info, SRVPROC3_SZ+2);
+		int has_v4_stats = has_stats(srvproc4info, SRVPROC4_SZ+2);
 		if ((opt_prt & PRNT_V2) || 
-				((opt_prt & PRNT_AUTO) && has_stats(srvproc2info))) {
-			if (opt_sleep && !has_stats(srvproc2info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v2_stats)) {
+			if (!opt_sleep || has_v2_stats) {
 				print_callstats_list("nfs v2 server",
 					nfsv2name, srvproc2info + 1, 
 					sizeof(nfsv2name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V3) || 
-				((opt_prt & PRNT_AUTO) && has_stats(srvproc3info))) {
-			if (opt_sleep && !has_stats(srvproc3info)) {
-				;
-			} else {
+				((opt_prt & PRNT_AUTO) && has_v3_stats)) {
+			if (!opt_sleep || has_v3_stats) {
 				print_callstats_list("nfs v3 server",
 					nfsv3name, srvproc3info + 1, 
 					sizeof(nfsv3name)/sizeof(char *));
 			}
 		}
 		if ((opt_prt & PRNT_V4) || 
-				((opt_prt & PRNT_AUTO) && has_stats(srvproc4opsinfo))) {
-			if (opt_sleep && !has_stats(srvproc4info)) {
-				;
-			} else {
-				print_callstats_list("nfs v4 ops",
+				((opt_prt & PRNT_AUTO) && has_v4_stats)) {
+			if (!opt_sleep || has_v4_stats) {
+				print_callstats_list("nfs v4 server",
+					nfssrvproc4name, srvproc4info + 1, 
+					sizeof(nfssrvproc4name)/sizeof(char *));
+				print_callstats_list("nfs v4 servop",
 					nfssrvproc4opname, srvproc4opsinfo + 1, 
 					sizeof(nfssrvproc4opname)/sizeof(char *));
 			}
@@ -1054,9 +1040,9 @@ out:
  * there are stats if the sum's greater than the entry-count.
  */
 static int
-has_stats(const unsigned int *info)
+has_stats(const unsigned int *info, int nr)
 {
-	return (info[0] && info[info[0] + 1] > info[0]);
+	return (info[0] && info[nr-1] > info[0]);
 }
 static int
 has_rpcstats(const unsigned int *info, int size)
diff --git a/utils/nfsstat/nfsstat.man b/utils/nfsstat/nfsstat.man
index 52215a9..cd573b0 100644
--- a/utils/nfsstat/nfsstat.man
+++ b/utils/nfsstat/nfsstat.man
@@ -30,10 +30,12 @@ Print only NFS v2 statistics. The default is to only print information
 about the versions of \fBNFS\fR that have non-zero counts.
 .TP
 .B \-3
-Print only NFS v3 statistics. 
+Print only NFS v3 statistics. The default is to only print information
+about the versions of \fBNFS\fR that have non-zero counts.
 .TP
 .B \-4
-Print only NFS v4 statistics. 
+Print only NFS v4 statistics. The default is to only print information
+about the versions of \fBNFS\fR that have non-zero counts.
 .TP
 .B \-m, \-\-mounts
 Print information about each of the mounted \fBNFS\fR file systems.
diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index 38f2265..616a3cb 100644
--- a/utils/statd/hostname.c
+++ b/utils/statd/hostname.c
@@ -39,10 +39,6 @@
 #include "statd.h"
 #include "xlog.h"
 
-#ifndef HAVE_DECL_AI_ADDRCONFIG
-#define AI_ADDRCONFIG	0
-#endif
-
 /**
  * statd_present_address - convert sockaddr to presentation address
  * @sap: pointer to socket address to convert
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 437e37a..1f490b0 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -34,8 +34,9 @@
 #include "nsm.h"
 #include "nfsrpc.h"
 
-#ifndef HAVE_DECL_AI_ADDRCONFIG
-#define AI_ADDRCONFIG	0
+/* glibc before 2.3.4 */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV	0
 #endif
 
 #define NSM_TIMEOUT	2
@@ -78,7 +79,6 @@ smn_lookup(const char *name)
 {
 	struct addrinfo	*ai = NULL;
 	struct addrinfo hint = {
-		.ai_flags	= AI_ADDRCONFIG,
 		.ai_family	= (nsm_family == AF_INET ? AF_INET: AF_UNSPEC),
 		.ai_protocol	= (int)IPPROTO_UDP,
 	};
@@ -253,6 +253,7 @@ smn_bind_address(const char *srcaddr, const char *srcport)
 	if (srcaddr == NULL)
 		hint.ai_flags |= AI_PASSIVE;
 
+	/* Do not allow "node" and "service" parameters both to be NULL */
 	if (srcport == NULL)
 		error = getaddrinfo(srcaddr, "", &hint, &ai);
 	else