[nfs-utils] Updated to latest upstream RC release: nfs-utils-1-3-2-rc4 (bz 1164477)

Steve Dickson steved at fedoraproject.org
Sun Dec 14 18:34:39 UTC 2014


commit 7859467589327add2e8231448f4a802d774f882a
Author: Steve Dickson <steved at redhat.com>
Date:   Sat Dec 13 13:54:39 2014 -0500

    Updated to latest upstream RC release: nfs-utils-1-3-2-rc4 (bz 1164477)
    
    Signed-off-by: Steve Dickson <steved at redhat.com>

 nfs-utils-1.3.2.rc4.patch | 3463 +++++++++++++++++++++++++++++++++++++++++++++
 nfs-utils.spec            |    9 +-
 2 files changed, 3468 insertions(+), 4 deletions(-)
---
diff --git a/nfs-utils-1.3.2.rc4.patch b/nfs-utils-1.3.2.rc4.patch
new file mode 100644
index 0000000..9a629c6
--- /dev/null
+++ b/nfs-utils-1.3.2.rc4.patch
@@ -0,0 +1,3463 @@
+diff --git a/Makefile.am b/Makefile.am
+index 5824adc..4a2edc6 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -2,41 +2,13 @@
+ 
+ AUTOMAKE_OPTIONS = foreign
+ 
+-SUBDIRS = tools support utils linux-nfs tests
++SUBDIRS = tools support utils linux-nfs tests systemd
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+ 
+ EXTRA_DIST = \
+ 	autogen.sh \
+ 	\
+-	debian/changelog \
+-	debian/control \
+-	debian/copyright \
+-	debian/etc.exports \
+-	debian/idmapd.conf \
+-	debian/nfs-common.conffiles \
+-	debian/nfs-common.default \
+-	debian/nfs-common.dirs \
+-	debian/nfs-common.files \
+-	debian/nfs-common.init \
+-	debian/nfs-common.install \
+-	debian/nfs-common.postinst \
+-	debian/nfs-common.postrm \
+-	debian/nfs-common.prerm \
+-	debian/nfs-kernel-server.NEWS \
+-	debian/nfs-kernel-server.conffiles \
+-	debian/nfs-kernel-server.default \
+-	debian/nfs-kernel-server.dirs \
+-	debian/nfs-kernel-server.init \
+-	debian/nfs-kernel-server.postinst \
+-	debian/nfs-kernel-server.postrm \
+-	debian/nfs-kernel-server.prerm \
+-	debian/nhfsstone.dirs \
+-	debian/nhfsstone.files \
+-	debian/nhfsstone.postinst \
+-	debian/nhfsstone.prerm \
+-	debian/rules \
+-	\
+ 	aclocal/bsdsignals.m4 \
+ 	aclocal/nfs-utils.m4 \
+ 	aclocal/kerberos5.m4 \
+diff --git a/aclocal/libtirpc.m4 b/aclocal/libtirpc.m4
+index b823364..ebc1bea 100644
+--- a/aclocal/libtirpc.m4
++++ b/aclocal/libtirpc.m4
+@@ -2,61 +2,18 @@ dnl Checks for TI-RPC library and headers
+ dnl
+ AC_DEFUN([AC_LIBTIRPC], [
+ 
+-  AC_ARG_WITH([tirpcinclude],
+-              [AC_HELP_STRING([--with-tirpcinclude=DIR],
+-                              [use TI-RPC headers in DIR])],
+-              [tirpc_header_dir=$withval],
+-              [tirpc_header_dir=/usr/include/tirpc])
+-
+-  dnl if --enable-tirpc was specifed, the following components
+-  dnl must be present, and we set up HAVE_ macros for them.
+-
+-  if test "$enable_tirpc" != "no"; then
+-
+-    dnl look for the library
+-    AC_CHECK_LIB([tirpc], [clnt_tli_create], [:],
+-                 [if test "$enable_tirpc" = "yes"; then
+-			AC_MSG_ERROR([libtirpc not found.])
+-		  else
+-			AC_MSG_WARN([libtirpc not found. TIRPC disabled!])
+-			enable_tirpc="no"
+-		  fi])
+-  fi
+-
+-  if test "$enable_tirpc" != "no"; then
+-
+-    dnl Check if library contains authgss_free_private_data
+-    AC_CHECK_LIB([tirpc], [authgss_free_private_data], [have_free_private_data=yes],
+-			[have_free_private_data=no])
+-  fi
+-
+-  if test "$enable_tirpc" != "no"; then
+-    dnl also must have the headers installed where we expect
+-    dnl look for headers; add -I compiler option if found
+-    AC_CHECK_HEADERS([${tirpc_header_dir}/netconfig.h],
+-    		      AC_SUBST([AM_CPPFLAGS], ["-I${tirpc_header_dir}"]),
+-		      [if test "$enable_tirpc" = "yes"; then
+-			 AC_MSG_ERROR([libtirpc headers not found.])
+-		       else
+-			 AC_MSG_WARN([libtirpc headers not found. TIRPC disabled!])
+-			 enable_tirpc="no"
+-		       fi])
+-
+-  fi
+-
+-  dnl now set $LIBTIRPC accordingly
+-  if test "$enable_tirpc" != "no"; then
+-    AC_DEFINE([HAVE_LIBTIRPC], 1,
+-              [Define to 1 if you have and wish to use libtirpc.])
+-    LIBTIRPC="-ltirpc"
+-    if test "$have_free_private_data" = "yes"; then
+-      AC_DEFINE([HAVE_AUTHGSS_FREE_PRIVATE_DATA], 1,
+-	      [Define to 1 if your rpcsec library provides authgss_free_private_data,])
+-    fi
+-  else
+-    LIBTIRPC=""
+-  fi
+-
++  PKG_PROG_PKG_CONFIG([0.9.0])
++  AS_IF(
++    [test "$enable_tirpc" != "no"],
++    [PKG_CHECK_MODULES([TIRPC], [libtirpc >= 0.2.4],
++                      [LIBTIRPC="${TIRPC_LIBS}"
++                       AM_CPPFLAGS="${AM_CPPFLAGS} ${TIRPC_CFLAGS}"
++                       AC_DEFINE([HAVE_LIBTIRPC], [1],
++                                 [Define to 1 if you have and wish to use libtirpc.])],
++                      [AS_IF([test "$enable_tirpc" != "no"], [AC_MSG_ERROR([libtirpc not found.])],
++                             [LIBTIRPC=""])])])
++
++  AC_SUBST([AM_CPPFLAGS])
+   AC_SUBST(LIBTIRPC)
+ 
+ ])dnl
+diff --git a/configure.ac b/configure.ac
+index 59fd14d..a8b1fc3 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -55,6 +55,16 @@ AC_ARG_WITH(start-statd,
+ 	)
+ 	AC_SUBST(startstatd)
+ 	AC_DEFINE_UNQUOTED(START_STATD, "$startstatd", [Define this to a script which can start statd on mount])
++unitdir=/usr/lib/systemd/system
++AC_ARG_WITH(systemd,
++	[AC_HELP_STRING([--with-systemd@<:@=unit-dir-path@:>@],
++			[install systemd unit files @<:@Default: no, and path defaults to /usr/lib/systemd/system if not given@:>@])],
++	test "$withval" = "no" && use_systemd=0 || unitdir=$withval use_systemd=1
++	use_systemd=0
++	)
++	AM_CONDITIONAL(INSTALL_SYSTEMD, [test "$use_systemd" = 1])
++	AC_SUBST(unitdir)
++
+ AC_ARG_ENABLE(nfsv4,
+ 	[AC_HELP_STRING([--enable-nfsv4],
+                         [enable support for NFSv4 @<:@default=yes@:>@])],
+@@ -108,10 +118,8 @@ AC_ARG_ENABLE(svcgss,
+     [enable building svcgssd for rpcsec_gss server support @<:@default=yes@:>@])],
+ 	enable_svcgss=$enableval,
+ 	enable_svcgss=yes)
+-	if test "$enable_gss" = yes; then
+-		if "enable_svcgss" = yes; then
+-			SVCGSSD=svcgssd
+-		fi
++	if test "$enable_gss" = yes -a "$enable_svcgss" = yes; then
++		SVCGSSD=svcgssd
+ 	else
+ 		enable_svcgss=
+ 		SVCGSSD=
+@@ -518,6 +526,7 @@ AC_CONFIG_FILES([
+ 	utils/showmount/Makefile
+ 	utils/statd/Makefile
+ 	utils/osd_login/Makefile
++	systemd/Makefile
+ 	tests/Makefile
+ 	tests/nsm_client/Makefile])
+ AC_OUTPUT
+diff --git a/support/export/client.c b/support/export/client.c
+index f85e11c..95156f0 100644
+--- a/support/export/client.c
++++ b/support/export/client.c
+@@ -277,7 +277,7 @@ client_lookup(char *hname, int canonical)
+ 	if (htype == MCL_FQDN && !canonical) {
+ 		ai = host_addrinfo(hname);
+ 		if (!ai) {
+-			xlog(L_ERROR, "Failed to resolve %s", hname);
++			xlog(L_WARNING, "Failed to resolve %s", hname);
+ 			goto out;
+ 		}
+ 		hname = ai->ai_canonname;
+diff --git a/support/export/export.c b/support/export/export.c
+index 6b1d045..e1bebce 100644
+--- a/support/export/export.c
++++ b/support/export/export.c
+@@ -69,22 +69,30 @@ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep)
+  * export_read - read entries from /etc/exports
+  * @fname: name of file to read from
+  *
++ * Returns number of read entries.
+  */
+-void
++int
+ export_read(char *fname)
+ {
+ 	struct exportent	*eep;
+ 	nfs_export		*exp;
+ 
++	int volumes = 0;
++
+ 	setexportent(fname, "r");
+ 	while ((eep = getexportent(0,1)) != NULL) {
+ 		exp = export_lookup(eep->e_hostname, eep->e_path, 0);
+-		if (!exp)
+-			export_create(eep, 0);
++		if (!exp) {
++			if (export_create(eep, 0))
++				/* possible complaints already logged */
++				volumes++;
++		}
+ 		else
+ 			warn_duplicated_exports(exp, eep);
+ 	}
+ 	endexportent();
++
++	return volumes;
+ }
+ 
+ /**
+diff --git a/support/export/hostname.c b/support/export/hostname.c
+index d9153e1..169baa5 100644
+--- a/support/export/hostname.c
++++ b/support/export/hostname.c
+@@ -177,11 +177,11 @@ host_addrinfo(const char *hostname)
+ 	case 0:
+ 		return ai;
+ 	case EAI_SYSTEM:
+-		xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m",
++		xlog(D_PARSE, "%s: failed to resolve %s: (%d) %m",
+ 				__func__, hostname, errno);
+ 		break;
+ 	default:
+-		xlog(D_GENERAL, "%s: failed to resolve %s: %s",
++		xlog(D_PARSE, "%s: failed to resolve %s: %s",
+ 				__func__, hostname, gai_strerror(error));
+ 		break;
+ 	}
+diff --git a/support/include/Makefile.am b/support/include/Makefile.am
+index 4b33ee9..5c80c8b 100644
+--- a/support/include/Makefile.am
++++ b/support/include/Makefile.am
+@@ -3,6 +3,7 @@
+ SUBDIRS = nfs rpcsvc sys
+ 
+ noinst_HEADERS = \
++	cld.h \
+ 	exportfs.h \
+ 	ha-callout.h \
+ 	misc.h \
+@@ -10,9 +11,13 @@ noinst_HEADERS = \
+ 	nfs_paths.h \
+ 	nfslib.h \
+ 	nfsrpc.h \
++	nls.h \
+ 	nsm.h \
++	pseudoflavors.h \
+ 	rpcmisc.h \
++	sockaddr.h \
+ 	tcpwrapper.h \
++	v4root.h \
+ 	xio.h \
+ 	xlog.h \
+ 	xmalloc.h \
+diff --git a/support/include/exportfs.h b/support/include/exportfs.h
+index 9021fae..4cac203 100644
+--- a/support/include/exportfs.h
++++ b/support/include/exportfs.h
+@@ -10,6 +10,7 @@
+ #define EXPORTFS_H
+ 
+ #include <netdb.h>
++#include <string.h>
+ 
+ #include "sockaddr.h"
+ #include "nfslib.h"
+@@ -133,7 +134,7 @@ struct addrinfo *		client_resolve(const struct sockaddr *sap);
+ int 				client_member(const char *client,
+ 						const char *name);
+ 
+-void				export_read(char *fname);
++int				export_read(char *fname);
+ void				export_reset(nfs_export *);
+ nfs_export *			export_lookup(char *hname, char *path, int caconical);
+ nfs_export *			export_find(const struct addrinfo *ai,
+diff --git a/support/include/nfslib.h b/support/include/nfslib.h
+index ce4b14b..c5dc6f8 100644
+--- a/support/include/nfslib.h
++++ b/support/include/nfslib.h
+@@ -152,11 +152,6 @@ struct nfs_fh_len *	getfh(const struct sockaddr_in *sin, const char *path);
+ struct nfs_fh_len *	getfh_size(const struct sockaddr_in *sin,
+ 					const char *path, int const size);
+ 
+-void qword_print(FILE *f, char *str);
+-void qword_printhex(FILE *f, char *str, int slen);
+-void qword_printint(FILE *f, int num);
+-int qword_eol(FILE *f);
+-int readline(int fd, char **buf, int *lenp);
+ int qword_get(char **bpp, char *dest, int bufsize);
+ int qword_get_int(char **bpp, int *anint);
+ void cache_flush(int force);
+@@ -167,13 +162,12 @@ void qword_addint(char **bpp, int *lp, int n);
+ void qword_adduint(char **bpp, int *lp, unsigned int n);
+ void qword_addeol(char **bpp, int *lp);
+ int qword_get_uint(char **bpp, unsigned int *anint);
+-void qword_printuint(FILE *f, unsigned int num);
+-void qword_printtimefrom(FILE *f, unsigned int num);
+ 
+ void closeall(int min);
+ 
+ int			svctcp_socket (u_long __number, int __reuse);
+ int			svcudp_socket (u_long __number);
++int			svcsock_nonblock (int __sock);
+ 
+ /* Misc shared code prototypes */
+ size_t  strlcat(char *, const char *, size_t);
+diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h
+index 1bfae7a..fbbdb6a 100644
+--- a/support/include/nfsrpc.h
++++ b/support/include/nfsrpc.h
+@@ -23,6 +23,7 @@
+ #ifndef __NFS_UTILS_NFSRPC_H
+ #define __NFS_UTILS_NFSRPC_H
+ 
++#include <string.h>
+ #include <rpc/types.h>
+ #include <rpc/clnt.h>
+ 
+diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c
+index 61e07a8..42e2502 100644
+--- a/support/nfs/cacheio.c
++++ b/support/nfs/cacheio.c
+@@ -18,6 +18,7 @@
+ #include <nfslib.h>
+ #include <stdio.h>
+ #include <stdio_ext.h>
++#include <string.h>
+ #include <ctype.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+@@ -120,74 +121,6 @@ void qword_addeol(char **bpp, int *lp)
+ 	(*lp)--;
+ }
+ 
+-static char qword_buf[8192];
+-void qword_print(FILE *f, char *str)
+-{
+-	char *bp = qword_buf;
+-	int len = sizeof(qword_buf);
+-	qword_add(&bp, &len, str);
+-	if (fwrite(qword_buf, bp-qword_buf, 1, f) != 1) {
+-		xlog_warn("qword_print: fwrite failed: errno %d (%s)",
+-			errno, strerror(errno));
+-	}
+-}
+-
+-void qword_printhex(FILE *f, char *str, int slen)
+-{
+-	char *bp = qword_buf;
+-	int len = sizeof(qword_buf);
+-	qword_addhex(&bp, &len, str, slen);
+-	if (fwrite(qword_buf, bp-qword_buf, 1, f) != 1) {
+-		xlog_warn("qword_printhex: fwrite failed: errno %d (%s)",
+-			errno, strerror(errno));
+-	}
+-}
+-
+-void qword_printint(FILE *f, int num)
+-{
+-	fprintf(f, "%d ", num);
+-}
+-
+-void qword_printuint(FILE *f, unsigned int num)
+-{
+-	fprintf(f, "%u ", num);
+-}
+-
+-void qword_printtimefrom(FILE *f, unsigned int num)
+-{
+-	fprintf(f, "%lu ", time(0) + num);
+-}
+-
+-int qword_eol(FILE *f)
+-{
+-	int err;
+-
+-	err = fprintf(f,"\n");
+-	if (err < 0) {
+-		xlog_warn("qword_eol: fprintf failed: errno %d (%s)",
+-			    errno, strerror(errno));
+-	} else {
+-		err = fflush(f);
+-		if (err) {
+-			xlog_warn("qword_eol: fflush failed: errno %d (%s)",
+-				  errno, strerror(errno));
+-		}
+-	}
+-	/*
+-	 * We must send one line (and one line only) in a single write
+-	 * call.  In case of a write error, libc may accumulate the
+-	 * unwritten data and try to write it again later, resulting in a
+-	 * multi-line write.  So we must explicitly ask it to throw away
+-	 * any such cached data.  But we return any original error
+-	 * indication to the caller.
+-	 */
+-	__fpurge(f);
+-	fflush(f);
+-	return err;
+-}
+-
+-
+-
+ #define isodigit(c) (isdigit(c) && c <= '7')
+ int qword_get(char **bpp, char *dest, int bufsize)
+ {
+@@ -265,48 +198,6 @@ int qword_get_uint(char **bpp, unsigned int *anint)
+ 	return 0;
+ }
+ 
+-#define READLINE_BUFFER_INCREMENT 2048
+-
+-int readline(int fd, char **buf, int *lenp)
+-{
+-	/* read a line into *buf, which is malloced *len long
+-	 * realloc if needed until we find a \n
+-	 * nul out the \n and return
+-	 * 0 on eof, 1 on success
+-	 */
+-	int len;
+-
+-	if (*lenp == 0) {
+-		char *b = malloc(READLINE_BUFFER_INCREMENT);
+-		if (b == NULL)
+-			return 0;
+-		*buf = b;
+-		*lenp = READLINE_BUFFER_INCREMENT;
+-	}
+-	len = read(fd, *buf, *lenp);
+-	if (len <= 0)
+-		return 0;
+-	while ((*buf)[len-1] != '\n') {
+-	/* now the less common case.  There was no newline,
+-	 * so we have to keep reading after re-alloc
+-	 */
+-		char *new;
+-		int nl;
+-		*lenp += READLINE_BUFFER_INCREMENT;
+-		new = realloc(*buf, *lenp);
+-		if (new == NULL)
+-			return 0;
+-		*buf = new;
+-		nl = read(fd, *buf + len, *lenp - len);
+-		if (nl <= 0)
+-			return 0;
+-		len += nl;
+-	}
+-	(*buf)[len-1] = '\0';
+-	return 1;
+-}
+-
+-
+ /* Check if we should use the new caching interface
+  * This succeeds iff the "nfsd" filesystem is mounted on
+  * /proc/fs/nfs
+diff --git a/support/nfs/nfsexport.c b/support/nfs/nfsexport.c
+index f129fd2..afd7c90 100644
+--- a/support/nfs/nfsexport.c
++++ b/support/nfs/nfsexport.c
+@@ -18,6 +18,7 @@
+ #include <fcntl.h>
+ 
+ #include "nfslib.h"
++#include "misc.h"
+ 
+ 	/* if /proc/net/rpc/... exists, then 
+ 	 * write to it, as that interface is more stable.
+@@ -32,62 +33,72 @@
+ static int
+ exp_unexp(struct nfsctl_export *exp, int export)
+ {
+-	FILE *f;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
+ 	struct stat stb;
+ 	__u32 fsid;
+ 	char fsidstr[8];
+ 	__u16 dev;
+ 	__u32 inode;
+-	int err;
++	int err = 0, f, blen;
+ 
++	f = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
++	if (f < 0) return -1;
+ 
+-	f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
+-	if (f == NULL) return -1;
+-	qword_print(f, exp->ex_client);
+-	qword_print(f, exp->ex_path);
++	bp = buf; blen = sizeof(buf);
++	qword_add(&bp, &blen, exp->ex_client);
++	qword_add(&bp, &blen, exp->ex_path);
+ 	if (export) {
+-		qword_printint(f, 0x7fffffff);
+-		qword_printint(f, exp->ex_flags);
+-		qword_printint(f, exp->ex_anon_uid);
+-		qword_printint(f, exp->ex_anon_gid);
+-		qword_printint(f, exp->ex_dev);
++		qword_addint(&bp, &blen, 0x7fffffff);
++		qword_addint(&bp, &blen, exp->ex_flags);
++		qword_addint(&bp, &blen, exp->ex_anon_uid);
++		qword_addint(&bp, &blen, exp->ex_anon_gid);
++		qword_addint(&bp, &blen, exp->ex_dev);
+ 	} else
+-		qword_printint(f, 1);
+-
+-	err = qword_eol(f);
+-	fclose(f);
++		qword_addint(&bp, &blen, 1);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
++		err = -1;
++	close(f);
+ 
+ 	if (stat(exp->ex_path, &stb) != 0)
+ 		return -1;
+-	f = fopen("/proc/net/rpc/nfsd.fh/channel", "w");
+-	if (f==NULL) return -1;
++
++	f = open("/proc/net/rpc/nfsd.fh/channel", O_WRONLY);
++	if (f < 0) return -1;
+ 	if (exp->ex_flags & NFSEXP_FSID) {
+-		qword_print(f,exp->ex_client);
+-		qword_printint(f,1);
++		bp = buf; blen = sizeof(buf);
++		qword_add(&bp, &blen, exp->ex_client);
++		qword_addint(&bp, &blen, 1);
+ 		fsid = exp->ex_dev;
+-		qword_printhex(f, (char*)&fsid, 4);
++		qword_addhex(&bp, &blen, (char*)&fsid, 4);
+ 		if (export) {
+-			qword_printint(f, 0x7fffffff);
+-			qword_print(f, exp->ex_path);
++			qword_addint(&bp, &blen, 0x7fffffff);
++			qword_add(&bp, &blen, exp->ex_path);
+ 		} else
+-			qword_printint(f, 1);
+-
+-		err = qword_eol(f) || err;
++			qword_addint(&bp, &blen, 1);
++		qword_addeol(&bp, &blen);
++		if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
++			err = -1;
+ 	}
+-	qword_print(f,exp->ex_client);
+-	qword_printint(f,0);
++
++	bp = buf; blen = sizeof(buf);
++	qword_add(&bp, &blen, exp->ex_client);
++	qword_addint(&bp, &blen, 0);
+ 	dev = htons(major(stb.st_dev)); memcpy(fsidstr, &dev, 2);
+ 	dev = htons(minor(stb.st_dev)); memcpy(fsidstr+2, &dev, 2);
+ 	inode = stb.st_ino; memcpy(fsidstr+4, &inode, 4);
+ 	
+-	qword_printhex(f, fsidstr, 8);
++	qword_addhex(&bp, &blen, fsidstr, 8);
+ 	if (export) {
+-		qword_printint(f, 0x7fffffff);
+-		qword_print(f, exp->ex_path);
++		qword_addint(&bp, &blen, 0x7fffffff);
++		qword_add(&bp, &blen, exp->ex_path);
+ 	} else
+-		qword_printint(f, 1);
+-	err = qword_eol(f) || err;
+-	fclose(f);
++		qword_addint(&bp, &blen, 1);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
++		err = -1;
++	close(f);
++
+ 	return err;
+ }
+ 
+diff --git a/support/nfs/rpcmisc.c b/support/nfs/rpcmisc.c
+index 64c98ff..ae2c0a6 100644
+--- a/support/nfs/rpcmisc.c
++++ b/support/nfs/rpcmisc.c
+@@ -104,7 +104,7 @@ makesock(int port, int proto)
+ 		return -1;
+ 	}
+ 
+-	return sock;
++	return svcsock_nonblock(sock);
+ }
+ 
+ void
+diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
+index a706f87..5cb5ff6 100644
+--- a/support/nfs/svc_create.c
++++ b/support/nfs/svc_create.c
+@@ -49,6 +49,8 @@
+ 
+ #ifdef HAVE_LIBTIRPC
+ 
++#include <rpc/rpc_com.h>
++
+ #define SVC_CREATE_XPRT_CACHE_SIZE	(8)
+ static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
+ 
+@@ -277,6 +279,12 @@ svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
+ 			"(%s, %u, %s)", name, version, nconf->nc_netid);
+ 		return 0;
+ 	}
++	if (svcsock_nonblock(xprt->xp_fd) < 0) {
++		/* close() already done by svcsock_nonblock() */
++		xprt->xp_fd = RPC_ANYFD;
++		SVC_DESTROY(xprt);
++		return 0;
++	}
+ 
+ 	if (!svc_reg(xprt, program, version, dispatch, nconf)) {
+ 		/* svc_reg(3) destroys @xprt in this case */
+@@ -332,6 +340,7 @@ svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
+ 		int fd;
+ 
+ 		fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
++		fd = svcsock_nonblock(fd);
+ 		if (fd == -1)
+ 			goto out_free;
+ 
+@@ -394,6 +403,7 @@ nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
+ 	const struct sigaction create_sigaction = {
+ 		.sa_handler	= SIG_IGN,
+ 	};
++	int maxrec = RPC_MAXDATASIZE;
+ 	unsigned int visible, up, servport;
+ 	struct netconfig *nconf;
+ 	void *handlep;
+@@ -405,6 +415,20 @@ nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
+ 	 */
+ 	(void)sigaction(SIGPIPE, &create_sigaction, NULL);
+ 
++	/*
++	 * Setting MAXREC also enables non-blocking mode for tcp connections.
++	 * This avoids DOS attacks by a client sending many requests but never
++	 * reading the reply:
++	 * - if a second request already is present for reading in the socket,
++	 *   after the first request just was read, libtirpc will break the
++	 *   connection. Thus an attacker can't simply send requests as fast as
++	 *   he can without waiting for the response.
++	 * - if the write buffer of the socket is full, the next write() will
++	 *   fail with EAGAIN. libtirpc will retry the write in a loop for max.
++	 *   2 seconds. If write still fails, the connection will be closed.
++	 */   
++	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
++
+ 	handlep = setnetconfig();
+ 	if (handlep == NULL) {
+ 		xlog(L_ERROR, "Failed to access local netconfig database: %s",
+diff --git a/support/nfs/svc_socket.c b/support/nfs/svc_socket.c
+index 74273b9..99321e7 100644
+--- a/support/nfs/svc_socket.c
++++ b/support/nfs/svc_socket.c
+@@ -76,6 +76,39 @@ int getservport(u_long number, const char *proto)
+ 	return 0;
+ }
+ 
++int
++svcsock_nonblock(int sock)
++{
++	int flags;
++
++	if (sock < 0)
++		return sock;
++
++	/* This socket might be shared among multiple processes
++	 * if mountd is run multi-threaded.  So it is safest to
++	 * make it non-blocking, else all threads might wake
++	 * one will get the data, and the others will block
++	 * indefinitely.
++	 * In all cases, transaction on this socket are atomic
++	 * (accept for TCP, packet-read and packet-write for UDP)
++	 * so O_NONBLOCK will not confuse unprepared code causing
++	 * it to corrupt messages.
++	 * It generally safest to have O_NONBLOCK when doing an accept
++	 * as if we get a RST after the SYN and before accept runs,
++	 * we can block despite being told there was an acceptable
++	 * connection.
++	 */
++	if ((flags = fcntl(sock, F_GETFL)) < 0)
++		perror(_("svc_socket: can't get socket flags"));
++	else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
++		perror(_("svc_socket: can't set socket flags"));
++	else
++		return sock;
++
++	(void) __close(sock);
++	return -1;
++}
++
+ static int
+ svc_socket (u_long number, int type, int protocol, int reuse)
+ {
+@@ -113,38 +146,7 @@ svc_socket (u_long number, int type, int protocol, int reuse)
+       sock = -1;
+     }
+ 
+-  if (sock >= 0)
+-    {
+-	    /* This socket might be shared among multiple processes
+-	     * if mountd is run multi-threaded.  So it is safest to
+-	     * make it non-blocking, else all threads might wake
+-	     * one will get the data, and the others will block
+-	     * indefinitely.
+-	     * In all cases, transaction on this socket are atomic
+-	     * (accept for TCP, packet-read and packet-write for UDP)
+-	     * so O_NONBLOCK will not confuse unprepared code causing
+-	     * it to corrupt messages.
+-	     * It generally safest to have O_NONBLOCK when doing an accept
+-	     * as if we get a RST after the SYN and before accept runs,
+-	     * we can block despite being told there was an acceptable
+-	     * connection.
+-	     */
+-	int flags;
+-	if ((flags = fcntl(sock, F_GETFL)) < 0)
+-	  {
+-	      perror (_("svc_socket: can't get socket flags"));
+-	      (void) __close (sock);
+-	      sock = -1;
+-	  }
+-	else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
+-	  {
+-	      perror (_("svc_socket: can't set socket flags"));
+-	      (void) __close (sock);
+-	      sock = -1;
+-	  }
+-    }
+-
+-  return sock;
++  return svcsock_nonblock(sock);
+ }
+ 
+ /*
+diff --git a/systemd/Makefile.am b/systemd/Makefile.am
+new file mode 100644
+index 0000000..16cf5e6
+--- /dev/null
++++ b/systemd/Makefile.am
+@@ -0,0 +1,32 @@
++## Process this file with automake to produce Makefile.in
++
++MAINTAINERCLEANFILES = Makefile.in
++
++unit_files =  \
++    nfs-blkmap.target \
++    nfs-client.target \
++    \
++    auth-rpcgss-module.service \
++    nfs-blkmap.service \
++    nfs-config.service \
++    nfs-idmapd.service \
++    nfs-mountd.service \
++    nfs-server.service \
++    nfs-utils.service \
++    rpc-gssd.service \
++    rpc-statd-notify.service \
++    rpc-statd.service \
++    rpc-svcgssd.service \
++    \
++    proc-fs-nfsd.mount \
++    var-lib-nfs-rpc_pipefs.mount
++
++EXTRA_DIST = $(unit_files)
++
++unit_dir = /usr/lib/systemd/system
++
++if INSTALL_SYSTEMD
++install-data-hook: $(unit_files)
++	mkdir -p $(DESTDIR)/$(unitdir)
++	cp $(unit_files) $(DESTDIR)/$(unitdir)
++endif
+diff --git a/systemd/auth-rpcgss-module.service b/systemd/auth-rpcgss-module.service
+index 3fc2f4a..0355e13 100644
+--- a/systemd/auth-rpcgss-module.service
++++ b/systemd/auth-rpcgss-module.service
+@@ -6,7 +6,8 @@
+ # unit will fail.  But that's OK.)
+ [Unit]
+ Description=Kernel Module supporting RPCSEC_GSS
+-Before=gssproxy.service rpc-svcgssd.service
++Before=gssproxy.service rpc-svcgssd.service rpc-gssd.service
++Wants=gssproxy.service rpc-svcgssd.service rpc-gssd.service
+ ConditionPathExists=/etc/krb5.keytab
+ 
+ [Service]
+diff --git a/systemd/nfs-blkmap.service b/systemd/nfs-blkmap.service
+index 68cc9e9..f470e3d 100644
+--- a/systemd/nfs-blkmap.service
++++ b/systemd/nfs-blkmap.service
+@@ -13,4 +13,4 @@ PartOf=nfs-utils.service
+ [Service]
+ Type=forking
+ PIDFile=/var/run/blkmapd.pid
+-ExecStart=/usr/sbin/blkmapd
++ExecStart=/usr/sbin/blkmapd $BLKMAPDARGS
+diff --git a/systemd/nfs-client.target b/systemd/nfs-client.target
+index 474f5e9..9b792a3 100644
+--- a/systemd/nfs-client.target
++++ b/systemd/nfs-client.target
+@@ -5,9 +5,12 @@ Wants=remote-fs-pre.target
+ 
+ # Note: we don't "Wants=rpc-statd.service" as "mount.nfs" will arrange to
+ # start that on demand if needed.
+-Wants=rpc-gssd.service rpc-svcgssd.service auth-rpcgss-module.service
+ Wants=nfs-blkmap.service rpc-statd-notify.service
+-Before=rpc-gssd.service rpc-svcgssd.service nfs-blkmap.service
++After=nfs-blkmap.service
++
++# GSS services dependencies and ordering
++Wants=auth-rpcgss-module.service
++After=rpc-gssd.service rpc-svcgssd.service gssproxy.service
+ 
+ [Install]
+ WantedBy=multi-user.target
+diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
+index 1048c5c..8010aad 100644
+--- a/systemd/nfs-server.service
++++ b/systemd/nfs-server.service
+@@ -2,15 +2,17 @@
+ Description=NFS server and services
+ Requires= network.target proc-fs-nfsd.mount rpcbind.target
+ Requires= nfs-mountd.service
+-Wants=rpc-statd.service nfs-idmapd.service auth-rpcgss-module.service
+-Wants=rpc-gssd.service gssproxy.service rpc-svcgssd.service
++Wants=rpc-statd.service nfs-idmapd.service
+ Wants=rpc-statd-notify.service
+ 
+ After= network.target proc-fs-nfsd.mount rpcbind.target nfs-mountd.service
+ After= nfs-idmapd.service rpc-statd.service
+-After= rpc-gssd.service gssproxy.service rpc-svcgssd.service
+ Before= rpc-statd-notify.service
+ 
++# GSS services dependencies and ordering
++Wants=auth-rpcgss-module.service
++After=rpc-gssd.service gssproxy.service rpc-svcgssd.service
++
+ Wants=nfs-config.service
+ After=nfs-config.service
+ 
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index faa8197..1f96264 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -11,3 +11,4 @@ SUBDIRS = nsm_client
+ MAINTAINERCLEANFILES = Makefile.in
+ 
+ TESTS = t0001-statd-basic-mon-unmon.sh
++EXTRA_DIST = test-lib.sh $(TESTS)
+diff --git a/tests/nsm_client/Makefile.am b/tests/nsm_client/Makefile.am
+index 4c15346..a8fc131 100644
+--- a/tests/nsm_client/Makefile.am
++++ b/tests/nsm_client/Makefile.am
+@@ -7,6 +7,7 @@ GENFILES_H	= nlm_sm_inter.h
+ 
+ GENFILES	= $(GENFILES_CLNT) $(GENFILES_SVC) $(GENFILES_XDR) $(GENFILES_H)
+ 
++EXTRA_DIST = nlm_sm_inter.x
+ 
+ check_PROGRAMS	= nsm_client
+ nsm_client_SOURCES = $(GENFILES) nsm_client.c
+diff --git a/tools/mountstats/mountstats.man b/tools/mountstats/mountstats.man
+index 0de31b7..bee3f86 100644
+--- a/tools/mountstats/mountstats.man
++++ b/tools/mountstats/mountstats.man
+@@ -1,32 +1,146 @@
+ .\"
+ .\" mountstats(8)
+ .\"
+-.TH mountstats 8 "15 Apr 2010"
++.TH mountstats 8 "12 Dec 2014"
+ .SH NAME
+-mountstats \- Displays NFS client per-mount statistics
++mountstats \- Displays various NFS client per-mount statistics
+ .SH SYNOPSIS
+-.BI "mountstats ["<options> "] " <mount_point> " [ " <mount_point> "]" 
+-.SH DESCRIPTION
+-The
+ .B mountstats
+-command displays NFS client statisitics on each given
+-.I <mount_point>
++.RB [ \-h | \-\-help ]
++.RB [ \-v | \-\-version ]
++.RB [ \-f | \-\-file
++.IR infile ]
++.RB [ \-s | \-\-since
++.IR sincefile ]
++.\" .RB [ \-n | \-\-nfs | \-r | \-\-rpc | \-R | \-\-raw ]
++.R [
++.RB [ \-n | \-\-nfs ]
++.R |
++.RB [ \-r | \-\-rpc ]
++.R |
++.RB  [ \-R | \-\-raw ]
++.R ]
++.RI [ mountpoint ] ...
++.P
++.B mountstats iostat
++.RB [ \-h | \-\-help ]
++.RB [ \-v | \-\-version ]
++.RB [ \-f | \-\-file
++.IR infile ]
++.RB [ \-s | \-\-since
++.IR sincefile ]
++.RI [ interval ]
++.RI [ count ]
++.RI [ mountpoint ] ...
++.P
++.B mounstats nfsstat
++.RB [ \-h | \-\-help ]
++.RB [ \-v | \-\-version ]
++.RB [ \-f | \-\-file
++.IR infile ]
++.RB [ \-s | \-\-since
++.IR sincefile ]
++.RB [ \-3 ]
++.RB [ \-4 ]
++.RI [ mountpoint ] ...
++.P
++.SH DESCRIPTION
++.RB "The " mountstats " command displays various NFS client statisitics for each given"
++.IR mountpoint .
++.P
++.RI "If no " mountpoint " is given, statistics will be displayed for all NFS mountpoints on the client."
++.SS Sub-commands
++Valid
++.BR mountstats (8)
++subcommands are:
++.IP "\fBmountstats\fP"
++Display a combination of per-op RPC statistics, NFS event counts, and NFS byte counts.  This is the default sub-command that will be executed if no sub-command is given.
++.IP "\fBiostat\fP"
++Display iostat-like statistics.
++.IP "\fBnfsstat\fP"
++Display nfsstat-like statistics.
+ .SH OPTIONS
++.SS Options valid for all sub-commands
++.TP
++.B \-h, \-\-help
++show the help message and exit
++.TP
++.B \-v, \-\-version
++show program's version number and exit
++.TP
++\fB\-f \fIinfile\fR, \fB\-\-file \fIinfile
++Read stats from
++.I infile
++instead of 
++.IR /proc/self/mountstats ".  " infile
++must be in the same format as 
++.IR /proc/self/mountstats .
++This may be used with the
++.BR \-S | \-\-since
++options to display the delta between two different points in time.
++This may not be used with the
++.IR interval " or " count
++options of the
++.B iostat
++sub-command.
+ .TP
+-.B " \-\-nfs
+-display only the NFS statistics
++\fB\-S \fIsincefile\fR, \fB\-\-since \fIsincefile
++Show difference between current stats and those in
++.IR sincefile ".  " sincefile
++must be in the same format as 
++.IR /proc/self/mountstats .
++This may be used with the
++.BR \-f | \-\-file
++options to display the delta between two different points in time.
++This may not be used with the
++.IR interval " or " count
++options of the
++.B iostat
++sub-command.
++.SS Options specific to the mountstats sub-command
++.B \-n, \-\-nfs
++Display only the NFS statistics
+ .TP
+-.B " \-\-rpc 
+-display only the RPC statistics
++.B \-r, \-\-rpc
++Display only the RPC statistics
+ .TP
+-.B " \-\-version 
+-display the version of this command
++.B \-R, \-\-raw
++Display only the raw statistics.  This is intended for use with the
++.BR \-f | \-\-file
++and
++.BR \-S | \-\-since
++options.
++.SS Options specific to the iostat sub-command
++.IP "\fIinterval\fP"
++Specifies the amount of time in seconds between each report.  The first report contains statistics for the time since each file system was mounted.  Each subsequent report contains statistics collected during the interval since the previous report.  This may not be used with the
++.BR \-f | \-\-file
++or
++.BR \-s | \-\-since
++options.
++.P
++.IP "\fIcount\fP"
++Determines the number of reports generated at
++.I interval
++seconds apart.  If the
++.I interval
++parameter is specified without the
++.I count
++parameter, the command generates reports continuously.  This may not be used with the
++.BR \-f | \-\-file
++or
++.BR \-s | \-\-since 
++options.
++.SS Options specific to the nfsstat sub-command
++.IP "\fB\-3\fP"
++Show only NFS version 3 statistics.  The default is to show both version 3 and version 4 statistics.
++.IP "\fB\-4\fP"
++Show only NFS version 4 statistics.  The default is to show both version 3 and version 4 statistics.
+ .SH FILES
+ .TP
+ .B /proc/self/mountstats
+ .SH SEE ALSO
+ .BR iostat (8),
+ .BR nfsiostat (8),
+-.BR nfsstat(8)
++.BR nfsstat (8)
+ .SH AUTHOR
+ Chuck Lever <chuck.lever at oracle.com>
+diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py
+index 1fb3e2f..fd73feb 100644
+--- a/tools/mountstats/mountstats.py
++++ b/tools/mountstats/mountstats.py
+@@ -24,14 +24,189 @@ MA 02110-1301 USA
+ """
+ 
+ import sys, os, time
++from operator import itemgetter, add
++try:
++    import argparse
++except ImportError:
++    print('%s:  Failed to import argparse - make sure argparse is installed!'
++        % sys.argv[0])
++    sys.exit(1)
+ 
+-Mountstats_version = '0.2'
++Mountstats_version = '0.3'
+ 
+ def difference(x, y):
+     """Used for a map() function
+     """
+     return x - y
+ 
++NfsEventCounters = [
++    'inoderevalidates',
++    'dentryrevalidates',
++    'datainvalidates',
++    'attrinvalidates',
++    'vfsopen',
++    'vfslookup',
++    'vfspermission',
++    'vfsupdatepage',
++    'vfsreadpage',
++    'vfsreadpages',
++    'vfswritepage',
++    'vfswritepages',
++    'vfsreaddir',
++    'vfssetattr',
++    'vfsflush',
++    'vfsfsync',
++    'vfslock',
++    'vfsrelease',
++    'congestionwait',
++    'setattrtrunc',
++    'extendwrite',
++    'sillyrenames',
++    'shortreads',
++    'shortwrites',
++    'delay',
++    'pnfsreads',
++    'pnfswrites'
++]
++
++NfsByteCounters = [
++    'normalreadbytes',
++    'normalwritebytes',
++    'directreadbytes',
++    'directwritebytes',
++    'serverreadbytes',
++    'serverwritebytes',
++    'readpages',
++    'writepages'
++]
++
++XprtUdpCounters = [
++    'port',
++    'bind_count',
++    'rpcsends',
++    'rpcreceives',
++    'badxids',
++    'inflightsends',
++    'backlogutil'
++]
++
++XprtTcpCounters = [
++    'port',
++    'bind_count',
++    'connect_count',
++    'connect_time',
++    'idle_time',
++    'rpcsends',
++    'rpcreceives',
++    'badxids',
++    'inflightsends',
++    'backlogutil'
++]
++
++XprtRdmaCounters = [
++    'port',
++    'bind_count',
++    'connect_count',
++    'connect_time',
++    'idle_time',
++    'rpcsends',
++    'rpcreceives',
++    'badxids',
++    'backlogutil',
++    'read_chunks',
++    'write_chunks',
++    'reply_chunks',
++    'total_rdma_req',
++    'total_rdma_rep',
++    'pullup',
++    'fixup',
++    'hardway',
++    'failed_marshal',
++    'bad_reply'
++]
++
++Nfsv3ops = [
++    'NULL',
++    'GETATTR',
++    'SETATTR',
++    'LOOKUP',
++    'ACCESS',
++    'READLINK',
++    'READ',
++    'WRITE',
++    'CREATE',
++    'MKDIR',
++    'SYMLINK',
++    'MKNOD',
++    'REMOVE',
++    'RMDIR',
++    'RENAME',
++    'LINK',
++    'READDIR',
++    'READDIRPLUS',
++    'FSSTAT',
++    'FSINFO',
++    'PATHCONF',
++    'COMMIT'
++]
++
++Nfsv4ops = [
++    'NULL',
++    'READ',
++    'WRITE',
++    'COMMIT',
++    'OPEN',
++    'OPEN_CONFIRM',
++    'OPEN_NOATTR',
++    'OPEN_DOWNGRADE',
++    'CLOSE',
++    'SETATTR',
++    'FSINFO',
++    'RENEW',
++    'SETCLIENTID',
++    'SETCLIENTID_CONFIRM',
++    'LOCK',
++    'LOCKT',
++    'LOCKU',
++    'ACCESS',
++    'GETATTR',
++    'LOOKUP',
++    'LOOKUP_ROOT',
++    'REMOVE',
++    'RENAME',
++    'LINK',
++    'SYMLINK',
++    'CREATE',
++    'PATHCONF',
++    'STATFS',
++    'READLINK',
++    'READDIR',
++    'SERVER_CAPS',
++    'DELEGRETURN',
++    'GETACL',
++    'SETACL',
++    'FS_LOCATIONS',
++    'RELEASE_LOCKOWNER',
++    'SECINFO',
++    'FSID_PRESENT',
++    'EXCHANGE_ID',
++    'CREATE_SESSION',
++    'DESTROY_SESSION',
++    'SEQUENCE',
++    'GET_LEASE_TIME',
++    'RECLAIM_COMPLETE',
++    'LAYOUTGET',
++    'GETDEVICEINFO',
++    'LAYOUTCOMMIT',
++    'LAYOUTRETURN',
++    'SECINFO_NO_NAME',
++    'TEST_STATEID',
++    'FREE_STATEID',
++    'GETDEVICELIST',
++    'BIND_CONN_TO_SESSION',
++    'DESTROY_CLIENTID'
++]
++
+ class DeviceData:
+     """DeviceData objects provide methods for parsing and displaying
+     data for a single mount grabbed from /proc/self/mountstats
+@@ -46,13 +221,13 @@ class DeviceData:
+             self.__nfs_data['export'] = words[1]
+             self.__nfs_data['mountpoint'] = words[4]
+             self.__nfs_data['fstype'] = words[7]
+-            if words[7].find('nfs') != -1:
++            if words[7].find('nfs') != -1 and words[7] != 'nfsd':
+                 self.__nfs_data['statvers'] = words[8]
+         elif 'nfs' in words or 'nfs4' in words:
+             self.__nfs_data['export'] = words[0]
+             self.__nfs_data['mountpoint'] = words[3]
+             self.__nfs_data['fstype'] = words[6]
+-            if words[6].find('nfs') != -1:
++            if words[6].find('nfs') != -1 and words[6] != 'nfsd':
+                 self.__nfs_data['statvers'] = words[7]
+         elif words[0] == 'age:':
+             self.__nfs_data['age'] = int(words[1])
+@@ -69,36 +244,18 @@ class DeviceData:
+             if self.__nfs_data['flavor'] == 6:
+                 self.__nfs_data['pseudoflavor'] = int(keys[1].split('=')[1])
+         elif words[0] == 'events:':
+-            self.__nfs_data['inoderevalidates'] = int(words[1])
+-            self.__nfs_data['dentryrevalidates'] = int(words[2])
+-            self.__nfs_data['datainvalidates'] = int(words[3])
+-            self.__nfs_data['attrinvalidates'] = int(words[4])
+-            self.__nfs_data['syncinodes'] = int(words[5])
+-            self.__nfs_data['vfsopen'] = int(words[6])
+-            self.__nfs_data['vfslookup'] = int(words[7])
+-            self.__nfs_data['vfspermission'] = int(words[8])
+-            self.__nfs_data['vfsreadpage'] = int(words[9])
+-            self.__nfs_data['vfsreadpages'] = int(words[10])
+-            self.__nfs_data['vfswritepage'] = int(words[11])
+-            self.__nfs_data['vfswritepages'] = int(words[12])
+-            self.__nfs_data['vfsreaddir'] = int(words[13])
+-            self.__nfs_data['vfsflush'] = int(words[14])
+-            self.__nfs_data['vfsfsync'] = int(words[15])
+-            self.__nfs_data['vfslock'] = int(words[16])
+-            self.__nfs_data['vfsrelease'] = int(words[17])
+-            self.__nfs_data['setattrtrunc'] = int(words[18])
+-            self.__nfs_data['extendwrite'] = int(words[19])
+-            self.__nfs_data['sillyrenames'] = int(words[20])
+-            self.__nfs_data['shortreads'] = int(words[21])
+-            self.__nfs_data['shortwrites'] = int(words[22])
+-            self.__nfs_data['delay'] = int(words[23])
++            i = 1
++            for key in NfsEventCounters:
++                try:
++                    self.__nfs_data[key] = int(words[i])
++                except IndexError as err:
++                    self.__nfs_data[key] = 0
++                i += 1
+         elif words[0] == 'bytes:':
+-            self.__nfs_data['normalreadbytes'] = int(words[1])
+-            self.__nfs_data['normalwritebytes'] = int(words[2])
+-            self.__nfs_data['directreadbytes'] = int(words[3])
+-            self.__nfs_data['directwritebytes'] = int(words[4])
+-            self.__nfs_data['serverreadbytes'] = int(words[5])
+-            self.__nfs_data['serverwritebytes'] = int(words[6])
++            i = 1
++            for key in NfsByteCounters:
++                self.__nfs_data[key] = int(words[i])
++                i += 1
+ 
+     def __parse_rpc_line(self, words):
+         if words[0] == 'RPC':
+@@ -107,44 +264,20 @@ class DeviceData:
+         elif words[0] == 'xprt:':
+             self.__rpc_data['protocol'] = words[1]
+             if words[1] == 'udp':
+-                self.__rpc_data['port'] = int(words[2])
+-                self.__rpc_data['bind_count'] = int(words[3])
+-                self.__rpc_data['rpcsends'] = int(words[4])
+-                self.__rpc_data['rpcreceives'] = int(words[5])
+-                self.__rpc_data['badxids'] = int(words[6])
+-                self.__rpc_data['inflightsends'] = int(words[7])
+-                self.__rpc_data['backlogutil'] = int(words[8])
++                i = 2
++                for key in XprtUdpCounters:
++                    self.__rpc_data[key] = int(words[i])
++                    i += 1
+             elif words[1] == 'tcp':
+-                self.__rpc_data['port'] = words[2]
+-                self.__rpc_data['bind_count'] = int(words[3])
+-                self.__rpc_data['connect_count'] = int(words[4])
+-                self.__rpc_data['connect_time'] = int(words[5])
+-                self.__rpc_data['idle_time'] = int(words[6])
+-                self.__rpc_data['rpcsends'] = int(words[7])
+-                self.__rpc_data['rpcreceives'] = int(words[8])
+-                self.__rpc_data['badxids'] = int(words[9])
+-                self.__rpc_data['inflightsends'] = int(words[10])
+-                self.__rpc_data['backlogutil'] = int(words[11])
++                i = 2
++                for key in XprtTcpCounters:
++                    self.__rpc_data[key] = int(words[i])
++                    i += 1
+             elif words[1] == 'rdma':
+-                self.__rpc_data['port'] = words[2]
+-                self.__rpc_data['bind_count'] = int(words[3])
+-                self.__rpc_data['connect_count'] = int(words[4])
+-                self.__rpc_data['connect_time'] = int(words[5])
+-                self.__rpc_data['idle_time'] = int(words[6])
+-                self.__rpc_data['rpcsends'] = int(words[7])
+-                self.__rpc_data['rpcreceives'] = int(words[8])
+-                self.__rpc_data['badxids'] = int(words[9])
+-                self.__rpc_data['backlogutil'] = int(words[10])
+-                self.__rpc_data['read_chunks'] = int(words[11])
+-                self.__rpc_data['write_chunks'] = int(words[12])
+-                self.__rpc_data['reply_chunks'] = int(words[13])
+-                self.__rpc_data['total_rdma_req'] = int(words[14])
+-                self.__rpc_data['total_rdma_rep'] = int(words[15])
+-                self.__rpc_data['pullup'] = int(words[16])
+-                self.__rpc_data['fixup'] = int(words[17])
+-                self.__rpc_data['hardway'] = int(words[18])
+-                self.__rpc_data['failed_marshal'] = int(words[19])
+-                self.__rpc_data['bad_reply'] = int(words[20])
++                i = 2
++                for key in XprtRdmaCounters:
++                    self.__rpc_data[key] = int(words[i])
++                    i += 1
+         elif words[0] == 'per-op':
+             self.__rpc_data['per-op'] = words
+         else:
+@@ -178,12 +311,55 @@ class DeviceData:
+             return True
+         return False
+ 
+-    def display_nfs_options(self):
+-        """Pretty-print the NFS options
++    def nfs_version(self):
++        if self.is_nfs_mountpoint():
++            prog, vers = self.__rpc_data['programversion'].split('/')
++            return int(vers)
++
++    def display_raw_stats(self):
++        """Prints out stats in the same format as /proc/self/mountstats
+         """
++        print('device %s mounted on %s with fstype %s %s' % \
++            (self.__nfs_data['export'], self.__nfs_data['mountpoint'], \
++            self.__nfs_data['fstype'], self.__nfs_data['statvers']))
++        print('\topts:\t%s' % ','.join(self.__nfs_data['mountoptions']))
++        print('\tage:\t%d' % self.__nfs_data['age'])
++        print('\tcaps:\t%s' % ','.join(self.__nfs_data['servercapabilities']))
++        print('\tsec:\tflavor=%d,pseudoflavor=%d' % (self.__nfs_data['flavor'], \
++            self.__nfs_data['pseudoflavor']))
++        print('\tevents:\t%s' % " ".join([str(self.__nfs_data[key]) for key in NfsEventCounters]))
++        print('\tbytes:\t%s' % " ".join([str(self.__nfs_data[key]) for key in NfsByteCounters]))
++        print('\tRPC iostats version: %1.1f p/v: %s (nfs)' % (self.__rpc_data['statsvers'], \
++            self.__rpc_data['programversion']))
++        if self.__rpc_data['protocol'] == 'udp':
++            print('\txprt:\tudp %s' % " ".join([str(self.__rpc_data[key]) for key in XprtUdpCounters]))
++        elif self.__rpc_data['protocol'] == 'tcp':
++            print('\txprt:\ttcp %s' % " ".join([str(self.__rpc_data[key]) for key in XprtTcpCounters]))
++        elif self.__rpc_data['protocol'] == 'rdma':
++            print('\txprt:\trdma %s' % " ".join([str(self.__rpc_data[key]) for key in XprtRdmaCounters]))
++        else:
++            raise Exception('Unknown RPC transport protocol %s' % self.__rpc_data['protocol'])
++        print('\tper-op statistics')
++        prog, vers = self.__rpc_data['programversion'].split('/')
++        if vers == '3':
++            for op in Nfsv3ops:
++                print('\t%12s: %s' % (op, " ".join(str(x) for x in self.__rpc_data[op])))
++        elif vers == '4':
++            for op in Nfsv4ops:
++                print('\t%12s: %s' % (op, " ".join(str(x) for x in self.__rpc_data[op])))
++        else:
++            print('\tnot implemented for version %d' % vers)
++        print()
++
++    def display_stats_header(self):
+         print('Stats for %s mounted on %s:' % \
+             (self.__nfs_data['export'], self.__nfs_data['mountpoint']))
+ 
++    def display_nfs_options(self):
++        """Pretty-print the NFS options
++        """
++        self.display_stats_header()
++
+         print('  NFS mount options: %s' % ','.join(self.__nfs_data['mountoptions']))
+         print('  NFS server capabilities: %s' % ','.join(self.__nfs_data['servercapabilities']))
+         if 'nfsv4flags' in self.__nfs_data:
+@@ -201,7 +377,6 @@ class DeviceData:
+         print('Cache events:')
+         print('  data cache invalidated %d times' % self.__nfs_data['datainvalidates'])
+         print('  attribute cache invalidated %d times' % self.__nfs_data['attrinvalidates'])
+-        print('  inodes synced %d times' % self.__nfs_data['syncinodes'])
+         print()
+         print('VFS calls:')
+         print('  VFS requested %d inode revalidations' % self.__nfs_data['inoderevalidates'])
+@@ -262,29 +437,82 @@ class DeviceData:
+         """
+         sends = self.__rpc_data['rpcsends']
+ 
+-        # XXX: these should be sorted by 'count'
+-        print()
++        allstats = []
+         for op in self.__rpc_data['ops']:
+-            stats = self.__rpc_data[op]
+-            count = stats[0]
+-            retrans = stats[1] - count
++            allstats.append([op] + self.__rpc_data[op])
++
++        print()
++        for stats in sorted(allstats, key=itemgetter(1), reverse=True):
++            count = stats[1]
+             if count != 0:
+-                print('%s:' % op)
++                print('%s:' % stats[0])
+                 print('\t%d ops (%d%%)' % \
+                     (count, ((count * 100) / sends)), end=' ')
+-                print('\t%d retrans (%d%%)' % (retrans, ((retrans * 100) / count)), end=' ')
+-                print('\t%d major timeouts' % stats[2])
++                retrans = stats[2] - count
++                if retrans != 0:
++                    print('\t%d retrans (%d%%)' % (retrans, ((retrans * 100) / count)), end=' ')
++                    print('\t%d major timeouts' % stats[3])
++                else:
++                    print('')
+                 print('\tavg bytes sent per op: %d\tavg bytes received per op: %d' % \
+-                    (stats[3] / count, stats[4] / count))
+-                print('\tbacklog wait: %f' % (float(stats[5]) / count), end=' ')
+-                print('\tRTT: %f' % (float(stats[6]) / count), end=' ')
++                    (stats[4] / count, stats[5] / count))
++                print('\tbacklog wait: %f' % (float(stats[6]) / count), end=' ')
++                print('\tRTT: %f' % (float(stats[7]) / count), end=' ')
+                 print('\ttotal execute time: %f (milliseconds)' % \
+-                    (float(stats[7]) / count))
++                    (float(stats[8]) / count))
++
++    def client_rpc_stats(self):
++        """Tally high-level rpc stats for the nfsstat command
++        """
++        sends = 0
++        trans = 0
++        authrefrsh = 0
++        for op in self.__rpc_data['ops']:
++            sends += self.__rpc_data[op][0]
++            trans += self.__rpc_data[op][1]
++        retrans = trans - sends
++        # authrefresh stats don't actually get captured in
++        # /proc/self/mountstats, so we fudge it here
++        authrefrsh = sends
++        return (sends, trans, authrefrsh)
++
++    def display_nfsstat_stats(self):
++        """Pretty-print nfsstat-style stats
++        """
++        sends = 0
++        for op in self.__rpc_data['ops']:
++            sends += self.__rpc_data[op][0]
++        if sends == 0:
++            return
++        print()
++        vers = self.nfs_version()
++        print('Client nfs v%d' % vers)
++        info = []
++        for op in self.__rpc_data['ops']:
++            print('%-13s' % str.lower(op)[:12], end='')
++            count = self.__rpc_data[op][0]
++            pct = (count * 100) / sends
++            info.append((count, pct))
++            if (self.__rpc_data['ops'].index(op) + 1) % 6 == 0:
++                print()
++                for (count, pct) in info:
++                    print('%-8u%3u%% ' % (count, pct), end='')
++                print()
++                info = []
++        print()
++        if len(info) > 0:
++            for (count, pct) in info:
++                print('%-8u%3u%% ' % (count, pct), end='')
++            print()
+ 
+     def compare_iostats(self, old_stats):
+         """Return the difference between two sets of stats
+         """
++        if old_stats.__nfs_data['age'] > self.__nfs_data['age']:
++            return self
++
+         result = DeviceData()
++        protocol = self.__rpc_data['protocol']
+ 
+         # copy self into result
+         for key, value in self.__nfs_data.items():
+@@ -299,70 +527,118 @@ class DeviceData:
+         for op in result.__rpc_data['ops']:
+             result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op]))
+ 
+-        # update the remaining keys we care about
+-        result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends']
+-        result.__rpc_data['backlogutil'] -= old_stats.__rpc_data['backlogutil']
+-        result.__nfs_data['serverreadbytes'] -= old_stats.__nfs_data['serverreadbytes']
+-        result.__nfs_data['serverwritebytes'] -= old_stats.__nfs_data['serverwritebytes']
+-
++        # update the remaining keys
++        if protocol == 'udp':
++            for key in XprtUdpCounters:
++                result.__rpc_data[key] -= old_stats.__rpc_data[key]
++        elif protocol == 'tcp':
++            for key in XprtTcpCounters:
++                result.__rpc_data[key] -= old_stats.__rpc_data[key]
++        elif protocol == 'rdma':
++            for key in XprtRdmaCounters:
++                result.__rpc_data[key] -= old_stats.__rpc_data[key]
++        result.__nfs_data['age'] -= old_stats.__nfs_data['age']
++        for key in NfsEventCounters:
++            result.__nfs_data[key] -= old_stats.__nfs_data[key]
++        for key in NfsByteCounters:
++            result.__nfs_data[key] -= old_stats.__nfs_data[key]
+         return result
+ 
++    def setup_accumulator(self, ops):
++        """Initialize a DeviceData instance to tally stats for all mountpoints
++        with the same major version. This is for the nfsstat command.
++        """
++        if ops == Nfsv3ops:
++            self.__rpc_data['programversion'] = '100003/3'
++            self.__nfs_data['fstype'] = 'nfs'
++        elif ops == Nfsv4ops:
++            self.__rpc_data['programversion'] = '100003/4'
++            self.__nfs_data['fstype'] = 'nfs4'
++        self.__rpc_data['ops'] = ops
++        for op in ops:
++            self.__rpc_data[op] = [0 for i in range(8)]
++
++    def accumulate_iostats(self, new_stats):
++        """Accumulate counters from all RPC op buckets in new_stats.  This is
++        for the nfsstat command.
++        """
++        for op in new_stats.__rpc_data['ops']:
++            self.__rpc_data[op] = list(map(add, self.__rpc_data[op], new_stats.__rpc_data[op]))
++
++    def __print_rpc_op_stats(self, op, sample_time):
++        """Print generic stats for one RPC op
++        """
++        if op not in self.__rpc_data:
++            return
++
++        rpc_stats = self.__rpc_data[op]
++        ops = float(rpc_stats[0])
++        retrans = float(rpc_stats[1] - rpc_stats[0])
++        kilobytes = float(rpc_stats[3] + rpc_stats[4]) / 1024
++        rtt = float(rpc_stats[6])
++        exe = float(rpc_stats[7])
++
++        # prevent floating point exceptions
++        if ops != 0:
++            kb_per_op = kilobytes / ops
++            retrans_percent = (retrans * 100) / ops
++            rtt_per_op = rtt / ops
++            exe_per_op = exe / ops
++        else:
++            kb_per_op = 0.0
++            retrans_percent = 0.0
++            rtt_per_op = 0.0
++            exe_per_op = 0.0
++
++        op += ':'
++        print(format(op.lower(), '<16s'), end='')
++        print(format('ops/s', '>8s'), end='')
++        print(format('kB/s', '>16s'), end='')
++        print(format('kB/op', '>16s'), end='')
++        print(format('retrans', '>16s'), end='')
++        print(format('avg RTT (ms)', '>16s'), end='')
++        print(format('avg exe (ms)', '>16s'))
++
++        print(format((ops / sample_time), '>24.3f'), end='')
++        print(format((kilobytes / sample_time), '>16.3f'), end='')
++        print(format(kb_per_op, '>16.3f'), end='')
++        retransmits = '{0:>10.0f} ({1:>3.1f}%)'.format(retrans, retrans_percent).strip()
++        print(format(retransmits, '>16'), end='')
++        print(format(rtt_per_op, '>16.3f'), end='')
++        print(format(exe_per_op, '>16.3f'))
++
+     def display_iostats(self, sample_time):
+         """Display NFS and RPC stats in an iostat-like way
+         """
+         sends = float(self.__rpc_data['rpcsends'])
+         if sample_time == 0:
+             sample_time = float(self.__nfs_data['age'])
++        #  sample_time could still be zero if the export was just mounted.
++        #  Set it to 1 to avoid divide by zero errors in this case since we'll
++        #  likely still have relevant mount statistics to show.
++        #
++        if sample_time == 0:
++            sample_time = 1;
++        if sends != 0:
++            backlog = (float(self.__rpc_data['backlogutil']) / sends) / sample_time
++        else:
++            backlog = 0.0
+ 
+         print()
+         print('%s mounted on %s:' % \
+             (self.__nfs_data['export'], self.__nfs_data['mountpoint']))
++        print()
+ 
+-        print('\top/s\trpc bklog')
+-        print('\t%.2f' % (sends / sample_time), end=' ')
+-        if sends != 0:
+-            print('\t%.2f' % \
+-                ((float(self.__rpc_data['backlogutil']) / sends) / sample_time))
+-        else:
+-            print('\t0.00')
+-
+-        # reads:  ops/s, kB/s, avg rtt, and avg exe
+-        # XXX: include avg xfer size and retransmits?
+-        read_rpc_stats = self.__rpc_data['READ']
+-        ops = float(read_rpc_stats[0])
+-        kilobytes = float(self.__nfs_data['serverreadbytes']) / 1024
+-        rtt = float(read_rpc_stats[6])
+-        exe = float(read_rpc_stats[7])
+-
+-        print('\treads:\tops/s\t\tkB/s\t\tavg RTT (ms)\tavg exe (ms)')
+-        print('\t\t%.2f' % (ops / sample_time), end=' ')
+-        print('\t\t%.2f' % (kilobytes / sample_time), end=' ')
+-        if ops != 0:
+-            print('\t\t%.2f' % (rtt / ops), end=' ')
+-            print('\t\t%.2f' % (exe / ops))
+-        else:
+-            print('\t\t0.00', end=' ')
+-            print('\t\t0.00')
+-
+-        # writes:  ops/s, kB/s, avg rtt, and avg exe
+-        # XXX: include avg xfer size and retransmits?
+-        write_rpc_stats = self.__rpc_data['WRITE']
+-        ops = float(write_rpc_stats[0])
+-        kilobytes = float(self.__nfs_data['serverwritebytes']) / 1024
+-        rtt = float(write_rpc_stats[6])
+-        exe = float(write_rpc_stats[7])
+-
+-        print('\twrites:\tops/s\t\tkB/s\t\tavg RTT (ms)\tavg exe (ms)')
+-        print('\t\t%.2f' % (ops / sample_time), end=' ')
+-        print('\t\t%.2f' % (kilobytes / sample_time), end=' ')
+-        if ops != 0:
+-            print('\t\t%.2f' % (rtt / ops), end=' ')
+-            print('\t\t%.2f' % (exe / ops))
+-        else:
+-            print('\t\t0.00', end=' ')
+-            print('\t\t0.00')
++        print(format('ops/s', '>16') + format('rpc bklog', '>16'))
++        print(format((sends / sample_time), '>16.3f'), end='')
++        print(format(backlog, '>16.3f'))
++        print()
+ 
+-def parse_stats_file(filename):
++        self.__print_rpc_op_stats('READ', sample_time)
++        self.__print_rpc_op_stats('WRITE', sample_time)
++        sys.stdout.flush()
++
++def parse_stats_file(f):
+     """pop the contents of a mountstats file into a dictionary,
+     keyed by mount point.  each value object is a list of the
+     lines in the mountstats file corresponding to the mount
+@@ -371,7 +647,7 @@ def parse_stats_file(filename):
+     ms_dict = dict()
+     key = ''
+ 
+-    f = open(filename)
++    f.seek(0)
+     for line in f.readlines():
+         words = line.split()
+         if len(words) == 0:
+@@ -385,132 +661,154 @@ def parse_stats_file(filename):
+         else:
+             new += [ line.strip() ]
+         ms_dict[key] = new
+-    f.close
+ 
+     return ms_dict
+ 
+-def print_mountstats_help(name):
+-    print('usage: %s [ options ] <mount point>' % name)
+-    print()
+-    print(' Version %s' % Mountstats_version)
+-    print()
+-    print(' Display NFS client per-mount statistics.')
+-    print()
+-    print('  --version    display the version of this command')
+-    print('  --nfs        display only the NFS statistics')
+-    print('  --rpc        display only the RPC statistics')
+-    print('  --start      sample and save statistics')
+-    print('  --end        resample statistics and compare them with saved')
++def print_mountstats(stats, nfs_only, rpc_only, raw):
++    if nfs_only:
++       stats.display_nfs_options()
++       stats.display_nfs_events()
++       stats.display_nfs_bytes()
++    elif rpc_only:
++       stats.display_stats_header()
++       stats.display_rpc_generic_stats()
++       stats.display_rpc_op_stats()
++    elif raw:
++       stats.display_raw_stats()
++    else:
++       stats.display_nfs_options()
++       stats.display_nfs_bytes()
++       stats.display_rpc_generic_stats()
++       stats.display_rpc_op_stats()
+     print()
+ 
+-def mountstats_command():
++def mountstats_command(args):
+     """Mountstats command
+     """
+-    mountpoints = []
+-    nfs_only = False
+-    rpc_only = False
+-
+-    for arg in sys.argv:
+-        if arg in ['-h', '--help', 'help', 'usage']:
+-            print_mountstats_help(prog)
+-            return
+-
+-        if arg in ['-v', '--version', 'version']:
+-            print('%s version %s' % (sys.argv[0], Mountstats_version))
+-            sys.exit(0)
+-
+-        if arg in ['-n', '--nfs']:
+-            nfs_only = True
+-            continue
+-
+-        if arg in ['-r', '--rpc']:
+-            rpc_only = True
+-            continue
++    mountstats = parse_stats_file(args.infile)
++    mountpoints = args.mountpoints
+ 
+-        if arg in ['-s', '--start']:
+-            raise Exception('Sampling is not yet implemented')
++    # make certain devices contains only NFS mount points
++    if len(mountpoints) > 0:
++        check = []
++        for device in mountpoints:
++            stats = DeviceData()
++            try:
++                stats.parse_stats(mountstats[device])
++                if stats.is_nfs_mountpoint():
++                    check += [device]
++            except KeyError:
++                continue
++        mountpoints = check
++    else:
++        for device, descr in mountstats.items():
++            stats = DeviceData()
++            stats.parse_stats(descr)
++            if stats.is_nfs_mountpoint():
++                mountpoints += [device]
++    if len(mountpoints) == 0:
++        print('No NFS mount points were found')
++        return
+ 
+-        if arg in ['-e', '--end']:
+-            raise Exception('Sampling is not yet implemented')
++    if args.since:
++        old_mountstats = parse_stats_file(args.since)
+ 
+-        if arg == sys.argv[0]:
+-            continue
++    for mp in mountpoints:
++        stats = DeviceData()
++        stats.parse_stats(mountstats[mp])
++        if not args.since:
++            print_mountstats(stats, args.nfs_only, args.rpc_only, args.raw)
++        elif args.since and mp not in old_mountstats:
++            print_mountstats(stats, args.nfs_only, args.rpc_only, args.raw)
++        else:
++            old_stats = DeviceData()
++            old_stats.parse_stats(old_mountstats[mp])
++            diff_stats = stats.compare_iostats(old_stats)
++            print_mountstats(diff_stats, args.nfs_only, args.rpc_only, args.raw)
+ 
+-        mountpoints += [arg]
++    args.infile.close()
++    if args.since:
++        args.since.close()
+ 
+-    if mountpoints == []:
+-        print_mountstats_help(prog)
+-        return
++def nfsstat_command(args):
++    """nfsstat-like command for NFS mount points
++    """
++    mountstats = parse_stats_file(args.infile)
++    mountpoints = args.mountpoints
++    v3stats = DeviceData()
++    v3stats.setup_accumulator(Nfsv3ops)
++    v4stats = DeviceData()
++    v4stats.setup_accumulator(Nfsv4ops)
++
++    # ensure stats get printed if neither v3 nor v4 was specified
++    if args.show_v3 or args.show_v4:
++        show_both = False
++    else:
++        show_both = True
+ 
+-    if rpc_only == True and nfs_only == True:
+-        print_mountstats_help(prog)
++    # make certain devices contains only NFS mount points
++    if len(mountpoints) > 0:
++        check = []
++        for device in mountpoints:
++            stats = DeviceData()
++            try:
++                stats.parse_stats(mountstats[device])
++                if stats.is_nfs_mountpoint():
++                    check += [device]
++            except KeyError:
++                continue
++        mountpoints = check
++    else:
++        for device, descr in mountstats.items():
++            stats = DeviceData()
++            stats.parse_stats(descr)
++            if stats.is_nfs_mountpoint():
++                mountpoints += [device]
++    if len(mountpoints) == 0:
++        print('No NFS mount points were found')
+         return
+ 
+-    mountstats = parse_stats_file('/proc/self/mountstats')
++    if args.since:
++        old_mountstats = parse_stats_file(args.since)
+ 
+     for mp in mountpoints:
+-        if mp not in mountstats:
+-            print('Statistics for mount point %s not found' % mp)
+-            continue
+-
+         stats = DeviceData()
+         stats.parse_stats(mountstats[mp])
++        vers = stats.nfs_version()
+ 
+-        if not stats.is_nfs_mountpoint():
+-            print('Mount point %s exists but is not an NFS mount' % mp)
+-            continue
+-
+-        if nfs_only:
+-           stats.display_nfs_options()
+-           stats.display_nfs_events()
+-           stats.display_nfs_bytes()
+-        elif rpc_only:
+-           stats.display_rpc_generic_stats()
+-           stats.display_rpc_op_stats()
++        if not args.since:
++            acc_stats = stats
++        elif args.since and mp not in old_mountstats:
++            acc_stats = stats
+         else:
+-           stats.display_nfs_options()
+-           stats.display_nfs_bytes()
+-           stats.display_rpc_generic_stats()
+-           stats.display_rpc_op_stats()
++            old_stats = DeviceData()
++            old_stats.parse_stats(old_mountstats[mp])
++            acc_stats = stats.compare_iostats(old_stats)
+ 
+-def print_nfsstat_help(name):
+-    print('usage: %s [ options ]' % name)
+-    print()
+-    print(' Version %s' % Mountstats_version)
+-    print()
+-    print(' nfsstat-like program that uses NFS client per-mount statistics.')
+-    print()
++        if vers == 3 and (show_both or args.show_v3):
++           v3stats.accumulate_iostats(acc_stats)
++        elif vers == 4 and (show_both or args.show_v4):
++           v4stats.accumulate_iostats(acc_stats)
+ 
+-def nfsstat_command():
+-    print_nfsstat_help(prog)
++    sends, retrans, authrefrsh = map(add, v3stats.client_rpc_stats(), v4stats.client_rpc_stats())
++    print('Client rpc stats:')
++    print('calls      retrans    authrefrsh')
++    print('%-11u%-11u%-11u' % (sends, retrans, authrefrsh))
+ 
+-def print_iostat_help(name):
+-    print('usage: %s [ <interval> [ <count> ] ] [ <mount point> ] ' % name)
+-    print()
+-    print(' Version %s' % Mountstats_version)
+-    print()
+-    print(' iostat-like program to display NFS client per-mount statistics.')
+-    print()
+-    print(' The <interval> parameter specifies the amount of time in seconds between')
+-    print(' each report.  The first report contains statistics for the time since each')
+-    print(' file system was mounted.  Each subsequent report contains statistics')
+-    print(' collected during the interval since the previous report.')
+-    print()
+-    print(' If the <count> parameter is specified, the value of <count> determines the')
+-    print(' number of reports generated at <interval> seconds apart.  If the interval')
+-    print(' parameter is specified without the <count> parameter, the command generates')
+-    print(' reports continuously.')
+-    print()
+-    print(' If one or more <mount point> names are specified, statistics for only these')
+-    print(' mount points will be displayed.  Otherwise, all NFS mount points on the')
+-    print(' client are listed.')
+-    print()
++    if show_both or args.show_v3:
++        v3stats.display_nfsstat_stats()
++    if show_both or args.show_v4:
++        v4stats.display_nfsstat_stats()
++
++    args.infile.close()
++    if args.since:
++        args.since.close()
+ 
+ def print_iostat_summary(old, new, devices, time):
+     for device in devices:
+         stats = DeviceData()
+         stats.parse_stats(new[device])
+-        if not old:
++        if not old or device not in old:
+             stats.display_iostats(time)
+         else:
+             old_stats = DeviceData()
+@@ -518,51 +816,28 @@ def print_iostat_summary(old, new, devices, time):
+             diff_stats = stats.compare_iostats(old_stats)
+             diff_stats.display_iostats(time)
+ 
+-def iostat_command():
++def iostat_command(args):
+     """iostat-like command for NFS mount points
+     """
+-    mountstats = parse_stats_file('/proc/self/mountstats')
+-    devices = []
+-    interval_seen = False
+-    count_seen = False
+-
+-    for arg in sys.argv:
+-        if arg in ['-h', '--help', 'help', 'usage']:
+-            print_iostat_help(prog)
+-            return
+-
+-        if arg in ['-v', '--version', 'version']:
+-            print('%s version %s' % (sys.argv[0], Mountstats_version))
+-            return
+-
+-        if arg == sys.argv[0]:
+-            continue
++    mountstats = parse_stats_file(args.infile)
++    devices = args.mountpoints
+ 
+-        if arg in mountstats:
+-            devices += [arg]
+-        elif not interval_seen:
+-            interval = int(arg)
+-            if interval > 0:
+-                interval_seen = True
+-            else:
+-                print('Illegal <interval> value')
+-                return
+-        elif not count_seen:
+-            count = int(arg)
+-            if count > 0:
+-                count_seen = True
+-            else:
+-                print('Illegal <count> value')
+-                return
++    if args.since:
++        old_mountstats = parse_stats_file(args.since)
++    else:
++        old_mountstats = None
+ 
+     # make certain devices contains only NFS mount points
+     if len(devices) > 0:
+         check = []
+         for device in devices:
+             stats = DeviceData()
+-            stats.parse_stats(mountstats[device])
+-            if stats.is_nfs_mountpoint():
+-                check += [device]
++            try:
++                stats.parse_stats(mountstats[device])
++                if stats.is_nfs_mountpoint():
++                    check += [device]
++            except KeyError:
++                continue
+         devices = check
+     else:
+         for device, descr in mountstats.items():
+@@ -574,43 +849,145 @@ def iostat_command():
+         print('No NFS mount points were found')
+         return
+ 
+-    old_mountstats = None
+     sample_time = 0
+ 
+-    if not interval_seen:
++    if args.interval is None:
+         print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
+         return
+ 
+-    if count_seen:
++    if args.count is not None:
++        count = args.count
+         while count != 0:
+             print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
+             old_mountstats = mountstats
+-            time.sleep(interval)
+-            sample_time = interval
+-            mountstats = parse_stats_file('/proc/self/mountstats')
++            time.sleep(args.interval)
++            sample_time = args.interval
++            mountstats = parse_stats_file(args.infile)
+             count -= 1
+     else: 
+         while True:
+             print_iostat_summary(old_mountstats, mountstats, devices, sample_time)
+             old_mountstats = mountstats
+-            time.sleep(interval)
+-            sample_time = interval
+-            mountstats = parse_stats_file('/proc/self/mountstats')
++            time.sleep(args.interval)
++            sample_time = args.interval
++            mountstats = parse_stats_file(args.infile)
+ 
+-#
+-# Main
+-#
+-prog = os.path.basename(sys.argv[0])
++    args.infile.close()
++    if args.since:
++        args.since.close()
++
++class ICMAction(argparse.Action):
++    """Custom action to deal with interval, count, and mountpoints.
++    """
++    def __call__(self, parser, namespace, values, option_string=None):
++        if namespace.mountpoints is None:
++            namespace.mountpoints = []
++        if values is None:
++            return
++        elif (type(values) == type([])):
++            for value in values:
++                self._handle_one(namespace, value)
++        else:
++            self._handle_one(namespace, values)
++
++    def _handle_one(self, namespace, value):
++        try:
++            intval = int(value)
++            if namespace.infile.name != '/proc/self/mountstats':
++                raise argparse.ArgumentError(self, "not allowed with argument -f/--file or -S/--since")
++            self._handle_int(namespace, intval)
++        except ValueError:
++            namespace.mountpoints.append(value)
++
++    def _handle_int(self, namespace, value):
++        if namespace.interval is None:
++            namespace.interval = value
++        elif namespace.count is None:
++            namespace.count = value
++        else:
++            raise argparse.ArgumentError(self, "too many integer arguments")
++
++def main():
++    parser = argparse.ArgumentParser(epilog='For specific sub-command help, '
++        'run \'mountstats SUB-COMMAND -h|--help\'')
++    subparsers = parser.add_subparsers(help='sub-command help')
++
++    common_parser = argparse.ArgumentParser(add_help=False)
++    common_parser.add_argument('-v', '--version', action='version',
++        version='mountstats ' + Mountstats_version)
++    common_parser.add_argument('-f', '--file', default=open('/proc/self/mountstats', 'r'),
++        type=argparse.FileType('r'), dest='infile',
++        help='Read stats from %(dest)s instead of /proc/self/mountstats')
++    common_parser.add_argument('-S', '--since', type=argparse.FileType('r'),
++        metavar='SINCEFILE',
++        help='Show difference between current stats and those in SINCEFILE')
++
++    mountstats_parser = subparsers.add_parser('mountstats',
++        parents=[common_parser],
++        help='Display a combination of per-op RPC statistics, NFS event counts, and NFS byte counts. '
++            'This is the default sub-command if no sub-command is given.')
++    group = mountstats_parser.add_mutually_exclusive_group()
++    group.add_argument('-n', '--nfs', action='store_true', dest='nfs_only',
++        help='Display only the NFS statistics')
++    group.add_argument('-r', '--rpc', action='store_true', dest='rpc_only',
++        help='Display only the RPC statistics')
++    group.add_argument('-R', '--raw', action='store_true',
++        help='Display only the raw statistics')
++    # The mountpoints argument cannot be moved into the common_parser because
++    # it will screw up the parsing of the iostat arguments (interval and count)
++    mountstats_parser.add_argument('mountpoints', nargs='*', metavar='mountpoint',
++        help='Display statistics for this mountpoint. More than one may be specified. '
++            'If absent, statistics for all NFS mountpoints will be generated.')
++    mountstats_parser.set_defaults(func=mountstats_command)
++
++    nfsstat_parser = subparsers.add_parser('nfsstat',
++        parents=[common_parser],
++        help='Display nfsstat-like statistics.')
++    nfsstat_parser.add_argument('-3', action='store_true', dest='show_v3',
++        help='Show NFS version 3 statistics')
++    nfsstat_parser.add_argument('-4', action='store_true', dest='show_v4',
++        help='Show NFS version 4 statistics')
++    # The mountpoints argument cannot be moved into the common_parser because
++    # it will screw up the parsing of the iostat arguments (interval and count)
++    nfsstat_parser.add_argument('mountpoints', nargs='*', metavar='mountpoint',
++        help='Display statistics for this mountpoint. More than one may be specified. '
++            'If absent, statistics for all NFS mountpoints will be generated.')
++    nfsstat_parser.set_defaults(func=nfsstat_command)
++
++    iostat_parser = subparsers.add_parser('iostat',
++        parents=[common_parser],
++        help='Display iostat-like statistics.')
++    iostat_parser.add_argument('interval', nargs='?', action=ICMAction,
++        help='Number of seconds between reports. If absent, only one report will '
++            'be generated.')
++    iostat_parser.add_argument('count', nargs='?', action=ICMAction,
++        help='Number of reports generated at <interval> seconds apart. If absent, '
++            'reports will be generated continuously.')
++    # The mountpoints argument cannot be moved into the common_parser because
++    # it will screw up the parsing of the iostat arguments (interval and count)
++    iostat_parser.add_argument('mountpoints', nargs='*', action=ICMAction, metavar='mountpoint',
++        help='Display statsistics for this mountpoint. More than one may be specified. '
++            'If absent, statistics for all NFS mountpoints will be generated.')
++    iostat_parser.set_defaults(func=iostat_command)
++ 
++    args = parser.parse_args()
++    return args.func(args)
+ 
+ try:
+-    if prog == 'mountstats':
+-        mountstats_command()
+-    elif prog == 'ms-nfsstat':
+-        nfsstat_command()
+-    elif prog == 'ms-iostat':
+-        iostat_command()
+-except KeyboardInterrupt:
+-    print('Caught ^C... exiting')
++    if __name__ == '__main__':
++        # Run the mounstats sub-command if no sub-command (or the help flag)
++        # is given.  If the argparse module ever gets support for optional
++        # (default) sub-commands, then this can be changed.
++        if len(sys.argv) == 1:
++            sys.argv.insert(1, 'mountstats')
++        elif sys.argv[1] not in ['-h', '--help', 'mountstats', 'iostat', 'nfsstat']:
++            sys.argv.insert(1, 'mountstats')
++        res = main()
++        sys.stdout.close()
++        sys.stderr.close()
++        sys.exit(res)
++except (SystemExit, KeyboardInterrupt, RuntimeError):
+     sys.exit(1)
++except IOError:
++    pass
+ 
+-sys.exit(0)
+diff --git a/tools/rpcgen/rpc_main.c b/tools/rpcgen/rpc_main.c
+index 28aa60c..f81da47 100644
+--- a/tools/rpcgen/rpc_main.c
++++ b/tools/rpcgen/rpc_main.c
+@@ -44,6 +44,7 @@ static char sccsid[] = "@(#)rpc_main.c 1.30 89/03/30 (C) 1987 SMI";
+ #include <unistd.h>
+ #include <ctype.h>
+ #include <errno.h>
++#include <libgen.h>
+ #include "rpc_parse.h"
+ #include "rpc_util.h"
+ #include "rpc_scan.h"
+@@ -389,7 +390,7 @@ c_output(char *infile, char *define, int extend, char *outfile)
+ 	open_output(infile, outfilename);
+ 	add_warning();
+ 	if (infile && (include = extendfile(infile, ".h"))) {
+-		f_print(fout, "#include \"%s\"\n", include);
++		f_print(fout, "#include \"%s\"\n", basename(include));
+ 		free(include);
+ 		/* .h file already contains rpc/rpc.h */
+ 	} else
+@@ -523,7 +524,7 @@ s_output(int argc, char **argv, char *infile, char *define, int extend,
+ 	open_output(infile, outfilename);
+ 	add_warning();
+ 	if (infile && (include = extendfile(infile, ".h"))) {
+-		f_print(fout, "#include \"%s\"\n", include);
++		f_print(fout, "#include \"%s\"\n", basename(include));
+ 		free(include);
+ 	} else
+ 	  f_print(fout, "#include <rpc/rpc.h>\n");
+@@ -630,7 +631,7 @@ l_output(char *infile, char *define, int extend, char *outfile)
+ 	if (Cflag)
+ 	  f_print (fout, "#include <memory.h> /* for memset */\n");
+ 	if (infile && (include = extendfile(infile, ".h"))) {
+-		f_print(fout, "#include \"%s\"\n", include);
++		f_print(fout, "#include \"%s\"\n", basename(include));
+ 		free(include);
+ 	} else
+ 	  f_print(fout, "#include <rpc/rpc.h>\n");
+diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
+index bdea12b..48eac00 100644
+--- a/utils/exportfs/exportfs.c
++++ b/utils/exportfs/exportfs.c
+@@ -47,7 +47,7 @@ static void	error(nfs_export *exp, int err);
+ static void	usage(const char *progname, int n);
+ static void	validate_export(nfs_export *exp);
+ static int	matchhostname(const char *hostname1, const char *hostname2);
+-static void	export_d_read(const char *dname);
++static int	export_d_read(const char *dname);
+ static void grab_lockfile(void);
+ static void release_lockfile(void);
+ 
+@@ -182,8 +182,11 @@ main(int argc, char **argv)
+ 	atexit(release_lockfile);
+ 
+ 	if (f_export && ! f_ignore) {
+-		export_read(_PATH_EXPORTS);
+-		export_d_read(_PATH_EXPORTS_D);
++		if (! (export_read(_PATH_EXPORTS) +
++		       export_d_read(_PATH_EXPORTS_D))) {
++			if (f_verbose)
++				xlog(L_WARNING, "No file systems exported!");
++		}
+ 	}
+ 	if (f_export) {
+ 		if (f_all)
+@@ -685,21 +688,22 @@ out:
+ 
+ /* Based on mnt_table_parse_dir() in
+    util-linux-ng/shlibs/mount/src/tab_parse.c */
+-static void
++static int
+ export_d_read(const char *dname)
+ {
+ 	int n = 0, i;
+ 	struct dirent **namelist = NULL;
++	int volumes = 0;
+ 
+ 
+ 	n = scandir(dname, &namelist, NULL, versionsort);
+ 	if (n < 0) {
+ 		if (errno == ENOENT)
+ 			/* Silently return */
+-			return;
++			return volumes;
+ 		xlog(L_NOTICE, "scandir %s: %s", dname, strerror(errno));
+ 	} else if (n == 0)
+-		return;
++		return volumes;
+ 
+ 	for (i = 0; i < n; i++) {
+ 		struct dirent *d = namelist[i];
+@@ -729,14 +733,14 @@ export_d_read(const char *dname)
+ 			continue;
+ 		}
+ 
+-		export_read(fname);
++		volumes += export_read(fname);
+ 	}
+ 
+ 	for (i = 0; i < n; i++)
+ 		free(namelist[i]);
+ 	free(namelist);
+ 
+-	return;
++	return volumes;
+ }
+ 
+ static char
+diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am
+index 9835117..62a70af 100644
+--- a/utils/gssd/Makefile.am
++++ b/utils/gssd/Makefile.am
+@@ -15,7 +15,6 @@ endif
+ sbin_PROGRAMS	= $(sbin_PREFIXED)
+ 
+ EXTRA_DIST = \
+-	gss_destroy_creds \
+ 	$(man8_MANS)
+ 
+ COMMON_SRCS = \
+@@ -46,8 +45,8 @@ gssd_SOURCES = \
+ 	write_bytes.h
+ 
+ gssd_LDADD =	../../support/nfs/libnfs.a \
+-		$(RPCSECGSS_LIBS) $(KRBLIBS) $(GSSAPI_LIBS)
+-gssd_LDFLAGS = $(KRBLDFLAGS) $(LIBTIRPC)
++		$(RPCSECGSS_LIBS) $(KRBLIBS) $(GSSAPI_LIBS) $(LIBTIRPC)
++gssd_LDFLAGS = $(KRBLDFLAGS)
+ 
+ gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \
+ 	      $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) $(GSSAPI_CFLAGS)
+diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
+index 121feb1..1d8e6a7 100644
+--- a/utils/gssd/gssd_proc.c
++++ b/utils/gssd/gssd_proc.c
+@@ -78,6 +78,7 @@
+ #include "nfsrpc.h"
+ #include "nfslib.h"
+ #include "gss_names.h"
++#include "misc.h"
+ 
+ /*
+  * pollarray:
+@@ -1250,7 +1251,7 @@ void
+ handle_gssd_upcall(struct clnt_info *clp)
+ {
+ 	uid_t			uid;
+-	char			*lbuf = NULL;
++	char			lbuf[RPC_CHAN_BUF_SIZE];
+ 	int			lbuflen = 0;
+ 	char			*p;
+ 	char			*mech = NULL;
+@@ -1260,11 +1261,14 @@ handle_gssd_upcall(struct clnt_info *clp)
+ 
+ 	printerr(1, "handling gssd upcall (%s)\n", clp->dirname);
+ 
+-	if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) {
++	lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf));
++	if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ 		printerr(0, "WARNING: handle_gssd_upcall: "
+ 			    "failed reading request\n");
+ 		return;
+ 	}
++	lbuf[lbuflen-1] = 0;
++
+ 	printerr(2, "%s: '%s'\n", __func__, lbuf);
+ 
+ 	/* find the mechanism name */
+@@ -1362,7 +1366,6 @@ handle_gssd_upcall(struct clnt_info *clp)
+ 	}
+ 
+ out:
+-	free(lbuf);
+ 	free(mech);
+ 	free(enctypes);
+ 	free(target);
+diff --git a/utils/gssd/svcgssd.h b/utils/gssd/svcgssd.h
+index 9a2e2e8..02b5c7a 100644
+--- a/utils/gssd/svcgssd.h
++++ b/utils/gssd/svcgssd.h
+@@ -35,7 +35,7 @@
+ #include <sys/queue.h>
+ #include <gssapi/gssapi.h>
+ 
+-void handle_nullreq(FILE *f);
++void handle_nullreq(int f);
+ void gssd_run(void);
+ 
+ #define GSSD_SERVICE_NAME	"nfs"
+diff --git a/utils/gssd/svcgssd_main_loop.c b/utils/gssd/svcgssd_main_loop.c
+index 2b4111c..b5681ce 100644
+--- a/utils/gssd/svcgssd_main_loop.c
++++ b/utils/gssd/svcgssd_main_loop.c
+@@ -54,19 +54,18 @@ void
+ gssd_run()
+ {
+ 	int			ret;
+-	FILE			*f;
++	int			f;
+ 	struct pollfd		pollfd;
+ 
+ #define NULLRPC_FILE "/proc/net/rpc/auth.rpcsec.init/channel"
+ 
+-	f = fopen(NULLRPC_FILE, "rw");
+-
+-	if (!f) {
++	f = open(NULLRPC_FILE, O_RDWR);
++	if (f < 0) {
+ 		printerr(0, "failed to open %s: %s\n",
+ 			 NULLRPC_FILE, strerror(errno));
+ 		exit(1);
+ 	}
+-	pollfd.fd = fileno(f);
++	pollfd.fd = f;
+ 	pollfd.events = POLLIN;
+ 	while (1) {
+ 		int save_err;
+diff --git a/utils/gssd/svcgssd_proc.c b/utils/gssd/svcgssd_proc.c
+index 5bdb438..72ec254 100644
+--- a/utils/gssd/svcgssd_proc.c
++++ b/utils/gssd/svcgssd_proc.c
+@@ -73,36 +73,35 @@ struct svc_cred {
+ 	int	cr_ngroups;
+ 	gid_t	cr_groups[NGROUPS];
+ };
+-static char vbuf[RPC_CHAN_BUF_SIZE];
+ 
+ static int
+ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+ 		gss_OID mech, gss_buffer_desc *context_token,
+ 		int32_t endtime, char *client_name)
+ {
+-	FILE *f;
+-	int i;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int i, f, err, blen;
+ 	char *fname = NULL;
+-	int err;
+ 
+ 	printerr(1, "doing downcall\n");
+ 	if ((fname = mech2file(mech)) == NULL)
+ 		goto out_err;
+-	f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
+-	if (f == NULL) {
++
++	f = open(SVCGSSD_CONTEXT_CHANNEL, O_WRONLY);
++	if (f < 0) {
+ 		printerr(0, "WARNING: unable to open downcall channel "
+ 			     "%s: %s\n",
+ 			     SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
+ 		goto out_err;
+ 	}
+-	setvbuf(f, vbuf, _IOLBF, RPC_CHAN_BUF_SIZE);
+-	qword_printhex(f, out_handle->value, out_handle->length);
++	bp = buf, blen = sizeof(buf);
++	qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
+ 	/* XXX are types OK for the rest of this? */
+ 	/* For context cache, use the actual context endtime */
+-	qword_printint(f, endtime);
+-	qword_printint(f, cred->cr_uid);
+-	qword_printint(f, cred->cr_gid);
+-	qword_printint(f, cred->cr_ngroups);
++	qword_addint(&bp, &blen, endtime);
++	qword_addint(&bp, &blen, cred->cr_uid);
++	qword_addint(&bp, &blen, cred->cr_gid);
++	qword_addint(&bp, &blen, cred->cr_ngroups);
+ 	printerr(2, "mech: %s, hndl len: %d, ctx len %d, timeout: %d (%d from now), "
+ 		 "clnt: %s, uid: %d, gid: %d, num aux grps: %d:\n",
+ 		 fname, out_handle->length, context_token->length,
+@@ -110,19 +109,21 @@ do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
+ 		 client_name ? client_name : "<null>",
+ 		 cred->cr_uid, cred->cr_gid, cred->cr_ngroups);
+ 	for (i=0; i < cred->cr_ngroups; i++) {
+-		qword_printint(f, cred->cr_groups[i]);
++		qword_addint(&bp, &blen, cred->cr_groups[i]);
+ 		printerr(2, "  (%4d) %d\n", i+1, cred->cr_groups[i]);
+ 	}
+-	qword_print(f, fname);
+-	qword_printhex(f, context_token->value, context_token->length);
++	qword_add(&bp, &blen, fname);
++	qword_addhex(&bp, &blen, context_token->value, context_token->length);
+ 	if (client_name)
+-		qword_print(f, client_name);
+-	err = qword_eol(f);
+-	if (err) {
++		qword_add(&bp, &blen, client_name);
++	qword_addeol(&bp, &blen);
++	err = 0;
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) {
+ 		printerr(1, "WARNING: error writing to downcall channel "
+ 			 "%s: %s\n", SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
++		err = -1;
+ 	}
+-	fclose(f);
++	close(f);
+ 	return err;
+ out_err:
+ 	printerr(1, "WARNING: downcall failed\n");
+@@ -317,7 +318,7 @@ print_hexl(const char *description, unsigned char *cp, int length)
+ #endif
+ 
+ void
+-handle_nullreq(FILE *f) {
++handle_nullreq(int f) {
+ 	/* XXX initialize to a random integer to reduce chances of unnecessary
+ 	 * invalidation of existing ctx's on restarting svcgssd. */
+ 	static u_int32_t	handle_seq = 0;
+@@ -339,19 +340,21 @@ handle_nullreq(FILE *f) {
+ 	u_int32_t		maj_stat = GSS_S_FAILURE, min_stat = 0;
+ 	u_int32_t		ignore_min_stat;
+ 	struct svc_cred		cred;
+-	static char		*lbuf = NULL;
+-	static int		lbuflen = 0;
+-	static char		*cp;
++	char			lbuf[RPC_CHAN_BUF_SIZE];
++	int			lbuflen = 0;
++	char			*cp;
+ 	int32_t			ctx_endtime;
+ 	char			*hostbased_name = NULL;
+ 
+ 	printerr(1, "handling null request\n");
+ 
+-	if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
++	lbuflen = read(f, lbuf, sizeof(lbuf));
++	if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') {
+ 		printerr(0, "WARNING: handle_nullreq: "
+ 			    "failed reading request\n");
+ 		return;
+ 	}
++	lbuf[lbuflen-1] = 0;
+ 
+ 	cp = lbuf;
+ 
+diff --git a/utils/gssd/write_bytes.h b/utils/gssd/write_bytes.h
+index 4fc72cc..b3f342b 100644
+--- a/utils/gssd/write_bytes.h
++++ b/utils/gssd/write_bytes.h
+@@ -32,6 +32,7 @@
+ #define _WRITE_BYTES_H_
+ 
+ #include <stdlib.h>
++#include <string.h>
+ #include <sys/types.h>
+ #include <netinet/in.h>		/* for ntohl */
+ 
+diff --git a/utils/idmapd/Makefile.am b/utils/idmapd/Makefile.am
+index 58b33ec..c2f8ba1 100644
+--- a/utils/idmapd/Makefile.am
++++ b/utils/idmapd/Makefile.am
+@@ -7,8 +7,7 @@ KPREFIX		= @kprefix@
+ sbin_PROGRAMS	= idmapd
+ 
+ EXTRA_DIST = \
+-	$(man8_MANS) \
+-	idmapd.conf
++	$(man8_MANS)
+ 
+ idmapd_SOURCES = \
+ 	idmapd.c \
+diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
+index 5810936..e24f3bd 100644
+--- a/utils/mount/Makefile.am
++++ b/utils/mount/Makefile.am
+@@ -8,19 +8,21 @@ man8_MANS	= mount.nfs.man umount.nfs.man
+ man5_MANS	= nfs.man
+ 
+ sbin_PROGRAMS	= mount.nfs
+-EXTRA_DIST = nfsmount.x $(man8_MANS) $(man5_MANS)
++EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS)
+ mount_common = error.c network.c token.c \
+ 		    parse_opt.c parse_dev.c \
+ 		    nfsmount.c nfs4mount.c stropts.c\
+ 		    mount_constants.h error.h network.h token.h \
+ 		    parse_opt.h parse_dev.h \
+-		    nfs4_mount.h nfs_mount4.h stropts.h version.h \
+-		    mount_config.h utils.c utils.h
++		    nfs4_mount.h stropts.h version.h \
++		    mount_config.h utils.c utils.h \
++		    nfs_mount.h
+ 
+ if MOUNT_CONFIG
+ mount_common += configfile.c
+ man5_MANS += nfsmount.conf.man
+-EXTRA_DIST += nfsmount.conf
++else
++EXTRA_DIST += nfsmount.conf.man
+ endif
+ 
+ mount_nfs_LDADD = ../../support/nfs/libnfs.a \
+diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c
+index 6f85dc9..fa46d54 100644
+--- a/utils/mount/mount_libmount.c
++++ b/utils/mount/mount_libmount.c
+@@ -174,7 +174,7 @@ static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
+ {
+ 	int rc, c;
+ 	char *spec = NULL, *opts = NULL;
+-	int ret = EX_FAIL;
++	int ret = EX_FAIL, verbose = 0;
+ 
+ 	static const struct option longopts[] = {
+ 		{ "force", 0, 0, 'f' },
+@@ -201,6 +201,8 @@ static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
+ 		return EX_USAGE;
+ 	}
+ 
++	verbose = mnt_context_is_verbose(cxt);
++
+ 	if (optind < argc)
+ 		spec = argv[optind++];
+ 
+@@ -228,6 +230,10 @@ static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
+ 		goto err;
+ 	}
+ 
++	if (verbose)
++		printf(_("%s: %s mount point detected\n"), spec,
++					mnt_context_get_fstype(cxt));
++
+ 	opts = retrieve_mount_options(mnt_context_get_fs(cxt));
+ 
+ 	if (!mnt_context_is_lazy(cxt)) {
+@@ -263,6 +269,12 @@ static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
+ 	}
+ 	ret = EX_SUCCESS;
+ err:
++	if (verbose) {
++		if (ret == EX_SUCCESS)
++			printf(_("%s: umounted\n"), spec);
++		else
++			printf(_("%s: umount failed\n"), spec);
++	}
+ 	free(opts);
+ 	return ret;
+ }
+diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
+index 7db968b..9e1ab5c 100644
+--- a/utils/mountd/Makefile.am
++++ b/utils/mountd/Makefile.am
+@@ -7,6 +7,7 @@ RPCPREFIX	= rpc.
+ KPREFIX		= @kprefix@
+ sbin_PROGRAMS	= mountd
+ 
++noinst_HEADERS = fsloc.h
+ mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
+ 		 svc_run.c fsloc.c v4root.c mountd.h
+ mountd_LDADD = ../../support/export/libexport.a \
+diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
+index 663a52a..c23d384 100644
+--- a/utils/mountd/cache.c
++++ b/utils/mountd/cache.c
+@@ -61,15 +61,13 @@ enum nfsd_fsid {
+  * Record is terminated with newline.
+  *
+  */
+-static int cache_export_ent(char *domain, struct exportent *exp, char *p);
++static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path);
+ 
+ #define INITIAL_MANAGED_GROUPS 100
+ 
+-char *lbuf  = NULL;
+-int lbuflen = 0;
+ extern int use_ipaddr;
+ 
+-static void auth_unix_ip(FILE *f)
++static void auth_unix_ip(int f)
+ {
+ 	/* requests are
+ 	 *  class IP-ADDR
+@@ -78,23 +76,26 @@ static void auth_unix_ip(FILE *f)
+ 	 *
+ 	 *  "nfsd" IP-ADDR expiry domainname
+ 	 */
+-	char *cp;
+ 	char class[20];
+ 	char ipaddr[INET6_ADDRSTRLEN + 1];
+ 	char *client = NULL;
+ 	struct addrinfo *tmp = NULL;
+-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
+-		return;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int blen;
++
++	blen = read(f, buf, sizeof(buf));
++	if (blen <= 0 || buf[blen-1] != '\n') return;
++	buf[blen-1] = 0;
+ 
+-	xlog(D_CALL, "auth_unix_ip: inbuf '%s'", lbuf);
++	xlog(D_CALL, "auth_unix_ip: inbuf '%s'", buf);
+ 
+-	cp = lbuf;
++	bp = buf;
+ 
+-	if (qword_get(&cp, class, 20) <= 0 ||
++	if (qword_get(&bp, class, 20) <= 0 ||
+ 	    strcmp(class, "nfsd") != 0)
+ 		return;
+ 
+-	if (qword_get(&cp, ipaddr, sizeof(ipaddr) - 1) <= 0)
++	if (qword_get(&bp, ipaddr, sizeof(ipaddr) - 1) <= 0)
+ 		return;
+ 
+ 	tmp = host_pton(ipaddr);
+@@ -113,16 +114,20 @@ static void auth_unix_ip(FILE *f)
+ 			freeaddrinfo(ai);
+ 		}
+ 	}
+-	qword_print(f, "nfsd");
+-	qword_print(f, ipaddr);
+-	qword_printtimefrom(f, DEFAULT_TTL);
++	bp = buf; blen = sizeof(buf);
++	qword_add(&bp, &blen, "nfsd");
++	qword_add(&bp, &blen, ipaddr);
++	qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
+ 	if (use_ipaddr) {
+ 		memmove(ipaddr + 1, ipaddr, strlen(ipaddr) + 1);
+ 		ipaddr[0] = '$';
+-		qword_print(f, ipaddr);
++		qword_add(&bp, &blen, ipaddr);
+ 	} else if (client)
+-		qword_print(f, *client?client:"DEFAULT");
+-	qword_eol(f);
++		qword_add(&bp, &blen, *client?client:"DEFAULT");
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
++		xlog(L_ERROR, "auth_unix_ip: error writing reply");
++
+ 	xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT");
+ 
+ 	free(client);
+@@ -130,7 +135,7 @@ static void auth_unix_ip(FILE *f)
+ 
+ }
+ 
+-static void auth_unix_gid(FILE *f)
++static void auth_unix_gid(int f)
+ {
+ 	/* Request are
+ 	 *  uid
+@@ -144,7 +149,8 @@ static void auth_unix_gid(FILE *f)
+ 	gid_t *more_groups;
+ 	int ngroups;
+ 	int rv, i;
+-	char *cp;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int blen;
+ 
+ 	if (groups_len == 0) {
+ 		groups = malloc(sizeof(gid_t) * INITIAL_MANAGED_GROUPS);
+@@ -156,11 +162,12 @@ static void auth_unix_gid(FILE *f)
+ 
+ 	ngroups = groups_len;
+ 
+-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
+-		return;
++	blen = read(f, buf, sizeof(buf));
++	if (blen <= 0 || buf[blen-1] != '\n') return;
++	buf[blen-1] = 0;
+ 
+-	cp = lbuf;
+-	if (qword_get_uint(&cp, &uid) != 0)
++	bp = buf;
++	if (qword_get_uint(&bp, &uid) != 0)
+ 		return;
+ 
+ 	pw = getpwuid(uid);
+@@ -180,15 +187,19 @@ static void auth_unix_gid(FILE *f)
+ 			}
+ 		}
+ 	}
+-	qword_printuint(f, uid);
+-	qword_printtimefrom(f, DEFAULT_TTL);
++
++	bp = buf; blen = sizeof(buf);
++	qword_adduint(&bp, &blen, uid);
++	qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
+ 	if (rv >= 0) {
+-		qword_printuint(f, ngroups);
++		qword_adduint(&bp, &blen, ngroups);
+ 		for (i=0; i<ngroups; i++)
+-			qword_printuint(f, groups[i]);
++			qword_adduint(&bp, &blen, groups[i]);
+ 	} else
+-		qword_printuint(f, 0);
+-	qword_eol(f);
++		qword_adduint(&bp, &blen, 0);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
++		xlog(L_ERROR, "auth_unix_gid: error writing reply");
+ }
+ 
+ #if USE_BLKID
+@@ -659,14 +670,13 @@ static struct addrinfo *lookup_client_addr(char *dom)
+ 	return ret;
+ }
+ 
+-static void nfsd_fh(FILE *f)
++static void nfsd_fh(int f)
+ {
+ 	/* request are:
+ 	 *  domain fsidtype fsid
+ 	 * interpret fsid, find export point and options, and write:
+ 	 *  domain fsidtype fsid expiry path
+ 	 */
+-	char *cp;
+ 	char *dom;
+ 	int fsidtype;
+ 	int fsidlen;
+@@ -678,24 +688,27 @@ static void nfsd_fh(FILE *f)
+ 	nfs_export *exp;
+ 	int i;
+ 	int dev_missing = 0;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int blen;
+ 
+-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
+-		return;
++	blen = read(f, buf, sizeof(buf));
++	if (blen <= 0 || buf[blen-1] != '\n') return;
++	buf[blen-1] = 0;
+ 
+-	xlog(D_CALL, "nfsd_fh: inbuf '%s'", lbuf);
++	xlog(D_CALL, "nfsd_fh: inbuf '%s'", buf);
+ 
+-	cp = lbuf;
++	bp = buf;
+ 
+-	dom = malloc(strlen(cp));
++	dom = malloc(blen);
+ 	if (dom == NULL)
+ 		return;
+-	if (qword_get(&cp, dom, strlen(cp)) <= 0)
++	if (qword_get(&bp, dom, blen) <= 0)
+ 		goto out;
+-	if (qword_get_int(&cp, &fsidtype) != 0)
++	if (qword_get_int(&bp, &fsidtype) != 0)
+ 		goto out;
+ 	if (fsidtype < 0 || fsidtype > 7)
+ 		goto out; /* unknown type */
+-	if ((fsidlen = qword_get(&cp, fsid, 32)) <= 0)
++	if ((fsidlen = qword_get(&bp, fsid, 32)) <= 0)
+ 		goto out;
+ 	if (parse_fsid(fsidtype, fsidlen, fsid, &parsed))
+ 		goto out;
+@@ -796,12 +809,13 @@ static void nfsd_fh(FILE *f)
+ 	}
+ 
+ 	if (found)
+-		if (cache_export_ent(dom, found, found_path) < 0)
++		if (cache_export_ent(buf, sizeof(buf), dom, found, found_path) < 0)
+ 			found = 0;
+ 
+-	qword_print(f, dom);
+-	qword_printint(f, fsidtype);
+-	qword_printhex(f, fsid, fsidlen);
++	bp = buf; blen = sizeof(buf);
++	qword_add(&bp, &blen, dom);
++	qword_addint(&bp, &blen, fsidtype);
++	qword_addhex(&bp, &blen, fsid, fsidlen);
+ 	/* The fsid -> path lookup can be quite expensive as it
+ 	 * potentially stats and reads lots of devices, and some of those
+ 	 * might have spun-down.  The Answer is not likely to
+@@ -810,20 +824,21 @@ static void nfsd_fh(FILE *f)
+ 	 * timeout.  Maybe this should be configurable on the command
+ 	 * line.
+ 	 */
+-	qword_printint(f, 0x7fffffff);
++	qword_addint(&bp, &blen, 0x7fffffff);
+ 	if (found)
+-		qword_print(f, found_path);
+-	qword_eol(f);
+- out:
++		qword_add(&bp, &blen, found_path);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
++		xlog(L_ERROR, "nfsd_fh: error writing reply");
++out:
+ 	if (found_path)
+ 		free(found_path);
+ 	freeaddrinfo(ai);
+ 	free(dom);
+ 	xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
+-	return;		
+ }
+ 
+-static void write_fsloc(FILE *f, struct exportent *ep)
++static void write_fsloc(char **bp, int *blen, struct exportent *ep)
+ {
+ 	struct servers *servers;
+ 
+@@ -833,20 +848,20 @@ static void write_fsloc(FILE *f, struct exportent *ep)
+ 	servers = replicas_lookup(ep->e_fslocmethod, ep->e_fslocdata);
+ 	if (!servers)
+ 		return;
+-	qword_print(f, "fsloc");
+-	qword_printint(f, servers->h_num);
++	qword_add(bp, blen, "fsloc");
++	qword_addint(bp, blen, servers->h_num);
+ 	if (servers->h_num >= 0) {
+ 		int i;
+ 		for (i=0; i<servers->h_num; i++) {
+-			qword_print(f, servers->h_mp[i]->h_host);
+-			qword_print(f, servers->h_mp[i]->h_path);
++			qword_add(bp, blen, servers->h_mp[i]->h_host);
++			qword_add(bp, blen, servers->h_mp[i]->h_path);
+ 		}
+ 	}
+-	qword_printint(f, servers->h_referral);
++	qword_addint(bp, blen, servers->h_referral);
+ 	release_replicas(servers);
+ }
+ 
+-static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask)
++static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask)
+ {
+ 	struct sec_entry *p;
+ 
+@@ -857,45 +872,52 @@ static void write_secinfo(FILE *f, struct exportent *ep, int flag_mask)
+ 		return;
+ 	}
+ 	fix_pseudoflavor_flags(ep);
+-	qword_print(f, "secinfo");
+-	qword_printint(f, p - ep->e_secinfo);
++	qword_add(bp, blen, "secinfo");
++	qword_addint(bp, blen, p - ep->e_secinfo);
+ 	for (p = ep->e_secinfo; p->flav; p++) {
+-		qword_printint(f, p->flav->fnum);
+-		qword_printint(f, p->flags & flag_mask);
++		qword_addint(bp, blen, p->flav->fnum);
++		qword_addint(bp, blen, p->flags & flag_mask);
+ 	}
+ 
+ }
+ 
+-static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *exp)
++static int dump_to_cache(int f, char *buf, int buflen, char *domain, char *path, struct exportent *exp)
+ {
+-	qword_print(f, domain);
+-	qword_print(f, path);
++	char *bp = buf;
++	int blen = buflen;
++	time_t now = time(0);
++
++	qword_add(&bp, &blen, domain);
++	qword_add(&bp, &blen, path);
+ 	if (exp) {
+ 		int different_fs = strcmp(path, exp->e_path) != 0;
+ 		int flag_mask = different_fs ? ~NFSEXP_FSID : ~0;
+ 
+-		qword_printtimefrom(f, exp->e_ttl);
+-		qword_printint(f, exp->e_flags & flag_mask);
+-		qword_printint(f, exp->e_anonuid);
+-		qword_printint(f, exp->e_anongid);
+-		qword_printint(f, exp->e_fsid);
+-		write_fsloc(f, exp);
+-		write_secinfo(f, exp, flag_mask);
+- 		if (exp->e_uuid == NULL || different_fs) {
+- 			char u[16];
+- 			if (uuid_by_path(path, 0, 16, u)) {
+- 				qword_print(f, "uuid");
+- 				qword_printhex(f, u, 16);
+- 			}
+- 		} else {
+- 			char u[16];
+- 			get_uuid(exp->e_uuid, 16, u);
+- 			qword_print(f, "uuid");
+- 			qword_printhex(f, u, 16);
+- 		}
++		qword_adduint(&bp, &blen, now + exp->e_ttl);
++		qword_addint(&bp, &blen, exp->e_flags & flag_mask);
++		qword_addint(&bp, &blen, exp->e_anonuid);
++		qword_addint(&bp, &blen, exp->e_anongid);
++		qword_addint(&bp, &blen, exp->e_fsid);
++		write_fsloc(&bp, &blen, exp);
++		write_secinfo(&bp, &blen, exp, flag_mask);
++		if (exp->e_uuid == NULL || different_fs) {
++			char u[16];
++			if (uuid_by_path(path, 0, 16, u)) {
++				qword_add(&bp, &blen, "uuid");
++				qword_addhex(&bp, &blen, u, 16);
++			}
++		} else {
++			char u[16];
++			get_uuid(exp->e_uuid, 16, u);
++			qword_add(&bp, &blen, "uuid");
++			qword_addhex(&bp, &blen, u, 16);
++		}
+ 	} else
+-		qword_printtimefrom(f, DEFAULT_TTL);
+-	return qword_eol(f);
++		qword_adduint(&bp, &blen, now + DEFAULT_TTL);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0) return -1;
++	if (write(f, buf, bp - buf) != bp - buf) return -1;
++	return 0;
+ }
+ 
+ static nfs_export *
+@@ -1245,27 +1267,27 @@ static struct exportent *lookup_junction(char *dom, const char *pathname,
+ 	return exp;
+ }
+ 
+-static void lookup_nonexport(FILE *f, char *dom, char *path,
++static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path,
+ 		struct addrinfo *ai)
+ {
+ 	struct exportent *eep;
+ 
+ 	eep = lookup_junction(dom, path, ai);
+-	dump_to_cache(f, dom, path, eep);
++	dump_to_cache(f, buf, buflen, dom, path, eep);
+ 	if (eep == NULL)
+ 		return;
+ 	exportent_release(eep);
+ 	free(eep);
+ }
+ #else	/* !HAVE_NFS_PLUGIN_H */
+-static void lookup_nonexport(FILE *f, char *dom, char *path,
++static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path,
+ 		struct addrinfo *UNUSED(ai))
+ {
+-	dump_to_cache(f, dom, path, NULL);
++	dump_to_cache(f, buf, buflen, dom, path, NULL);
+ }
+ #endif	/* !HAVE_NFS_PLUGIN_H */
+ 
+-static void nfsd_export(FILE *f)
++static void nfsd_export(int f)
+ {
+ 	/* requests are:
+ 	 *  domain path
+@@ -1273,26 +1295,28 @@ static void nfsd_export(FILE *f)
+ 	 *  domain path expiry flags anonuid anongid fsid
+ 	 */
+ 
+-	char *cp;
+ 	char *dom, *path;
+ 	nfs_export *found = NULL;
+ 	struct addrinfo *ai = NULL;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int blen;
+ 
+-	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
+-		return;
++	blen = read(f, buf, sizeof(buf));
++	if (blen <= 0 || buf[blen-1] != '\n') return;
++	buf[blen-1] = 0;
+ 
+-	xlog(D_CALL, "nfsd_export: inbuf '%s'", lbuf);
++	xlog(D_CALL, "nfsd_export: inbuf '%s'", buf);
+ 
+-	cp = lbuf;
+-	dom = malloc(strlen(cp));
+-	path = malloc(strlen(cp));
++	bp = buf;
++	dom = malloc(blen);
++	path = malloc(blen);
+ 
+ 	if (!dom || !path)
+ 		goto out;
+ 
+-	if (qword_get(&cp, dom, strlen(lbuf)) <= 0)
++	if (qword_get(&bp, dom, blen) <= 0)
+ 		goto out;
+-	if (qword_get(&cp, path, strlen(lbuf)) <= 0)
++	if (qword_get(&bp, path, blen) <= 0)
+ 		goto out;
+ 
+ 	auth_reload();
+@@ -1306,14 +1330,14 @@ static void nfsd_export(FILE *f)
+ 	found = lookup_export(dom, path, ai);
+ 
+ 	if (found) {
+-		if (dump_to_cache(f, dom, path, &found->m_export) < 0) {
++		if (dump_to_cache(f, buf, sizeof(buf), dom, path, &found->m_export) < 0) {
+ 			xlog(L_WARNING,
+ 			     "Cannot export %s, possibly unsupported filesystem"
+ 			     " or fsid= required", path);
+-			dump_to_cache(f, dom, path, NULL);
++			dump_to_cache(f, buf, sizeof(buf), dom, path, NULL);
+ 		}
+ 	} else
+-		lookup_nonexport(f, dom, path, ai);
++		lookup_nonexport(f, buf, sizeof(buf), dom, path, ai);
+ 
+  out:
+ 	xlog(D_CALL, "nfsd_export: found %p path %s", found, path ? path : NULL);
+@@ -1325,15 +1349,14 @@ static void nfsd_export(FILE *f)
+ 
+ struct {
+ 	char *cache_name;
+-	void (*cache_handle)(FILE *f);
+-	FILE *f;
+-	char vbuf[RPC_CHAN_BUF_SIZE];
++	void (*cache_handle)(int f);
++	int f;
+ } cachelist[] = {
+-	{ "auth.unix.ip", auth_unix_ip, NULL, ""},
+-	{ "auth.unix.gid", auth_unix_gid, NULL, ""},
+-	{ "nfsd.export", nfsd_export, NULL, ""},
+-	{ "nfsd.fh", nfsd_fh, NULL, ""},
+-	{ NULL, NULL, NULL, ""}
++	{ "auth.unix.ip", auth_unix_ip, -1 },
++	{ "auth.unix.gid", auth_unix_gid, -1 },
++	{ "nfsd.export", nfsd_export, -1 },
++	{ "nfsd.fh", nfsd_fh, -1 },
++	{ NULL, NULL, -1 }
+ };
+ 
+ extern int manage_gids;
+@@ -1350,11 +1373,7 @@ void cache_open(void)
+ 		if (!manage_gids && cachelist[i].cache_handle == auth_unix_gid)
+ 			continue;
+ 		sprintf(path, "/proc/net/rpc/%s/channel", cachelist[i].cache_name);
+-		cachelist[i].f = fopen(path, "r+");
+-		if (cachelist[i].f != NULL) {
+-			setvbuf(cachelist[i].f, cachelist[i].vbuf, _IOLBF, 
+-				RPC_CHAN_BUF_SIZE);
+-		}
++		cachelist[i].f = open(path, O_RDWR);
+ 	}
+ }
+ 
+@@ -1366,8 +1385,8 @@ void cache_set_fds(fd_set *fdset)
+ {
+ 	int i;
+ 	for (i=0; cachelist[i].cache_name; i++) {
+-		if (cachelist[i].f)
+-			FD_SET(fileno(cachelist[i].f), fdset);
++		if (cachelist[i].f >= 0)
++			FD_SET(cachelist[i].f, fdset);
+ 	}
+ }
+ 
+@@ -1380,11 +1399,11 @@ int cache_process_req(fd_set *readfds)
+ 	int i;
+ 	int cnt = 0;
+ 	for (i=0; cachelist[i].cache_name; i++) {
+-		if (cachelist[i].f != NULL &&
+-		    FD_ISSET(fileno(cachelist[i].f), readfds)) {
++		if (cachelist[i].f >= 0 &&
++		    FD_ISSET(cachelist[i].f, readfds)) {
+ 			cnt++;
+ 			cachelist[i].cache_handle(cachelist[i].f);
+-			FD_CLR(fileno(cachelist[i].f), readfds);
++			FD_CLR(cachelist[i].f, readfds);
+ 		}
+ 	}
+ 	return cnt;
+@@ -1397,14 +1416,14 @@ int cache_process_req(fd_set *readfds)
+  * % echo $domain $path $[now+DEFAULT_TTL] $options $anonuid $anongid $fsid > /proc/net/rpc/nfsd.export/channel
+  */
+ 
+-static int cache_export_ent(char *domain, struct exportent *exp, char *path)
++static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path)
+ {
+-	int err;
+-	FILE *f = fopen("/proc/net/rpc/nfsd.export/channel", "w");
+-	if (!f)
+-		return -1;
++	int f, err;
++
++	f = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
++	if (f < 0) return -1;
+ 
+-	err = dump_to_cache(f, domain, exp->e_path, exp);
++	err = dump_to_cache(f, buf, buflen, domain, exp->e_path, exp);
+ 	if (err) {
+ 		xlog(L_WARNING,
+ 		     "Cannot export %s, possibly unsupported filesystem or"
+@@ -1445,13 +1464,13 @@ static int cache_export_ent(char *domain, struct exportent *exp, char *path)
+ 				continue;
+ 			dev = stb.st_dev;
+ 			path[l] = 0;
+-			dump_to_cache(f, domain, path, exp);
++			dump_to_cache(f, buf, buflen, domain, path, exp);
+ 			path[l] = c;
+ 		}
+ 		break;
+ 	}
+ 
+-	fclose(f);
++	close(f);
+ 	return err;
+ }
+ 
+@@ -1462,27 +1481,25 @@ static int cache_export_ent(char *domain, struct exportent *exp, char *path)
+  */
+ int cache_export(nfs_export *exp, char *path)
+ {
+-	char buf[INET6_ADDRSTRLEN];
+-	int err;
+-	FILE *f;
++	char ip[INET6_ADDRSTRLEN];
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int blen, f;
+ 
+-	f = fopen("/proc/net/rpc/auth.unix.ip/channel", "w");
+-	if (!f)
++	f = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
++	if (f < 0)
+ 		return -1;
+ 
+-
+-	qword_print(f, "nfsd");
+-	qword_print(f,
+-		host_ntop(get_addrlist(exp->m_client, 0), buf, sizeof(buf)));
+-	qword_printtimefrom(f, exp->m_export.e_ttl);
+-	qword_print(f, exp->m_client->m_hostname);
+-	err = qword_eol(f);
+-	
+-	fclose(f);
+-
+-	err = cache_export_ent(exp->m_client->m_hostname, &exp->m_export, path)
+-		|| err;
+-	return err;
++	bp = buf, blen = sizeof(buf);
++	qword_add(&bp, &blen, "nfsd");
++	qword_add(&bp, &blen, host_ntop(get_addrlist(exp->m_client, 0), ip, sizeof(ip)));
++	qword_adduint(&bp, &blen, time(0) + exp->m_export.e_ttl);
++	qword_add(&bp, &blen, exp->m_client->m_hostname);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) blen = -1;
++	close(f);
++	if (blen < 0) return -1;
++
++	return cache_export_ent(buf, sizeof(buf), exp->m_client->m_hostname, &exp->m_export, path);
+ }
+ 
+ /**
+@@ -1501,27 +1518,33 @@ int cache_export(nfs_export *exp, char *path)
+ struct nfs_fh_len *
+ cache_get_filehandle(nfs_export *exp, int len, char *p)
+ {
+-	FILE *f = fopen("/proc/fs/nfsd/filehandle", "r+");
+-	char buf[200];
+-	char *bp = buf;
+-	int failed;
+ 	static struct nfs_fh_len fh;
++	char buf[RPC_CHAN_BUF_SIZE], *bp;
++	int blen, f;
++
++	f = open("/proc/fs/nfsd/filehandle", O_RDWR);
++	if (f < 0) {
++		f = open("/proc/fs/nfs/filehandle", O_RDWR);
++		if (f < 0) return NULL;
++	}
+ 
+-	if (!f)
+-		f = fopen("/proc/fs/nfs/filehandle", "r+");
+-	if (!f)
++	bp = buf, blen = sizeof(buf);
++	qword_add(&bp, &blen, exp->m_client->m_hostname);
++	qword_add(&bp, &blen, p);
++	qword_addint(&bp, &blen, len);
++	qword_addeol(&bp, &blen);
++	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) {
++		close(f);
+ 		return NULL;
++	}
++	bp = buf;
++	blen = read(f, buf, sizeof(buf));
++	close(f);
+ 
+-	qword_print(f, exp->m_client->m_hostname);
+-	qword_print(f, p);
+-	qword_printint(f, len);	
+-	failed = qword_eol(f);
+-	
+-	if (!failed)
+-		failed = (fgets(buf, sizeof(buf), f) == NULL);
+-	fclose(f);
+-	if (failed)
++	if (blen <= 0 || buf[blen-1] != '\n')
+ 		return NULL;
++	buf[blen-1] = 0;
++
+ 	memset(fh.fh_handle, 0, sizeof(fh.fh_handle));
+ 	fh.fh_size = qword_get(&bp, (char *)fh.fh_handle, NFS3_FHSIZE);
+ 	return &fh;
+diff --git a/utils/nfsd/Makefile.am b/utils/nfsd/Makefile.am
+index 1536065..39a6e6f 100644
+--- a/utils/nfsd/Makefile.am
++++ b/utils/nfsd/Makefile.am
+@@ -7,6 +7,7 @@ RPCPREFIX	= rpc.
+ KPREFIX		= @kprefix@
+ sbin_PROGRAMS	= nfsd
+ 
++noinst_HEADERS = nfssvc.h
+ nfsd_SOURCES = nfsd.c nfssvc.c
+ nfsd_LDADD = ../../support/nfs/libnfs.a $(LIBTIRPC)
+ 
+diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
+index 0675b6a..027e5ac 100644
+--- a/utils/nfsd/nfssvc.c
++++ b/utils/nfsd/nfssvc.c
+@@ -20,6 +20,7 @@
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <stdlib.h>
++#include <string.h>
+ 
+ #include "nfslib.h"
+ #include "xlog.h"
+diff --git a/utils/nfsdcltrack/Makefile.am b/utils/nfsdcltrack/Makefile.am
+index 7524295..0a2858f 100644
+--- a/utils/nfsdcltrack/Makefile.am
++++ b/utils/nfsdcltrack/Makefile.am
+@@ -10,6 +10,8 @@ EXTRA_DIST	= $(man8_MANS)
+ AM_CFLAGS	+= -D_LARGEFILE64_SOURCE
+ sbin_PROGRAMS	= nfsdcltrack
+ 
++noinst_HEADERS	= sqlite.h
++
+ nfsdcltrack_SOURCES = nfsdcltrack.c sqlite.c
+ nfsdcltrack_LDADD = ../../support/nfs/libnfs.a $(LIBSQLITE) $(LIBCAP)
+ 
+diff --git a/utils/nfsdcltrack/sqlite.c b/utils/nfsdcltrack/sqlite.c
+index fb45c4a..54cd748 100644
+--- a/utils/nfsdcltrack/sqlite.c
++++ b/utils/nfsdcltrack/sqlite.c
+@@ -102,7 +102,7 @@ sqlite_query_schema_version(void)
+ 		 -1, &stmt, NULL);
+ 	if (ret != SQLITE_OK) {
+ 		xlog(L_ERROR, "Unable to prepare select statement: %s",
+-			sqlite3_errstr(ret));
++			sqlite3_errmsg(dbh));
+ 		ret = 0;
+ 		goto out;
+ 	}
+@@ -111,7 +111,7 @@ sqlite_query_schema_version(void)
+ 	ret = sqlite3_step(stmt);
+ 	if (ret != SQLITE_ROW) {
+ 		xlog(L_ERROR, "Select statement execution failed: %s",
+-				sqlite3_errstr(ret));
++				sqlite3_errmsg(dbh));
+ 		ret = 0;
+ 		goto out;
+ 	}
+@@ -324,7 +324,7 @@ sqlite_prepare_dbh(const char *topdir)
+ 	ret = sqlite3_busy_timeout(dbh, CLTRACK_SQLITE_BUSY_TIMEOUT);
+ 	if (ret != SQLITE_OK) {
+ 		xlog(L_ERROR, "Unable to set sqlite busy timeout: %s",
+-				sqlite3_errstr(ret));
++				sqlite3_errmsg(dbh));
+ 		goto out_close;
+ 	}
+ 
+@@ -357,7 +357,7 @@ sqlite_prepare_dbh(const char *topdir)
+ 
+ 	return ret;
+ out_close:
+-	sqlite3_close_v2(dbh);
++	sqlite3_close(dbh);
+ 	dbh = NULL;
+ 	return ret;
+ }
+@@ -574,21 +574,21 @@ sqlite_query_reclaiming(const time_t grace_start)
+ 				      "time < ? OR has_session != 1", -1, &stmt, NULL);
+ 	if (ret != SQLITE_OK) {
+ 		xlog(L_ERROR, "%s: unable to prepare select statement: %s",
+-				__func__, sqlite3_errstr(ret));
++				__func__, sqlite3_errmsg(dbh));
+ 		return ret;
+ 	}
+ 
+ 	ret = sqlite3_bind_int64(stmt, 1, (sqlite3_int64)grace_start);
+ 	if (ret != SQLITE_OK) {
+ 		xlog(L_ERROR, "%s: bind int64 failed: %s",
+-				__func__, sqlite3_errstr(ret));
++				__func__, sqlite3_errmsg(dbh));
+ 		return ret;
+ 	}
+ 
+ 	ret = sqlite3_step(stmt);
+ 	if (ret != SQLITE_ROW) {
+ 		xlog(L_ERROR, "%s: unexpected return code from select: %s",
+-				__func__, sqlite3_errstr(ret));
++				__func__, sqlite3_errmsg(dbh));
+ 		return ret;
+ 	}
+ 
+diff --git a/utils/nfsidmap/Makefile.am b/utils/nfsidmap/Makefile.am
+index 737a219..91cedfd 100644
+--- a/utils/nfsidmap/Makefile.am
++++ b/utils/nfsidmap/Makefile.am
+@@ -7,4 +7,4 @@ nfsidmap_SOURCES = nfsidmap.c
+ nfsidmap_LDADD = $(LIBNFSIDMAP) -lkeyutils ../../support/nfs/libnfs.a
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+-EXTRA_DIST = id_resolver.conf
++EXTRA_DIST = id_resolver.conf $(man8_MANS)
+diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c
+index e0d31e7..5d62078 100644
+--- a/utils/nfsidmap/nfsidmap.c
++++ b/utils/nfsidmap/nfsidmap.c
+@@ -25,7 +25,7 @@ char *usage="Usage: %s [-v] [-c || [-u|-g|-r key] || [-t timeout] key desc]";
+ 
+ #define PROCKEYS "/proc/keys"
+ #ifndef DEFAULT_KEYRING
+-#define DEFAULT_KEYRING "id_resolver"
++#define DEFAULT_KEYRING ".id_resolver"
+ #endif
+ 
+ #ifndef PATH_IDMAPDCONF
+@@ -209,10 +209,23 @@ static int key_invalidate(char *keystr, int keymask)
+ 		*(strchr(buf, ' ')) = '\0';
+ 		sscanf(buf, "%x", &key);
+ 
+-		if (keyctl_invalidate(key) < 0) {
+-			xlog_err("keyctl_invalidate(0x%x) failed: %m", key);
+-			fclose(fp);
+-			return 1;
++/* older libkeyutils compatibility */
++#ifndef KEYCTL_INVALIDATE
++#define KEYCTL_INVALIDATE 21      /* invalidate a key */
++#endif
++		if (keyctl(KEYCTL_INVALIDATE, key) < 0) {
++			if (errno != EOPNOTSUPP) {
++				xlog_err("keyctl_invalidate(0x%x) failed: %m", key);
++				fclose(fp);
++				return 1;
++			} else {
++				/* older kernel compatibility attempt: */
++				if (keyctl_revoke(key) < 0) {
++					xlog_err("keyctl_revoke(0x%x) failed: %m", key);
++					fclose(fp);
++					return 1;
++				}
++			}
+ 		}
+ 
+ 		keymask &= ~mask;
+@@ -316,6 +329,9 @@ int main(int argc, char **argv)
+ 			key, type, value, timeout);
+ 	}
+ 
++	/* Become a possesor of the to-be-instantiated key to set the key's timeout */
++	request_key("keyring", DEFAULT_KEYRING, NULL, KEY_SPEC_THREAD_KEYRING);
++
+ 	if (strcmp(type, "uid") == 0)
+ 		rc = id_lookup(value, key, USER);
+ 	else if (strcmp(type, "gid") == 0)
+diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
+index 18e4d27..9f481db 100644
+--- a/utils/nfsstat/nfsstat.c
++++ b/utils/nfsstat/nfsstat.c
+@@ -558,7 +558,7 @@ print_server_stats(int opt_prt)
+ 			;
+ 		} else {
+ 			print_numbers(LABEL_srvrpc
+-				"calls      badcalls   badclnt    badauth    xdrcall\n",
++				"calls      badcalls   badfmt     badauth    badclnt\n",
+ 				srvrpcinfo, 5);
+ 			printf("\n");
+ 		}
+diff --git a/utils/osd_login/Makefile.am b/utils/osd_login/Makefile.am
+index 20c2d8c..ded1fd3 100644
+--- a/utils/osd_login/Makefile.am
++++ b/utils/osd_login/Makefile.am
+@@ -4,6 +4,6 @@
+ # overridden at config time.
+ sbindir = /sbin
+ 
+-sbin_SCRIPTS = osd_login
++dist_sbin_SCRIPTS = osd_login
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/utils/statd/Makefile.am b/utils/statd/Makefile.am
+index dc2bfc4..152b680 100644
+--- a/utils/statd/Makefile.am
++++ b/utils/statd/Makefile.am
+@@ -8,7 +8,7 @@ sbin_PROGRAMS	= statd sm-notify
+ dist_sbin_SCRIPTS	= start-statd
+ statd_SOURCES = callback.c notlist.c misc.c monitor.c hostname.c \
+ 	        simu.c stat.c statd.c svc_run.c rmtcall.c \
+-	        notlist.h statd.h system.h version.h
++	        notlist.h statd.h system.h
+ sm_notify_SOURCES = sm-notify.c
+ 
+ BUILT_SOURCES = $(GENFILES)
+@@ -20,7 +20,7 @@ sm_notify_LDADD = ../../support/nsm/libnsm.a \
+ 		  ../../support/nfs/libnfs.a \
+ 		  $(LIBNSL) $(LIBCAP) $(LIBTIRPC)
+ 
+-EXTRA_DIST = sim_sm_inter.x $(man8_MANS) COPYRIGHT simulate.c
++EXTRA_DIST = sim_sm_inter.x $(man8_MANS) simulate.c
+ 
+ if CONFIG_RPCGEN
+ RPCGEN	= $(top_builddir)/tools/rpcgen/rpcgen
diff --git a/nfs-utils.spec b/nfs-utils.spec
index e515754..2500a9f 100644
--- a/nfs-utils.spec
+++ b/nfs-utils.spec
@@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
 Name: nfs-utils
 URL: http://sourceforge.net/projects/nfs
 Version: 1.3.1
-Release: 2.4%{?dist}
+Release: 2.5%{?dist}
 Epoch: 1
 
 # group all 32bit related archs
@@ -15,8 +15,7 @@ Source2: nfs.sysconfig
 Source3: nfs-utils_env.sh
 Source4: lockd.conf
 
-Patch001: nfs-utils-1.3.2-rc2.patch
-Patch002: nfs-utils-1.3.1-mountd-dos.patch
+Patch001: nfs-utils-1.3.2.rc4.patch
 
 Patch100: nfs-utils-1.2.1-statdpath-man.patch
 Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
@@ -75,7 +74,6 @@ This package also contains the mount.nfs and umount.nfs program.
 %setup -q
 
 %patch001 -p1
-%patch002 -p1
 
 %patch100 -p1
 %patch101 -p1
@@ -285,6 +283,9 @@ fi
 /sbin/umount.nfs4
 
 %changelog
+* Sat Dec 13 2014 Steve Dickson <steved at redhat.com> 1.3.1-2.5
+- Updated to latest upstream RC release: nfs-utils-1-3-2-rc4 (bz 1164477)
+
 * Wed Dec  3 2014 Steve Dickson <steved at redhat.com> 1.3.1-2.4
 - Fixed typos in nfs-utils sysconfig files (bz 1170354)
 


More information about the scm-commits mailing list