Blob Blame History Raw
diff --git a/.gitignore b/.gitignore
index df791a83..682153d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,3 +86,5 @@ systemd/rpc-gssd.service
 cscope.*
 # generic editor backup et al
 *~
+# file generated by ctags
+tags
diff --git a/NEWS b/NEWS
index e70ae8ab..77872c5a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,32 +1,32 @@
 Significant changes for nfs-utils 1.1.0 - March/April 2007
 
- - rpc.lockd is gone.  One 3 old kernel releases need it.
- - rpc.rquotad is gone.  Use the one from the 'quota' package.
-   Everone else does.
+ - rpc.lockd is gone. One 3 old kernel releases need it.
+ - rpc.rquotad is gone. Use the one from the 'quota' package.
+   Everyone else does.
  - /sbin/{u,}mount.nfs{,4} are now installed so 'mount' will
    use these to mount nfs filesystems instead of internal code.
   + mount.nfs will check for 'statd' to be running when mounting
-    a filesystem which requires it.  If it is not running it will
+    a filesystem which requires it. If it is not running it will
     run "/usr/sbin/start-statd" to try to start it.
     If statd is not running and cannot be started, mount.nfs will
     refuse to mount the filesystem and will suggest the 'nolock'
     option.
  - Substantial changes to statd
   + The 'notify' process that must happen at boot has been split
-    into a separate program "sm-notify".  It ensures that it
-    only runs once even if you restart statd.  This is correct
+    into a separate program "sm-notify". It ensures that it
+    only runs once even if you restart statd. This is correct
     behaviour.
   + statd stores state in the files in /var/lib/nfs/sm/ so that
     if you kill and restart it, it will restore that state and
     continue working correctly.
   + statd makes more use of DNS lookup and should handle
-    multi-homed peers better.  In particular, files in
+    multi-homed peers better. In particular, files in
     /var/lib/nfs/sm/ are named with the Full Qualified Domain Name
     if available.
  - If you export a directory as 'crossmnt', all filesystems
    mounted beneath are automatically exported with the same
    options (unless explicitly exported with different options).
- - subtree_check is no-longer the default.  The default is now
+ - subtree_check is no-longer the default. The default is now
    no_subtree_check.
  - By default the system 'rpcgen' is used while building
    nfs-utils rather than the internal one.
@@ -43,14 +43,14 @@ Significant changes for nfs-utils 1.1.0 - March/April 2007
 
  - A new option, -n, was added to rpc.gssd which specifies that
    accesses by root should not use 'machine credentials' when
-   accessing NFS file systems mounted with Kerberos.  Using this
+   accessing NFS file systems mounted with Kerberos. Using this
    option allows the root user to access the NFS space using any
    Kerberos principal, rather than always using the machine
-   credentials.  However, its use also requires that root manually
+   credentials. However, its use also requires that root manually
    authenticate before attempting a mount with Kerberos.
 
    When rpc.gssd uses machine credentials, the selection algorithm has
-   been changed.  Instead of simply using the first "nfs/*" key in the
+   been changed. Instead of simply using the first "nfs/*" key in the
    keytab, the keytab is now searched for keys in the following
    defined order:
 
diff --git a/README b/README
index 5e982409..3b0e771f 100644
--- a/README
+++ b/README
@@ -25,7 +25,7 @@ Unpack the sources and run these commands:
     # ./configure
     # make
 
-To install binaries and documenation, run this command:
+To install binaries and documentation, run this command:
 
     # make install
 
@@ -40,7 +40,7 @@ Updating to the latest head after you've already got it.
 
 	git pull
 
-Building requires that autotools be installed.  To invoke them
+Building requires that autotools be installed. To invoke them
 simply
 
 	sh autogen.sh
@@ -70,7 +70,7 @@ scripts can be written to work correctly.
 3.1.  SERVER STARTUP
 
 
-   A/  mount -t nfsd /proc/fs/nfsd
+   A/  mount -t nfsd nfsd /proc/fs/nfsd
       This filesystem needs to be mount before most daemons,
       particularly exportfs, mountd, svcgssd, idmapd.
       It could be mounted once, or the script that starts each daemon
@@ -95,27 +95,27 @@ scripts can be written to work correctly.
 
    D/ rpc.statd --no-notify
        It is best if statd is started before nfsd though this isn't
-       critical.  Certainly it should be at most a few seconds after
+       critical. Certainly, it should be at most a few seconds after
        nfsd.
        When nfsd starts it will start lockd. If lockd then receives a
-       lock request it will communicate with statd.  If statd is not
+       lock request, it will communicate with statd. If statd is not
        running lockd will retry, but it won't wait forever for a
        reply.
        Note that if statd is started before nfsd, the --no-notify
-       option must be used.  If notify requests are sent out before
+       option must be used. If notify requests are sent out before
        nfsd start, clients may try to reclaim locks and, on finding
        that lockd isn't running, they will give up and never reclaim
        the lock.
        rpc.statd is only needed for NFSv2 and NFSv3 support.
 
    E/ rpc.nfsd
-       Starting nfsd will automatically start lockd.  The nfs server
+       Starting nfsd will automatically start lockd. The nfs server
        will now be fully active and respond to any requests from
        clients.
        
    F/ sm-notify
        This will notify any client which might have locks from before
-       a reboot to try to reclaim their locks.  This should start
+       a reboot to try to reclaim their locks. This should start
        immediately after rpc.nfsd is started so that clients have a
        chance to reclaim locks within the 90 second grace period.
        sm-notify is only needed for NFSv2 and NFSv3 support.
diff --git a/configure.ac b/configure.ac
index f1c46c5c..7672a760 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,18 +71,6 @@ AC_ARG_WITH(systemd,
 	AM_CONDITIONAL(INSTALL_SYSTEMD, [test "$use_systemd" = 1])
 	AC_SUBST(unitdir)
 
-modprobedir=/usr/lib/modprobe.d
-AC_ARG_WITH(modprobedir,
-	[AS_HELP_STRING([--with-modprobedir@<:@=modprobe-dir-path@:>@],[install modprobe config files @<:@Default: /usr/lib/modprobe.d@:>@])],
-	if test "$withval" != "no" ; then
-		modprobedir=$withval
-	else
-		modprobedir=
-	fi
-	)
-	AM_CONDITIONAL(INSTALL_MODPROBEDIR, [test -n "$modprobedir"])
-	AC_SUBST(modprobedir)
-
 AC_ARG_ENABLE(nfsv4,
 	[AS_HELP_STRING([--disable-nfsv4],[disable support for NFSv4 @<:@default=no@:>@])],
 	enable_nfsv4=$enableval,
@@ -249,6 +237,16 @@ AC_ARG_ENABLE(nfsdcld,
 	enable_nfsdcld=$enableval,
 	enable_nfsdcld="yes")
 
+AC_ARG_ENABLE(nfsrahead,
+	[AS_HELP_STRING([--disable-nfsrahead],[disable nfsrahead command @<:@default=no@:>@])],
+	enable_nfsrahead=$enableval,
+	enable_nfsrahead="yes")
+	AM_CONDITIONAL(CONFIG_NFSRAHEAD, [test "$enable_nfsrahead" = "yes" ])
+	if test "$enable_nfsrahead" = yes; then
+		dnl Check for -lmount
+		PKG_CHECK_MODULES([LIBMOUNT], [mount])
+	fi
+
 AC_ARG_ENABLE(nfsdcltrack,
 	[AS_HELP_STRING([--disable-nfsdcltrack],[disable NFSv4 clientid tracking programs @<:@default=no@:>@])],
 	enable_nfsdcltrack=$enableval,
@@ -678,12 +676,12 @@ AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4 $flg5"])
 # Make sure that $ACLOCAL_FLAGS are used during a rebuild
 AC_SUBST([ACLOCAL_AMFLAGS], ["-I $ac_macro_dir \$(ACLOCAL_FLAGS)"])
 
-# make _sysconfdir available for substituion in config files
+# make _sysconfdir available for substitution in config files
 # 2 "evals" needed late to expand variable names.
 AC_SUBST([_sysconfdir])
 AC_CONFIG_COMMANDS_PRE([eval eval _sysconfdir=$sysconfdir])
 
-# make _statedir available for substituion in config files
+# make _statedir available for substitution in config files
 # 2 "evals" needed late to expand variable names.
 AC_SUBST([_statedir])
 AC_CONFIG_COMMANDS_PRE([eval eval _statedir=$statedir])
@@ -695,7 +693,7 @@ else
 fi
 AC_SUBST(rpc_pipefsmount)
 
-# make _rpc_pipefsmount available for substituion in config files
+# make _rpc_pipefsmount available for substitution in config files
 # 2 "evals" needed late to expand variable names.
 AC_SUBST([_rpc_pipefsmount])
 AC_CONFIG_COMMANDS_PRE([eval eval _rpc_pipefsmount=$rpc_pipefsmount])
diff --git a/support/export/auth.c b/support/export/auth.c
index 03ce4b8a..2d7960f1 100644
--- a/support/export/auth.c
+++ b/support/export/auth.c
@@ -82,7 +82,7 @@ check_useipaddr(void)
 }
 
 unsigned int
-auth_reload()
+auth_reload(void)
 {
 	struct stat		stb;
 	static ino_t		last_inode;
diff --git a/support/export/cache.c b/support/export/cache.c
index a5823e92..0a37703b 100644
--- a/support/export/cache.c
+++ b/support/export/cache.c
@@ -346,27 +346,27 @@ static int uuid_by_path(char *path, int type, size_t uuidlen, char *uuid)
 
 	/* Possible sources of uuid are
 	 * - blkid uuid
-	 * - statfs64 uuid
+	 * - statfs uuid
 	 *
-	 * On some filesystems (e.g. vfat) the statfs64 uuid is simply an
+	 * On some filesystems (e.g. vfat) the statfs uuid is simply an
 	 * encoding of the device that the filesystem is mounted from, so
 	 * it we be very bad to use that (as device numbers change).  blkid
 	 * must be preferred.
-	 * On other filesystems (e.g. btrfs) the statfs64 uuid contains
+	 * On other filesystems (e.g. btrfs) the statfs uuid contains
 	 * important info that the blkid uuid cannot contain:  This happens
 	 * when multiple subvolumes are exported (they have the same
-	 * blkid uuid but different statfs64 uuids).
+	 * blkid uuid but different statfs uuids).
 	 * We rely on get_uuid_blkdev *knowing* which is which and not returning
-	 * a uuid for filesystems where the statfs64 uuid is better.
+	 * a uuid for filesystems where the statfs uuid is better.
 	 *
 	 */
-	struct statfs64 st;
+	struct statfs st;
 	char fsid_val[17];
 	const char *blkid_val = NULL;
 	const char *val;
 	int rc;
 
-	rc = nfsd_path_statfs64(path, &st);
+	rc = nfsd_path_statfs(path, &st);
 
 	if (type == 0 && rc == 0) {
 		const unsigned long *bad;
@@ -410,12 +410,16 @@ static char *next_mnt(void **v, char *p)
 		*v = f;
 	} else
 		f = *v;
-	while ((me = getmntent(f)) != NULL && l > 1) {
+	while ((me = getmntent(f)) != NULL && l >= 1) {
 		char *mnt_dir = nfsd_path_strip_root(me->mnt_dir);
 
 		if (!mnt_dir)
 			continue;
 
+		/* Everything below "/" is a proper sub-mount */
+		if (strcmp(p, "/") == 0)
+			return mnt_dir;
+
 		if (strncmp(mnt_dir, p, l) == 0 && mnt_dir[l] == '/')
 			return mnt_dir;
 	}
@@ -932,6 +936,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep)
 	release_replicas(servers);
 }
 #endif
+
 static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask)
 {
 	struct sec_entry *p;
@@ -949,7 +954,20 @@ static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_m
 		qword_addint(bp, blen, p->flav->fnum);
 		qword_addint(bp, blen, p->flags & flag_mask);
 	}
+}
+
+static void write_xprtsec(char **bp, int *blen, struct exportent *ep)
+{
+	struct xprtsec_entry *p;
+
+	for (p = ep->e_xprtsec; p->info; p++);
+	if (p == ep->e_xprtsec)
+		return;
 
+	qword_add(bp, blen, "xprtsec");
+	qword_addint(bp, blen, p - ep->e_xprtsec);
+	for (p = ep->e_xprtsec; p->info; p++)
+		qword_addint(bp, blen, p->info->number);
 }
 
 static int dump_to_cache(int f, char *buf, int blen, char *domain,
@@ -992,6 +1010,7 @@ static int dump_to_cache(int f, char *buf, int blen, char *domain,
 			qword_add(&bp, &blen, "uuid");
 			qword_addhex(&bp, &blen, u, 16);
 		}
+		write_xprtsec(&bp, &blen, exp);
 		xlog(D_AUTH, "granted access to %s for %s",
 		     path, *domain == '$' ? domain+1 : domain);
 	} else {
diff --git a/support/export/client.c b/support/export/client.c
index ea4f89d3..79164fef 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -699,6 +699,9 @@ check_netgroup(const nfs_client *clp, const struct addrinfo *ai)
 
 	/* check whether the IP itself is in the netgroup */
 	ip = calloc(INET6_ADDRSTRLEN, 1);
+	if (ip == NULL)
+		goto out;
+
 	if (inet_ntop(ai->ai_family, &(((struct sockaddr_in *)ai->ai_addr)->sin_addr), ip, INET6_ADDRSTRLEN) == ip) {
 		if (innetgr(netgroup, ip, NULL, NULL)) {
 			free(hname);
diff --git a/support/export/v4clients.c b/support/export/v4clients.c
index 5f15b614..32302512 100644
--- a/support/export/v4clients.c
+++ b/support/export/v4clients.c
@@ -26,7 +26,7 @@ void v4clients_init(void)
 {
 	struct stat sb;
 
-	if (!stat("/proc/fs/nfsd/clients", &sb) == 0 ||
+	if (stat("/proc/fs/nfsd/clients", &sb) != 0 ||
 	    !S_ISDIR(sb.st_mode))
 		return;
 	if (clients_fd >= 0)
diff --git a/support/export/v4root.c b/support/export/v4root.c
index c12a7d85..fbb0ad5f 100644
--- a/support/export/v4root.c
+++ b/support/export/v4root.c
@@ -198,7 +198,7 @@ static int v4root_add_parents(nfs_export *exp)
  * looking for components of the v4 mount.
  */
 void
-v4root_set()
+v4root_set(void)
 {
 	nfs_export	*exp;
 	int	i;
diff --git a/support/export/xtab.c b/support/export/xtab.c
index c888a80a..e210ca99 100644
--- a/support/export/xtab.c
+++ b/support/export/xtab.c
@@ -135,7 +135,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
 }
 
 int
-xtab_export_write()
+xtab_export_write(void)
 {
 	return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1);
 }
diff --git a/support/include/nfs/export.h b/support/include/nfs/export.h
index 0eca828e..be5867cf 100644
--- a/support/include/nfs/export.h
+++ b/support/include/nfs/export.h
@@ -40,4 +40,18 @@
 #define NFSEXP_OLD_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
 					| NFSEXP_ALLSQUASH)
 
+/*
+ * Transport layer security policies that are permitted to access
+ * an export
+ */
+#define NFSEXP_XPRTSEC_NONE	0x0001
+#define NFSEXP_XPRTSEC_TLS	0x0002
+#define NFSEXP_XPRTSEC_MTLS	0x0004
+
+#define NFSEXP_XPRTSEC_NUM	(3)
+
+#define NFSEXP_XPRTSEC_ALL	(NFSEXP_XPRTSEC_NONE | \
+				 NFSEXP_XPRTSEC_TLS | \
+				 NFSEXP_XPRTSEC_MTLS)
+
 #endif /* _NSF_EXPORT_H */
diff --git a/support/include/nfsd_path.h b/support/include/nfsd_path.h
index 3b73aadd..aa1e1dd0 100644
--- a/support/include/nfsd_path.h
+++ b/support/include/nfsd_path.h
@@ -7,7 +7,7 @@
 #include <sys/stat.h>
 
 struct file_handle;
-struct statfs64;
+struct statfs;
 
 void 		nfsd_path_init(void);
 
@@ -18,8 +18,8 @@ char *		nfsd_path_prepend_dir(const char *dir, const char *pathname);
 int 		nfsd_path_stat(const char *pathname, struct stat *statbuf);
 int 		nfsd_path_lstat(const char *pathname, struct stat *statbuf);
 
-int		nfsd_path_statfs64(const char *pathname,
-				   struct statfs64 *statbuf);
+int		nfsd_path_statfs(const char *pathname,
+				   struct statfs *statbuf);
 
 char *		nfsd_realpath(const char *path, char *resolved_path);
 
diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index 6faba71b..61c19933 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -62,6 +62,18 @@ struct sec_entry {
 	int flags;
 };
 
+#define XPRTSECMODE_COUNT 3
+
+struct xprtsec_info {
+	const char		*name;
+	int			number;
+};
+
+struct xprtsec_entry {
+	const struct xprtsec_info *info;
+	int			flags;
+};
+
 /*
  * Data related to a single exports entry as returned by getexportent.
  * FIXME: export options should probably be parsed at a later time to
@@ -83,6 +95,7 @@ struct exportent {
 	char *          e_fslocdata;
 	char *		e_uuid;
 	struct sec_entry e_secinfo[SECFLAVOR_COUNT+1];
+	struct xprtsec_entry e_xprtsec[XPRTSECMODE_COUNT + 1];
 	unsigned int	e_ttl;
 	char *		e_realpath;
 };
@@ -99,6 +112,7 @@ struct rmtabent {
 void			setexportent(char *fname, char *type);
 struct exportent *	getexportent(int,int);
 void 			secinfo_show(FILE *fp, struct exportent *ep);
+void			xprtsecinfo_show(FILE *fp, struct exportent *ep);
 void			putexportent(struct exportent *xep);
 void			endexportent(void);
 struct exportent *	mkexportent(char *hname, char *path, char *opts);
diff --git a/support/junction/junction.c b/support/junction/junction.c
index 41cce261..0628bb0f 100644
--- a/support/junction/junction.c
+++ b/support/junction/junction.c
@@ -63,7 +63,7 @@ junction_open_path(const char *pathname, int *fd)
 	if (pathname == NULL || fd == NULL)
 		return FEDFS_ERR_INVAL;
 
-	tmp = open(pathname, O_DIRECTORY);
+	tmp = open(pathname, O_PATH|O_DIRECTORY);
 	if (tmp == -1) {
 		switch (errno) {
 		case EPERM:
@@ -93,7 +93,7 @@ junction_is_directory(int fd, const char *path)
 {
 	struct stat stb;
 
-	if (fstat(fd, &stb) == -1) {
+	if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
 		xlog(D_GENERAL, "%s: failed to stat %s: %m",
 				__func__, path);
 		return FEDFS_ERR_ACCESS;
@@ -121,7 +121,7 @@ junction_is_sticky_bit_set(int fd, const char *path)
 {
 	struct stat stb;
 
-	if (fstat(fd, &stb) == -1) {
+	if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
 		xlog(D_GENERAL, "%s: failed to stat %s: %m",
 				__func__, path);
 		return FEDFS_ERR_ACCESS;
@@ -155,7 +155,7 @@ junction_set_sticky_bit(int fd, const char *path)
 {
 	struct stat stb;
 
-	if (fstat(fd, &stb) == -1) {
+	if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
 		xlog(D_GENERAL, "%s: failed to stat %s: %m",
 			__func__, path);
 		return FEDFS_ERR_ACCESS;
@@ -393,7 +393,7 @@ junction_get_mode(const char *pathname, mode_t *mode)
 	if (retval != FEDFS_OK)
 		return retval;
 
-	if (fstat(fd, &stb) == -1) {
+	if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) {
 		xlog(D_GENERAL, "%s: failed to stat %s: %m",
 			__func__, pathname);
 		(void)close(fd);
diff --git a/support/misc/nfsd_path.c b/support/misc/nfsd_path.c
index 65e53c13..c3dea4f0 100644
--- a/support/misc/nfsd_path.c
+++ b/support/misc/nfsd_path.c
@@ -184,46 +184,46 @@ nfsd_path_lstat(const char *pathname, struct stat *statbuf)
 	return nfsd_run_stat(nfsd_wq, nfsd_lstatfunc, pathname, statbuf);
 }
 
-struct nfsd_statfs64_data {
+struct nfsd_statfs_data {
 	const char *pathname;
-	struct statfs64 *statbuf;
+	struct statfs *statbuf;
 	int ret;
 	int err;
 };
 
 static void
-nfsd_statfs64func(void *data)
+nfsd_statfsfunc(void *data)
 {
-	struct nfsd_statfs64_data *d = data;
+	struct nfsd_statfs_data *d = data;
 
-	d->ret = statfs64(d->pathname, d->statbuf);
+	d->ret = statfs(d->pathname, d->statbuf);
 	if (d->ret < 0)
 		d->err = errno;
 }
 
 static int
-nfsd_run_statfs64(struct xthread_workqueue *wq,
+nfsd_run_statfs(struct xthread_workqueue *wq,
 		  const char *pathname,
-		  struct statfs64 *statbuf)
+		  struct statfs *statbuf)
 {
-	struct nfsd_statfs64_data data = {
+	struct nfsd_statfs_data data = {
 		pathname,
 		statbuf,
 		0,
 		0
 	};
-	xthread_work_run_sync(wq, nfsd_statfs64func, &data);
+	xthread_work_run_sync(wq, nfsd_statfsfunc, &data);
 	if (data.ret < 0)
 		errno = data.err;
 	return data.ret;
 }
 
 int
-nfsd_path_statfs64(const char *pathname, struct statfs64 *statbuf)
+nfsd_path_statfs(const char *pathname, struct statfs *statbuf)
 {
 	if (!nfsd_wq)
-		return statfs64(pathname, statbuf);
-	return nfsd_run_statfs64(nfsd_wq, pathname, statbuf);
+		return statfs(pathname, statbuf);
+	return nfsd_run_statfs(nfsd_wq, pathname, statbuf);
 }
 
 struct nfsd_realpath_data {
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index 2c8f0752..da8ace3a 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -99,6 +99,7 @@ static void init_exportent (struct exportent *ee, int fromkernel)
 	ee->e_fslocmethod = FSLOC_NONE;
 	ee->e_fslocdata = NULL;
 	ee->e_secinfo[0].flav = NULL;
+	ee->e_xprtsec[0].info = NULL;
 	ee->e_nsquids = 0;
 	ee->e_nsqgids = 0;
 	ee->e_uuid = NULL;
@@ -122,7 +123,7 @@ getexportent(int fromkernel, int fromexports)
 	if (first || (ok = getexport(exp, sizeof(exp))) == 0) {
 		has_default_opts = 0;
 		has_default_subtree_opts = 0;
-	
+
 		init_exportent(&def_ee, fromkernel);
 
 		ok = getpath(def_ee.e_path, sizeof(def_ee.e_path));
@@ -146,7 +147,7 @@ getexportent(int fromkernel, int fromexports)
 	if (exp[0] == '-' && !fromkernel) {
 		if (parseopts(exp + 1, &def_ee, 0, &has_default_subtree_opts) < 0)
 			return NULL;
-		
+
 		has_default_opts = 1;
 
 		ok = getexport(exp, sizeof(exp));
@@ -239,7 +240,6 @@ void secinfo_show(FILE *fp, struct exportent *ep)
 	if (ep->e_secinfo[0].flav == NULL)
 		secinfo_addflavor(find_flavor("sys"), ep);
 	for (p1=ep->e_secinfo; p1->flav; p1=p2) {
-
 		fprintf(fp, ",sec=%s", p1->flav->flavour);
 		for (p2=p1+1; (p2->flav != NULL) && (p1->flags == p2->flags);
 								p2++) {
@@ -249,6 +249,17 @@ void secinfo_show(FILE *fp, struct exportent *ep)
 	}
 }
 
+void xprtsecinfo_show(FILE *fp, struct exportent *ep)
+{
+	struct xprtsec_entry *p1, *p2;
+
+	for (p1 = ep->e_xprtsec; p1->info; p1 = p2) {
+		fprintf(fp, ",xprtsec=%s", p1->info->name);
+		for (p2 = p1 + 1; p2->info && (p1->flags == p2->flags); p2++)
+			fprintf(fp, ":%s", p2->info->name);
+	}
+}
+
 static void
 fprintpath(FILE *fp, const char *path)
 {
@@ -345,6 +356,7 @@ putexportent(struct exportent *ep)
 	}
 	fprintf(fp, "anonuid=%d,anongid=%d", ep->e_anonuid, ep->e_anongid);
 	secinfo_show(fp, ep);
+	xprtsecinfo_show(fp, ep);
 	fprintf(fp, ")\n");
 }
 
@@ -483,6 +495,75 @@ static unsigned int parse_flavors(char *str, struct exportent *ep)
 	return out;
 }
 
+static const struct xprtsec_info xprtsec_name2info[] = {
+	{ "none",	NFSEXP_XPRTSEC_NONE },
+	{ "tls",	NFSEXP_XPRTSEC_TLS },
+	{ "mtls",	NFSEXP_XPRTSEC_MTLS },
+	{ NULL,		0 }
+};
+
+static const struct xprtsec_info *find_xprtsec_info(const char *name)
+{
+	const struct xprtsec_info *info;
+
+	for (info = xprtsec_name2info; info->name; info++)
+		if (strcmp(info->name, name) == 0)
+			return info;
+	return NULL;
+}
+
+/*
+ * Append the given xprtsec mode to the exportent's e_xprtsec array,
+ * or do nothing if it's already there. Returns the index of flavor in
+ * the resulting array in any case.
+ */
+static int xprtsec_addmode(const struct xprtsec_info *info, struct exportent *ep)
+{
+	struct xprtsec_entry *p;
+
+	for (p = ep->e_xprtsec; p->info; p++)
+		if (p->info == info || p->info->number == info->number)
+			return p - ep->e_xprtsec;
+
+	if (p - ep->e_xprtsec >= XPRTSECMODE_COUNT) {
+		xlog(L_ERROR, "more than %d xprtsec modes on an export\n",
+			XPRTSECMODE_COUNT);
+		return -1;
+	}
+	p->info = info;
+	p->flags = ep->e_flags;
+	(p + 1)->info = NULL;
+	return p - ep->e_xprtsec;
+}
+
+/*
+ * @str is a colon seperated list of transport layer security modes.
+ * Their order is recorded in @ep, and a bitmap corresponding to the
+ * list is returned.
+ *
+ * A zero return indicates an error.
+ */
+static unsigned int parse_xprtsec(char *str, struct exportent *ep)
+{
+	unsigned int out = 0;
+	char *name;
+
+	while ((name = strsep(&str, ":"))) {
+		const struct xprtsec_info *info = find_xprtsec_info(name);
+		int bit;
+
+		if (!info) {
+			xlog(L_ERROR, "unknown xprtsec mode %s\n", name);
+			return 0;
+		}
+		bit = xprtsec_addmode(info, ep);
+		if (bit < 0)
+			return 0;
+		out |= 1 << bit;
+	}
+	return out;
+}
+
 /* Sets the bits in @mask for the appropriate security flavor flags. */
 static void setflags(int mask, unsigned int active, struct exportent *ep)
 {
@@ -621,7 +702,7 @@ parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr)
 			ep->e_anonuid = strtol(opt+8, &oe, 10);
 			if (opt[8]=='\0' || *oe != '\0') {
 				xlog(L_ERROR, "%s: %d: bad anonuid \"%s\"\n",
-				     flname, flline, opt);	
+				     flname, flline, opt);
 bad_option:
 				free(opt);
 				return -1;
@@ -631,7 +712,7 @@ bad_option:
 			ep->e_anongid = strtol(opt+8, &oe, 10);
 			if (opt[8]=='\0' || *oe != '\0') {
 				xlog(L_ERROR, "%s: %d: bad anongid \"%s\"\n",
-				     flname, flline, opt);	
+				     flname, flline, opt);
 				goto bad_option;
 			}
 		} else if (strncmp(opt, "squash_uids=", 12) == 0) {
@@ -649,13 +730,13 @@ bad_option:
 				setflags(NFSEXP_FSID, active, ep);
 			} else {
 				ep->e_fsid = strtoul(opt+5, &oe, 0);
-				if (opt[5]!='\0' && *oe == '\0') 
+				if (opt[5]!='\0' && *oe == '\0')
 					setflags(NFSEXP_FSID, active, ep);
 				else if (valid_uuid(opt+5))
 					ep->e_uuid = strdup(opt+5);
 				else {
 					xlog(L_ERROR, "%s: %d: bad fsid \"%s\"\n",
-					     flname, flline, opt);	
+					     flname, flline, opt);
 					goto bad_option;
 				}
 			}
@@ -688,6 +769,9 @@ bad_option:
 			active = parse_flavors(opt+4, ep);
 			if (!active)
 				goto bad_option;
+		} else if (strncmp(opt, "xprtsec=", 8) == 0) {
+			if (!parse_xprtsec(opt + 8, ep))
+				goto bad_option;
 		} else {
 			xlog(L_ERROR, "%s:%d: unknown keyword \"%s\"\n",
 					flname, flline, opt);
@@ -709,7 +793,7 @@ out:
 	if (warn && !had_subtree_opt)
 		xlog(L_WARNING, "%s [%d]: Neither 'subtree_check' or 'no_subtree_check' specified for export \"%s:%s\".\n"
 				"  Assuming default behaviour ('no_subtree_check').\n"
-		     		"  NOTE: this default has changed since nfs-utils version 1.0.x\n",
+				"  NOTE: this default has changed since nfs-utils version 1.0.x\n",
 
 				flname, flline,
 				ep->e_hostname, ep->e_path);
diff --git a/support/nfs/xlog.c b/support/nfs/xlog.c
index e5861b9d..fa125cef 100644
--- a/support/nfs/xlog.c
+++ b/support/nfs/xlog.c
@@ -46,11 +46,13 @@ int export_errno = 0;
 
 static void	xlog_toggle(int sig);
 static struct xlog_debugfac	debugnames[] = {
+	{ "0",		0, },
 	{ "general",	D_GENERAL, },
 	{ "call",	D_CALL, },
 	{ "auth",	D_AUTH, },
 	{ "parse",	D_PARSE, },
 	{ "all",	D_ALL, },
+	{ "1",		D_ALL, },
 	{ NULL,		0, },
 };
 
@@ -119,13 +121,14 @@ xlog_sconfig(char *kind, int on)
 {
 	struct xlog_debugfac	*tbl = debugnames;
 
-	while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind)) 
+	while (tbl->df_name != NULL && strcasecmp(tbl->df_name, kind))
 		tbl++;
 	if (!tbl->df_name) {
 		xlog (L_WARNING, "Invalid debug facility: %s\n", kind);
 		return;
 	}
-	xlog_config(tbl->df_fac, on);
+	if (tbl->df_fac)
+		xlog_config(tbl->df_fac, on);
 }
 
 void
diff --git a/support/nfsidmap/regex.c b/support/nfsidmap/regex.c
index 958b4ac8..8424179f 100644
--- a/support/nfsidmap/regex.c
+++ b/support/nfsidmap/regex.c
@@ -542,7 +542,7 @@ struct trans_func regex_trans = {
 	.gss_princ_to_grouplist	= regex_gss_princ_to_grouplist,
 };
 
-struct trans_func *libnfsidmap_plugin_init()
+struct trans_func *libnfsidmap_plugin_init(void)
 {
 	return (&regex_trans);
 }
diff --git a/systemd/50-nfs.conf b/systemd/50-nfs.conf
deleted file mode 100644
index 19e8ee73..00000000
--- a/systemd/50-nfs.conf
+++ /dev/null
@@ -1,16 +0,0 @@
-# Ensure all NFS systctl settings get applied when modules load
-
-# sunrpc module supports "sunrpc.*" sysctls
-install sunrpc /sbin/modprobe --ignore-install sunrpc $CMDLINE_OPTS && { /sbin/sysctl -q --pattern sunrpc --system; exit 0; }
-
-# rpcrdma module supports sunrpc.svc_rdma.*
-install rpcrdma /sbin/modprobe --ignore-install rpcrdma $CMDLINE_OPTS && { /sbin/sysctl -q --pattern sunrpc.svc_rdma --system; exit 0; }
-
-# lockd module supports "fs.nfs.nlm*" and "fs.nfs.nsm*" sysctls
-install lockd /sbin/modprobe --ignore-install lockd $CMDLINE_OPTS && { /sbin/sysctl -q --pattern fs.nfs.n[sl]m --system; exit 0; }
-
-# nfsv4 module supports "fs.nfs.*" sysctls (nfs_callback_tcpport and idmap_cache_timeout)
-install nfsv4 /sbin/modprobe --ignore-install nfsv4 $CMDLINE_OPTS && { /sbin/sysctl -q --pattern 'fs.nfs.(nfs_callback_tcpport|idmap_cache_timeout)' --system; exit 0; }
-
-# nfs module supports "fs.nfs.*" sysctls
-install nfs /sbin/modprobe --ignore-install nfs $CMDLINE_OPTS && { /sbin/sysctl -q --pattern fs.nfs --system; exit 0; }
diff --git a/systemd/60-nfs.rules b/systemd/60-nfs.rules
new file mode 100644
index 00000000..188423c1
--- /dev/null
+++ b/systemd/60-nfs.rules
@@ -0,0 +1,21 @@
+# Ensure all NFS systctl settings get applied when modules load
+
+# sunrpc module supports "sunrpc.*" sysctls
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="sunrpc", \
+  RUN+="/sbin/sysctl -q --pattern ^sunrpc --system"
+
+# rpcrdma module supports sunrpc.svc_rdma.*
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="rpcrdma", \
+  RUN+="/sbin/sysctl -q --pattern ^sunrpc.svc_rdma --system"
+
+# lockd module supports "fs.nfs.nlm*" and "fs.nfs.nsm*" sysctls
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="lockd", \
+  RUN+="/sbin/sysctl -q --pattern ^fs.nfs.n[sl]m --system"
+
+# nfsv4 module supports "fs.nfs.*" sysctls (nfs_callback_tcpport and idmap_cache_timeout)
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="nfsv4", \
+  RUN+="/sbin/sysctl -q --pattern ^fs.nfs.(nfs_callback_tcpport|idmap_cache_timeout) --system"
+
+# nfs module supports "fs.nfs.*" sysctls
+ACTION=="add", SUBSYSTEM=="module", KERNEL=="nfs", \
+  RUN+="/sbin/sysctl -q --pattern ^fs.nfs --system"
diff --git a/systemd/Makefile.am b/systemd/Makefile.am
index 7b5ab84b..577c6a22 100644
--- a/systemd/Makefile.am
+++ b/systemd/Makefile.am
@@ -2,7 +2,8 @@
 
 MAINTAINERCLEANFILES = Makefile.in
 
-modprobe_files = 50-nfs.conf
+udev_rulesdir = /usr/lib/udev/rules.d/
+udev_files = 60-nfs.rules
 
 unit_files =  \
     nfs-client.target \
@@ -53,7 +54,7 @@ endif
 
 man5_MANS	= nfs.conf.man
 man7_MANS	= nfs.systemd.man
-EXTRA_DIST = $(unit_files) $(modprobe_files) $(man5_MANS) $(man7_MANS)
+EXTRA_DIST = $(unit_files) $(udev_files) $(man5_MANS) $(man7_MANS)
 
 generator_dir = $(unitdir)/../system-generators
 
@@ -75,14 +76,10 @@ rpc_pipefs_generator_LDADD = ../support/nfs/libnfs.la
 
 if INSTALL_SYSTEMD
 genexec_PROGRAMS = nfs-server-generator rpc-pipefs-generator
-install-data-hook: $(unit_files) $(modprobe_files)
+install-data-hook: $(unit_files) $(udev_files)
 	mkdir -p $(DESTDIR)/$(unitdir)
 	cp $(unit_files) $(DESTDIR)/$(unitdir)
 	cp $(rpc_pipefs_mount_file) $(DESTDIR)/$(unitdir)/$(rpc_pipefsmount)
-else
-install-data-hook: $(modprobe_files)
-endif
-if INSTALL_MODPROBEDIR
-	mkdir -p $(DESTDIR)$(modprobedir)
-	cp $(modprobe_files) $(DESTDIR)$(modprobedir)
+	mkdir -p $(DESTDIR)/$(udev_rulesdir)
+	cp $(udev_files) $(DESTDIR)/$(udev_rulesdir)
 endif
diff --git a/systemd/auth-rpcgss-module.service b/systemd/auth-rpcgss-module.service
index 45482833..4a69a7b7 100644
--- a/systemd/auth-rpcgss-module.service
+++ b/systemd/auth-rpcgss-module.service
@@ -8,8 +8,9 @@
 Description=Kernel Module supporting RPCSEC_GSS
 DefaultDependencies=no
 Before=gssproxy.service rpc-svcgssd.service rpc-gssd.service
-Wants=gssproxy.service rpc-svcgssd.service rpc-gssd.service
+Wants=gssproxy.service rpc-gssd.service
 ConditionPathExists=/etc/krb5.keytab
+ConditionVirtualization=!container
 
 [Service]
 Type=oneshot
diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
index b432f910..2cdd7868 100644
--- a/systemd/nfs-server.service
+++ b/systemd/nfs-server.service
@@ -15,7 +15,7 @@ After=nfsdcld.service
 Before=rpc-statd-notify.service
 
 # GSS services dependencies and ordering
-Wants=auth-rpcgss-module.service
+Wants=auth-rpcgss-module.service rpc-svcgssd.service
 After=rpc-gssd.service gssproxy.service rpc-svcgssd.service
 
 [Service]
diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
index e74083e9..bfd3380f 100644
--- a/systemd/nfs.conf.man
+++ b/systemd/nfs.conf.man
@@ -98,6 +98,12 @@ value, which can be one or more from the list
 .BR parse ,
 .BR all .
 When a list is given, the members should be comma-separated.
+The values
+.BR 0
+and
+.BR 1
+are also accepted, with '0' making no changes to the debug level, and '1' equivalent to specifying 'all'.
+
 .TP
 .B general
 Recognized values:
@@ -166,6 +172,7 @@ for details.
 Recognized values:
 .BR threads ,
 .BR host ,
+.BR scope ,
 .BR port ,
 .BR grace-time ,
 .BR lease-time ,
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 40c17c37..48fd0cdf 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -12,6 +12,10 @@ if CONFIG_NFSDCLD
 OPTDIRS += nfsdclddb
 endif
 
-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts nfsrahead $(OPTDIRS)
+if CONFIG_NFSRAHEAD
+OPTDIRS += nfsrahead
+endif
+
+SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
index 1df74ba8..85294fb9 100755
--- a/tools/nfs-iostat/nfs-iostat.py
+++ b/tools/nfs-iostat/nfs-iostat.py
@@ -43,7 +43,7 @@ NfsEventCounters = [
     'vfspermission',
     'vfsupdatepage',
     'vfsreadpage',
-    'vfsreadpages',
+    'vfsreadpages', # or vfsreadahead in statvers=1.2 or above
     'vfswritepage',
     'vfswritepages',
     'vfsreaddir',
@@ -86,14 +86,14 @@ class DeviceData:
             self.__nfs_data['export'] = words[1]
             self.__nfs_data['mountpoint'] = words[4]
             self.__nfs_data['fstype'] = words[7]
-            if words[7] == 'nfs':
-                self.__nfs_data['statvers'] = words[8]
+            if words[7] == 'nfs' or words[7] == 'nfs4':
+                self.__nfs_data['statvers'] = float(words[8].split('=',1)[1])
         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] == 'nfs':
-                self.__nfs_data['statvers'] = words[7]
+                self.__nfs_data['statvers'] = float(words[7].split('=',1)[1])
         elif words[0] == 'age:':
             self.__nfs_data['age'] = int(words[1])
         elif words[0] == 'opts:':
@@ -294,8 +294,11 @@ class DeviceData:
         print()
         print('%d nfs_readpage() calls read %d pages' % \
             (vfsreadpage, vfsreadpage))
-        print('%d nfs_readpages() calls read %d pages' % \
-            (vfsreadpages, pages_read - vfsreadpage))
+        multipageread = "readpages"
+        if self.__nfs_data['statvers'] >= 1.2:
+            multipageread = "readahead"
+        print('%d nfs_%s() calls read %d pages' % \
+            (vfsreadpages, multipageread, pages_read - vfsreadpage))
         if vfsreadpages != 0:
             print('(%.1f pages per call)' % \
                 (float(pages_read - vfsreadpage) / vfsreadpages))
diff --git a/tools/nfsrahead/Makefile.am b/tools/nfsrahead/Makefile.am
index 845ea0d5..7e08233a 100644
--- a/tools/nfsrahead/Makefile.am
+++ b/tools/nfsrahead/Makefile.am
@@ -1,6 +1,6 @@
 libexec_PROGRAMS = nfsrahead
 nfsrahead_SOURCES = main.c
-nfsrahead_LDFLAGS= -lmount
+nfsrahead_LDFLAGS= $(LIBMOUNT_LIBS)
 nfsrahead_LDADD = ../../support/nfs/libnfsconf.la
 
 man5_MANS = nfsrahead.man
diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c
index c83c6f71..8a11cf1a 100644
--- a/tools/nfsrahead/main.c
+++ b/tools/nfsrahead/main.c
@@ -167,7 +167,7 @@ int main(int argc, char **argv)
 		if ((ret = get_device_info(argv[optind], &device)) == 0)
 			break;
 
-	if (ret != 0) {
+	if (ret != 0 || device.fstype == NULL) {
 		xlog(D_GENERAL, "unable to find device %s\n", argv[optind]);
 		goto out;
 	}
diff --git a/tools/rpcdebug/rpcdebug.c b/tools/rpcdebug/rpcdebug.c
index 68206cc5..ec05179e 100644
--- a/tools/rpcdebug/rpcdebug.c
+++ b/tools/rpcdebug/rpcdebug.c
@@ -257,7 +257,7 @@ get_flags(char *module)
 		perror(filename);
 		exit(1);
 	}
-	if ((len = read(sysfd, buffer, sizeof(buffer))) < 0) {
+	if ((len = read(sysfd, buffer, sizeof(buffer))) <= 0) {
 		perror("read");
 		exit(1);
 	}
diff --git a/utils/blkmapd/device-discovery.c b/utils/blkmapd/device-discovery.c
index 2736ac89..a565fdbd 100644
--- a/utils/blkmapd/device-discovery.c
+++ b/utils/blkmapd/device-discovery.c
@@ -187,10 +187,7 @@ static void bl_add_disk(char *filepath)
 	}
 
 	if (disk && diskpath) {
-		if (serial) {
-			free(serial->data);
-			free(serial);
-		}
+		bl_free_scsi_string(serial);
 		return;
 	}
 
@@ -228,10 +225,7 @@ static void bl_add_disk(char *filepath)
 			disk->size = size;
 			disk->valid_path = path;
 		}
-		if (serial) {
-			free(serial->data);
-			free(serial);
-		}
+		bl_free_scsi_string(serial);
 	}
 	return;
 
@@ -241,10 +235,7 @@ static void bl_add_disk(char *filepath)
 			free(path->full_path);
 		free(path);
 	}
-	if (serial) {
-		free(serial->data);
-		free(serial);
-	}
+	bl_free_scsi_string(serial);
 	return;
 }
 
@@ -462,7 +453,7 @@ static void sig_die(int signal)
 		unlink(PID_FILE);
 	}
 	BL_LOG_ERR("exit on signal(%d)\n", signal);
-	exit(1);
+	exit(0);
 }
 static void usage(void)
 {
@@ -507,28 +498,44 @@ int main(int argc, char **argv)
 	if (fg) {
 		openlog("blkmapd", LOG_PERROR, 0);
 	} else {
-		if (daemon(0, 0) != 0) {
-			fprintf(stderr, "Daemonize failed\n");
+		pid_t pid = fork();
+		if (pid < 0) {
+			BL_LOG_ERR("fork error\n");
 			exit(1);
+		} else if (pid != 0) {
+			pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644);
+			if (pidfd < 0) {
+				BL_LOG_ERR("Create pid file %s failed\n", PID_FILE);
+				exit(1);
+			}
+
+			if (lockf(pidfd, F_TLOCK, 0) < 0) {
+				BL_LOG_ERR("Already running; Exiting!");
+				close(pidfd);
+				exit(1);
+			}
+			if (ftruncate(pidfd, 0) < 0)
+				BL_LOG_ERR("ftruncate on %s failed: m\n", PID_FILE);
+			sprintf(pidbuf, "%d\n", pid);
+			if (write(pidfd, pidbuf, strlen(pidbuf)) != (ssize_t)strlen(pidbuf))
+				BL_LOG_ERR("write on %s failed: m\n", PID_FILE);
+			exit(0);
 		}
 
-		openlog("blkmapd", LOG_PID, 0);
-		pidfd = open(PID_FILE, O_WRONLY | O_CREAT, 0644);
-		if (pidfd < 0) {
-			BL_LOG_ERR("Create pid file %s failed\n", PID_FILE);
-			exit(1);
+		(void)setsid();
+		if (chdir("/")) {
+			BL_LOG_ERR("chdir error\n");
 		}
+		int fd = open("/dev/null", O_RDWR, 0);
+		if (fd >= 0) {
+		    (void)dup2(fd, STDIN_FILENO);
+		    (void)dup2(fd, STDOUT_FILENO);
+		    (void)dup2(fd, STDERR_FILENO);
 
-		if (lockf(pidfd, F_TLOCK, 0) < 0) {
-			BL_LOG_ERR("Already running; Exiting!");
-			close(pidfd);
-			exit(1);
+		    (void)close(fd);
 		}
-		if (ftruncate(pidfd, 0) < 0)
-			BL_LOG_WARNING("ftruncate on %s failed: m\n", PID_FILE);
-		sprintf(pidbuf, "%d\n", getpid());
-		if (write(pidfd, pidbuf, strlen(pidbuf)) != (ssize_t)strlen(pidbuf))
-			BL_LOG_WARNING("write on %s failed: m\n", PID_FILE);
+
+		openlog("blkmapd", LOG_PID, 0);
 	}
 
 	signal(SIGINT, sig_die);
diff --git a/utils/blkmapd/device-discovery.h b/utils/blkmapd/device-discovery.h
index a86eed99..462aa943 100644
--- a/utils/blkmapd/device-discovery.h
+++ b/utils/blkmapd/device-discovery.h
@@ -151,6 +151,8 @@ uint64_t process_deviceinfo(const char *dev_addr_buf,
 
 extern ssize_t atomicio(ssize_t(*f) (int, void *, size_t),
 			int fd, void *_s, size_t n);
+extern struct bl_serial *bl_create_scsi_string(int len, const char *bytes);
+extern void bl_free_scsi_string(struct bl_serial *str);
 extern struct bl_serial *bldev_read_serial(int fd, const char *filename);
 extern enum bl_path_state_e bldev_read_ap_state(int fd);
 extern int bl_discover_devices(void);
diff --git a/utils/blkmapd/device-inq.c b/utils/blkmapd/device-inq.c
index c7952c3e..9e5749ef 100644
--- a/utils/blkmapd/device-inq.c
+++ b/utils/blkmapd/device-inq.c
@@ -53,7 +53,7 @@
 #define DEF_ALLOC_LEN	255
 #define MX_ALLOC_LEN	(0xc000 + 0x80)
 
-static struct bl_serial *bl_create_scsi_string(int len, const char *bytes)
+struct bl_serial *bl_create_scsi_string(int len, const char *bytes)
 {
 	struct bl_serial *s;
 
@@ -66,7 +66,7 @@ static struct bl_serial *bl_create_scsi_string(int len, const char *bytes)
 	return s;
 }
 
-static void bl_free_scsi_string(struct bl_serial *str)
+void bl_free_scsi_string(struct bl_serial *str)
 {
 	if (str)
 		free(str);
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 6ba615d1..37b9e4b3 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -69,14 +69,14 @@ static int _lockfd = -1;
  * need these additional lockfile() routines.
  */
 static void
-grab_lockfile()
+grab_lockfile(void)
 {
 	_lockfd = open(lockfile, O_CREAT|O_RDWR, 0666);
 	if (_lockfd != -1)
 		lockf(_lockfd, F_LOCK, 0);
 }
 static void
-release_lockfile()
+release_lockfile(void)
 {
 	if (_lockfd != -1) {
 		lockf(_lockfd, F_ULOCK, 0);
@@ -513,7 +513,7 @@ validate_export(nfs_export *exp)
 	 */
 	struct stat stb;
 	char *path = exportent_realpath(&exp->m_export);
-	struct statfs64 stf;
+	struct statfs stf;
 	int fs_has_fsid = 0;
 
 	if (stat(path, &stb) < 0) {
@@ -528,7 +528,7 @@ validate_export(nfs_export *exp)
 	if (!can_test())
 		return;
 
-	if (!statfs64(path, &stf) &&
+	if (!statfs(path, &stf) &&
 	    (stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
 		fs_has_fsid = 1;
 
@@ -743,6 +743,7 @@ dump(int verbose, int export_format)
 #endif
 			}
 			secinfo_show(stdout, ep);
+			xprtsecinfo_show(stdout, ep);
 			printf("%c\n", (c != '(')? ')' : ' ');
 		}
 	}
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index 54b3f877..83dd6807 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -125,7 +125,55 @@ In that case you may include multiple sec= options, and following options
 will be enforced only for access using flavors listed in the immediately
 preceding sec= option.  The only options that are permitted to vary in
 this way are ro, rw, no_root_squash, root_squash, and all_squash.
+.SS Transport layer security
+The Linux NFS server allows the use of RPC-with-TLS (RFC 9289) to
+protect RPC traffic between itself and its clients.
+Alternately, administrators can secure NFS traffic using a VPN,
+or an ssh tunnel or similar mechanism, in a way that is transparent
+to the server.
 .PP
+To enable the use of RPC-with-TLS, the server's administrator must
+install and configure
+.BR tlshd
+to handle transport layer security handshake requests from the local
+kernel.
+Clients can then choose to use RPC-with-TLS or they may continue
+operating without it.
+.PP
+Administrators may require the use of RPC-with-TLS to protect access
+to individual exports.
+This is particularly useful when using non-cryptographic security
+flavors such as
+.IR sec=sys .
+The
+.I xprtsec=
+option, followed by an unordered colon-delimited list of security policies,
+can restrict access to the export to only clients that have negotiated
+transport-layer security.
+Currently supported transport layer security policies include:
+.TP
+.IR none
+The server permits clients to access the export
+without the use of transport layer security.
+.TP
+.IR tls
+The server permits clients that have negotiated an RPC-with-TLS session
+without peer authentication (confidentiality only) to access the export.
+Clients are not required to offer an x.509 certificate
+when establishing a transport layer security session.
+.TP
+.IR mtls
+The server permits clients that have negotiated an RPC-with-TLS session
+with peer authentication to access the export.
+The server requires clients to offer an x.509 certificate
+when establishing a transport layer security session.
+.PP
+If RPC-with-TLS is configured and enabled and the
+.I xprtsec=
+option is not specified, the default setting for an export is
+.IR xprtsec=none:tls:mtls .
+With this setting, the server permits clients to use any transport
+layer security mechanism or none at all to access the export.
 .SS General Options
 .BR exportfs
 understands the following export options:
@@ -581,7 +629,8 @@ a character class wildcard match.
 .BR netgroup (5),
 .BR mountd (8),
 .BR nfsd (8),
-.BR showmount (8).
+.BR showmount (8),
+.BR tlshd (8).
 .\".SH DIAGNOSTICS
 .\"An error parsing the file is reported using syslogd(8) as level NOTICE from
 .\"a DAEMON whenever
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
index e79c124d..cd9a965f 100644
--- a/utils/idmapd/idmapd.c
+++ b/utils/idmapd/idmapd.c
@@ -867,7 +867,7 @@ nfsdreopen_one(struct idmap_client *ic)
 }
 
 static void
-nfsdreopen()
+nfsdreopen(void)
 {
 	nfsdreopen_one(&nfsd_ic[IC_NAMEID]);
 	nfsdreopen_one(&nfsd_ic[IC_IDNAME]);
diff --git a/utils/mount/error.c b/utils/mount/error.c
index 73295bf0..9ddbcc09 100644
--- a/utils/mount/error.c
+++ b/utils/mount/error.c
@@ -207,16 +207,17 @@ void mount_error(const char *spec, const char *mount_point, int error)
 				progname, spec);
 		break;
 	case EINVAL:
-		nfs_error(_("%s: an incorrect mount option was specified"), progname);
+		nfs_error(_("%s: an incorrect mount option was specified for %s"),
+				progname, mount_point);
 		break;
 	case EOPNOTSUPP:
-		nfs_error(_("%s: requested NFS version or transport protocol is not supported"),
-				progname);
+		nfs_error(_("%s: requested NFS version or transport protocol is not supported for %s"),
+				progname, mount_point);
 		break;
 	case ENOTDIR:
 		if (spec)
-			nfs_error(_("%s: mount spec %s or point %s is not a "
-				  "directory"),	progname, spec, mount_point);
+			nfs_error(_("%s: mount spec %s or point %s is not a directory"),
+				  progname, spec, mount_point);
 		else
 			nfs_error(_("%s: mount point %s is not a directory"),
 				  progname, mount_point);
@@ -227,31 +228,31 @@ void mount_error(const char *spec, const char *mount_point, int error)
 		break;
 	case ENOENT:
 		if (spec)
-			nfs_error(_("%s: mounting %s failed, "
-				"reason given by server: %s"),
-				progname, spec, strerror(error));
+			nfs_error(_("%s: mounting %s failed, reason given by server: %s"),
+				  progname, spec, strerror(error));
 		else
 			nfs_error(_("%s: mount point %s does not exist"),
-				progname, mount_point);
+				  progname, mount_point);
 		break;
 	case ESPIPE:
 		rpc_mount_errors((char *)spec, 0, 0);
 		break;
 	case EIO:
-		nfs_error(_("%s: mount system call failed"), progname);
+		nfs_error(_("%s: mount system call failed for %s"),
+			  progname, mount_point);
 		break;
 	case EFAULT:
-		nfs_error(_("%s: encountered unexpected error condition."),
-				progname);
+		nfs_error(_("%s: encountered unexpected error condition for %s."),
+			  progname, mount_point);
 		nfs_error(_("%s: please report the error to" PACKAGE_BUGREPORT),
-				progname);
+			  progname);
 		break;
 	case EALREADY:
 		/* Error message has already been provided */
 		break;
 	default:
-		nfs_error(_("%s: %s"),
-			progname, strerror(error));
+		nfs_error(_("%s: %s for %s on %s"),
+			  progname, strerror(error), spec, mount_point);
 	}
 }
 
diff --git a/utils/mount/network.c b/utils/mount/network.c
index ed2f8253..01ead49f 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -179,7 +179,7 @@ static const unsigned long probe_mnt3_only[] = {
 
 static const unsigned int *nfs_default_proto(void);
 #ifdef MOUNT_CONFIG
-static const unsigned int *nfs_default_proto()
+static const unsigned int *nfs_default_proto(void)
 {
 	extern unsigned long config_default_proto;
 	/*
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index d9f34df3..7a410422 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -574,7 +574,39 @@ The
 .B sloppy
 option is an alternative to specifying
 .BR mount.nfs " -s " option.
-
+.TP 1.5i
+.BI xprtsec= policy
+Specifies the use of transport layer security to protect NFS network
+traffic on behalf of this mount point.
+.I policy
+can be one of
+.BR none ,
+.BR tls ,
+or
+.BR mtls .
+.IP
+If
+.B none
+is specified,
+transport layer security is forced off, even if the NFS server supports
+transport layer security.
+If
+.B tls
+is specified, the client uses RPC-with-TLS to provide in-transit
+confidentiality.
+If
+.B mtls
+is specified, the client uses RPC-with-TLS to authenticate itself and
+to provide in-transit confidentiality.
+If the server does not support RPC-with-TLS or peer authentication
+fails, the mount attempt fails.
+.IP
+If the
+.B xprtsec=
+option is not specified,
+the default behavior depends on the kernel,
+but is usually equivalent to
+.BR "xprtsec=none" .
 .SS "Options for NFS versions 2 and 3 only"
 Use these options, along with the options in the above subsection,
 for NFS versions 2 and 3 only.
diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf
index 342063f7..c498eb80 100644
--- a/utils/mount/nfsmount.conf
+++ b/utils/mount/nfsmount.conf
@@ -59,13 +59,13 @@
 # acregmin=30
 #
 # The Maximum time (in seconds) file attributes are cached
-# acregmin=60
+# acregmax=60
 #
 # The minimum time (in seconds) directory attributes are cached
-# acregmin=30
+# acdirmin=30
 #
 # The Maximum time (in seconds) directory attributes are cached
-# acregmin=60
+# acdirmax=60
 #
 # Enable Access  Control  Lists
 # Acl=False
diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c
index 0d3bcb95..2ade5d5d 100644
--- a/utils/mount/parse_dev.c
+++ b/utils/mount/parse_dev.c
@@ -170,7 +170,8 @@ static int nfs_parse_square_bracket(const char *dev,
 	if (pathname) {
 		*pathname = strndup(cbrace, path_len);
 		if (*pathname == NULL) {
-			free(*hostname);
+			if (hostname)
+				free(*hostname);
 			return nfs_pdn_nomem_err();
 		}
 	}
diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
index 4016a761..249df00b 100644
--- a/utils/nfsd/nfsd.c
+++ b/utils/nfsd/nfsd.c
@@ -23,6 +23,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <sched.h>
 
 #include "conffile.h"
 #include "nfslib.h"
@@ -39,6 +40,7 @@ static void	usage(const char *);
 static struct option longopts[] =
 {
 	{ "host", 1, 0, 'H' },
+	{ "scope", 1, 0, 'S'},
 	{ "help", 0, 0, 'h' },
 	{ "no-nfs-version", 1, 0, 'N' },
 	{ "nfs-version", 1, 0, 'V' },
@@ -69,6 +71,7 @@ main(int argc, char **argv)
 	int	count = NFSD_NPROC, c, i, error = 0, portnum, fd, found_one;
 	char *p, *progname, *port, *rdma_port = NULL;
 	char **haddr = NULL;
+	char *scope = NULL;
 	int hcounter = 0;
 	struct conf_list *hosts;
 	int	socket_up = 0;
@@ -168,8 +171,9 @@ main(int argc, char **argv)
 			hcounter++;
 		}
 	}
+	scope = conf_get_str("nfsd", "scope");
 
-	while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) {
+	while ((c = getopt_long(argc, argv, "dH:S:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) {
 		switch(c) {
 		case 'd':
 			xlog_config(D_ALL, 1);
@@ -190,6 +194,9 @@ main(int argc, char **argv)
 			haddr[hcounter] = optarg;
 			hcounter++;
 			break;
+		case 'S':
+			scope = optarg;
+			break;
 		case 'P':	/* XXX for nfs-server compatibility */
 		case 'p':
 			/* only the last -p option has any effect */
@@ -367,6 +374,14 @@ main(int argc, char **argv)
 	if (lease  > 0)
 		nfssvc_set_time("lease", lease);
 
+	if (scope) {
+		if (unshare(CLONE_NEWUTS) < 0 ||
+		    sethostname(scope, strlen(scope)) < 0) {
+			xlog(L_ERROR, "Unable to set server scope: %m");
+			error = -1;
+			goto out;
+		}
+	}
 	i = 0;
 	do {
 		error = nfssvc_set_sockets(protobits, haddr[i], port);
diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man
index 634b8a63..6f4fc1df 100644
--- a/utils/nfsd/nfsd.man
+++ b/utils/nfsd/nfsd.man
@@ -35,9 +35,17 @@ Note that
 .B lockd
 (which performs file locking services for NFS) may still accept
 request on all known network addresses.  This may change in future
-releases of the Linux Kernel. This option can be used multiple time 
+releases of the Linux Kernel. This option can be used multiple times
 to listen to more than one interface.
 .TP
+.B \-S " or " \-\-scope scope
+NFSv4.1 and later require the server to report a "scope" which is used
+by the clients to detect if two connections are to the same server.
+By default Linux NFSD uses the host name as the scope.
+.sp
+It is particularly important for high-availablity configurations to ensure
+that all potential server nodes report the same server scope.
+.TP
 .B \-p " or " \-\-port  port
 specify a different port to listen on for NFS requests. By default,
 .B rpc.nfsd
@@ -134,6 +142,9 @@ will listen on.  Use of the
 .B --host
 option replaces all host names listed here.
 .TP
+.B scope
+Set the server scope.
+.TP
 .B grace-time
 The grace time, for both NFSv4 and NLM, in seconds.
 .TP
@@ -159,7 +170,9 @@ Enable or disable TCP support.
 .B vers3
 .TP
 .B vers4
-Enable or disable a major NFS version.  3 and 4 are normally enabled
+Enable or disable 
+.B all 
+NFSv4 versions.  All versions are normally enabled
 by default.
 .TP
 .B vers4.1