diff --git a/SOURCES/nfs-utils-2.3.1-systemd-gssproxy-restart.patch b/SOURCES/nfs-utils-2.3.1-systemd-gssproxy-restart.patch
index 60762e8..f298b89 100644
--- a/SOURCES/nfs-utils-2.3.1-systemd-gssproxy-restart.patch
+++ b/SOURCES/nfs-utils-2.3.1-systemd-gssproxy-restart.patch
@@ -1,9 +1,9 @@
-diff -up nfs-utils-2.3.1/systemd/nfs-server.service.orig nfs-utils-2.3.1/systemd/nfs-server.service
---- nfs-utils-2.3.1/systemd/nfs-server.service.orig	2018-01-19 10:25:38.153513857 -0500
-+++ nfs-utils-2.3.1/systemd/nfs-server.service	2018-01-19 10:30:52.977245126 -0500
-@@ -26,6 +26,7 @@ Type=oneshot
+diff -up nfs-utils-2.3.3/systemd/nfs-server.service.orig nfs-utils-2.3.3/systemd/nfs-server.service
+--- nfs-utils-2.3.3/systemd/nfs-server.service.orig	2020-12-11 09:05:23.499222371 -0500
++++ nfs-utils-2.3.3/systemd/nfs-server.service	2020-12-11 09:06:38.970186395 -0500
+@@ -23,6 +23,7 @@ Type=oneshot
  RemainAfterExit=yes
- ExecStartPre=/usr/sbin/exportfs -r
+ ExecStartPre=-/usr/sbin/exportfs -r
  ExecStart=/usr/sbin/rpc.nfsd
 +ExecStart=-/bin/sh -c 'if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi'
  ExecStop=/usr/sbin/rpc.nfsd 0
diff --git a/SOURCES/nfs-utils-2.3.3-exportfs-man-labels.patch b/SOURCES/nfs-utils-2.3.3-exportfs-man-labels.patch
new file mode 100644
index 0000000..738fb85
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-exportfs-man-labels.patch
@@ -0,0 +1,50 @@
+commit 7d5dcd2358df55353eed94a0e84b77bb3597634e
+Author: J. Bruce Fields <bfields@redhat.com>
+Date:   Fri Mar 27 13:11:28 2020 -0400
+
+    exports man page: warn about subdirectory exports
+    
+    Subdirectory exports have a number of problems which have been poorly
+    documented.
+    
+    Signed-off-by: J. Bruce Fields <bfields@redhat.com>
+    Signed-off-by: Steve Dickson <steved@redhat.com>
+
+diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
+index e3a16f6b..1d171849 100644
+--- a/utils/exportfs/exports.man
++++ b/utils/exportfs/exports.man
+@@ -494,6 +494,33 @@ export entry for
+ .B /home/joe
+ in the example section below, which maps all requests to uid 150 (which
+ is supposedly that of user joe).
++
++.SS Subdirectory Exports
++
++Normally you should only export only the root of a filesystem.  The NFS
++server will also allow you to export a subdirectory of a filesystem,
++however, this has drawbacks:
++
++First, it may be possible for a malicious user to access files on the
++filesystem outside of the exported subdirectory, by guessing filehandles
++for those other files.  The only way to prevent this is by using the
++.IR no_subtree_check
++option, which can cause other problems.
++
++Second, export options may not be enforced in the way that you would
++expect.  For example, the
++.IR security_label
++option will not work on subdirectory exports, and if nested subdirectory
++exports change the
++.IR security_label
++or
++.IR sec=
++options, NFSv4 clients will normally see only the options on the parent
++export.  Also, where security options differ, a malicious client may use
++filehandle-guessing attacks to access the files from one subdirectory
++using the options from another.
++
++
+ .SS Extra Export Tables
+ After reading 
+ .I /etc/exports 
diff --git a/SOURCES/nfs-utils-2.3.3-exports-manpage-outdated.patch b/SOURCES/nfs-utils-2.3.3-exports-manpage-outdated.patch
new file mode 100644
index 0000000..1660781
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-exports-manpage-outdated.patch
@@ -0,0 +1,30 @@
+commit ba90d61be3abca5a699765ce08759ca6b986781d
+Author: Steve Dickson <steved@redhat.com>
+Date:   Thu Dec 10 14:05:23 2020 -0500
+
+    exports.man: Remove some outdated verbiage
+    
+    Years ago, commit 6a7d90cea765 removed the warning
+    this verbiage was talking about, but was never
+    removed from the man page.
+    
+    Signed-off-by: Steve Dickson <steved@redhat.com>
+
+diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
+index 1d171849..54b3f877 100644
+--- a/utils/exportfs/exports.man
++++ b/utils/exportfs/exports.man
+@@ -169,13 +169,6 @@ default.  In all releases after 1.0.0,
+ is the default, and
+ .I async
+ must be explicitly requested if needed.
+-To help make system administrators aware of this change,
+-.B exportfs
+-will issue a warning if neither
+-.I sync
+-nor
+-.I async
+-is specified.
+ .TP
+ .IR no_wdelay
+ This option has no effect if
diff --git a/SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch b/SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch
new file mode 100644
index 0000000..2813d0c
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-gssd-multithread-updates.patch
@@ -0,0 +1,1009 @@
+diff --git a/aclocal/libevent.m4 b/aclocal/libevent.m4
+index b5ac00ff..0ebcb524 100644
+--- a/aclocal/libevent.m4
++++ b/aclocal/libevent.m4
+@@ -4,9 +4,13 @@ AC_DEFUN([AC_LIBEVENT], [
+   dnl Check for libevent, but do not add -levent to LIBS
+   AC_CHECK_LIB([event], [event_dispatch], [LIBEVENT=-levent],
+                [AC_MSG_ERROR([libevent not found.])])
++  AC_CHECK_LIB([event_core], [event_base_dispatch], [LIBEVENT=-levent_core],
++  	[AC_MSG_ERROR([libevent2 not found.])])
+   AC_SUBST(LIBEVENT)
+ 
+   AC_CHECK_HEADERS([event.h], ,
+                    [AC_MSG_ERROR([libevent headers not found.])])
++  AC_CHECK_HEADERS([event2/event.h], ,
++                   [AC_MSG_ERROR([libevent headers not found.])])
+ 
+ ])dnl
+diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c
+index 2a7f3a13..982b96f4 100644
+--- a/utils/gssd/gss_names.c
++++ b/utils/gssd/gss_names.c
+@@ -110,10 +110,12 @@ get_hostbased_client_name(gss_name_t client_name, gss_OID mech,
+ 	/* For Kerberos, transform the NT_KRB5_PRINCIPAL name to
+ 	 * an NT_HOSTBASED_SERVICE name */
+ 	if (g_OID_equal(&krb5oid, mech)) {
+-		if (get_krb5_hostbased_name(&name, &cname) == 0)
+-			*hostbased_name = cname;
++		if (get_krb5_hostbased_name(&name, &cname) != 0)
++			goto out_rel_buf;
++		*hostbased_name = cname;
+ 	} else {
+ 		printerr(1, "WARNING: unknown/unsupport mech OID\n");
++		goto out_rel_buf;
+ 	}
+ 
+ 	res = 0;
+diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
+index af66ed62..6f461fdf 100644
+--- a/utils/gssd/gssd.c
++++ b/utils/gssd/gssd.c
+@@ -64,7 +64,7 @@
+ #include <fcntl.h>
+ #include <dirent.h>
+ #include <netdb.h>
+-#include <event.h>
++#include <event2/event.h>
+ 
+ #include "gssd.h"
+ #include "err_util.h"
+@@ -77,7 +77,7 @@ static char *pipefs_path = GSSD_PIPEFS_DIR;
+ static DIR *pipefs_dir;
+ static int pipefs_fd;
+ static int inotify_fd;
+-struct event inotify_ev;
++struct event *inotify_ev;
+ 
+ char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE;
+ char **ccachesearch;
+@@ -90,9 +90,9 @@ char *ccachedir = NULL;
+ /* Avoid DNS reverse lookups on server names */
+ static bool avoid_dns = true;
+ static bool use_gssproxy = false;
+-int thread_started = false;
+-pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER;
+-pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;
++pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER;
++static bool signal_received = false;
++static struct event_base *evbase = NULL;
+ 
+ TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
+ 
+@@ -359,20 +359,28 @@ out:
+ 	free(port);
+ }
+ 
++/* Actually frees clp and fields that might be used from other
++ * threads if was last reference.
++ */
+ static void
+-gssd_destroy_client(struct clnt_info *clp)
++gssd_free_client(struct clnt_info *clp)
+ {
+-	if (clp->krb5_fd >= 0) {
++	int refcnt;
++
++	pthread_mutex_lock(&clp_lock);
++	refcnt = --clp->refcount;
++	pthread_mutex_unlock(&clp_lock);
++	if (refcnt > 0)
++		return;
++
++	printerr(3, "freeing client %s\n", clp->relpath);
++
++	if (clp->krb5_fd >= 0)
+ 		close(clp->krb5_fd);
+-		event_del(&clp->krb5_ev);
+-	}
+ 
+-	if (clp->gssd_fd >= 0) {
++	if (clp->gssd_fd >= 0)
+ 		close(clp->gssd_fd);
+-		event_del(&clp->gssd_ev);
+-	}
+ 
+-	inotify_rm_watch(inotify_fd, clp->wd);
+ 	free(clp->relpath);
+ 	free(clp->servicename);
+ 	free(clp->servername);
+@@ -380,6 +388,30 @@ gssd_destroy_client(struct clnt_info *clp)
+ 	free(clp);
+ }
+ 
++/* Called when removing from clnt_list to tear down event handling.
++ * Will then free clp if was last reference.
++ */
++static void
++gssd_destroy_client(struct clnt_info *clp)
++{
++	printerr(3, "destroying client %s\n", clp->relpath);
++
++	if (clp->krb5_ev) {
++		event_del(clp->krb5_ev);
++		event_free(clp->krb5_ev);
++		clp->krb5_ev = NULL;
++	}
++
++	if (clp->gssd_ev) {
++		event_del(clp->gssd_ev);
++		event_free(clp->gssd_ev);
++		clp->gssd_ev = NULL;
++	}
++
++	inotify_rm_watch(inotify_fd, clp->wd);
++	gssd_free_client(clp);
++}
++
+ static void gssd_scan(void);
+ 
+ static int
+@@ -416,11 +448,21 @@ static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp)
+ 	info = malloc(sizeof(struct clnt_upcall_info));
+ 	if (info == NULL)
+ 		return NULL;
++
++	pthread_mutex_lock(&clp_lock);
++	clp->refcount++;
++	pthread_mutex_unlock(&clp_lock);
+ 	info->clp = clp;
+ 
+ 	return info;
+ }
+ 
++void free_upcall_info(struct clnt_upcall_info *info)
++{
++	gssd_free_client(info->clp);
++	free(info);
++}
++
+ /* For each upcall read the upcall info into the buffer, then create a
+  * thread in a detached state so that resources are released back into
+  * the system without the need for a join.
+@@ -438,13 +480,13 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data)
+ 	info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf));
+ 	if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') {
+ 		printerr(0, "WARNING: %s: failed reading request\n", __func__);
+-		free(info);
++		free_upcall_info(info);
+ 		return;
+ 	}
+ 	info->lbuf[info->lbuflen-1] = 0;
+ 
+ 	if (start_upcall_thread(handle_gssd_upcall, info))
+-		free(info);
++		free_upcall_info(info);
+ }
+ 
+ static void
+@@ -461,12 +503,12 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
+ 			sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) {
+ 		printerr(0, "WARNING: %s: failed reading uid from krb5 "
+ 			 "upcall pipe: %s\n", __func__, strerror(errno));
+-		free(info);
++		free_upcall_info(info);
+ 		return;
+ 	}
+ 
+ 	if (start_upcall_thread(handle_krb5_upcall, info))
+-		free(info);
++		free_upcall_info(info);
+ }
+ 
+ static struct clnt_info *
+@@ -501,6 +543,7 @@ gssd_get_clnt(struct topdir *tdi, const char *name)
+ 	clp->name = clp->relpath + strlen(tdi->name) + 1;
+ 	clp->krb5_fd = -1;
+ 	clp->gssd_fd = -1;
++	clp->refcount = 1;
+ 
+ 	TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list);
+ 	return clp;
+@@ -535,15 +578,15 @@ gssd_scan_clnt(struct clnt_info *clp)
+ 		clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK);
+ 
+ 	if (gssd_was_closed && clp->gssd_fd >= 0) {
+-		event_set(&clp->gssd_ev, clp->gssd_fd, EV_READ | EV_PERSIST,
+-			  gssd_clnt_gssd_cb, clp);
+-		event_add(&clp->gssd_ev, NULL);
++		clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST,
++					 gssd_clnt_gssd_cb, clp);
++		event_add(clp->gssd_ev, NULL);
+ 	}
+ 
+ 	if (krb5_was_closed && clp->krb5_fd >= 0) {
+-		event_set(&clp->krb5_ev, clp->krb5_fd, EV_READ | EV_PERSIST,
+-			  gssd_clnt_krb5_cb, clp);
+-		event_add(&clp->krb5_ev, NULL);
++		clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST,
++					 gssd_clnt_krb5_cb, clp);
++		event_add(clp->krb5_ev, NULL);
+ 	}
+ 
+ 	if (clp->krb5_fd == -1 && clp->gssd_fd == -1)
+@@ -649,7 +692,7 @@ gssd_scan_topdir(const char *name)
+ 		if (clp->scanned)
+ 			continue;
+ 
+-		printerr(3, "destroying client %s\n", clp->relpath);
++		printerr(3, "orphaned client %s\n", clp->relpath);
+ 		saveprev = clp->list.tqe_prev;
+ 		TAILQ_REMOVE(&tdi->clnt_list, clp, list);
+ 		gssd_destroy_client(clp);
+@@ -746,12 +789,16 @@ gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotif
+ 	} else if (ev->mask & IN_DELETE) {
+ 		if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) {
+ 			close(clp->gssd_fd);
+-			event_del(&clp->gssd_ev);
++			event_del(clp->gssd_ev);
++			event_free(clp->gssd_ev);
++			clp->gssd_ev = NULL;
+ 			clp->gssd_fd = -1;
+ 
+ 		} else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) {
+ 			close(clp->krb5_fd);
+-			event_del(&clp->krb5_ev);
++			event_del(clp->krb5_ev);
++			event_free(clp->krb5_ev);
++			clp->krb5_ev = NULL;
+ 			clp->krb5_fd = -1;
+ 		}
+ 
+@@ -824,10 +871,15 @@ found:
+ static void
+ sig_die(int signal)
+ {
+-	if (root_uses_machine_creds)
+-		gssd_destroy_krb5_machine_creds();
++	if (signal_received) {
++		gssd_destroy_krb5_principals(root_uses_machine_creds);
++		printerr(1, "forced exiting on signal %d\n", signal);
++		exit(0);
++	}
++
++	signal_received = true;
+ 	printerr(1, "exiting on signal %d\n", signal);
+-	exit(0);
++	event_base_loopexit(evbase, NULL);
+ }
+ 
+ static void
+@@ -884,9 +936,10 @@ main(int argc, char *argv[])
+ 	int rpc_verbosity = 0;
+ 	int opt;
+ 	int i;
++	int rc;
+ 	extern char *optarg;
+ 	char *progname;
+-	struct event sighup_ev;
++	struct event *sighup_ev;
+ 
+ 	read_gss_conf();
+ 
+@@ -1025,7 +1078,11 @@ main(int argc, char *argv[])
+ 	if (gssd_check_mechs() != 0)
+ 		errx(1, "Problem with gssapi library");
+ 
+-	event_init();
++	evbase = event_base_new();
++	if (!evbase) {
++		printerr(0, "ERROR: failed to create event base\n");
++		exit(EXIT_FAILURE);
++	}
+ 
+ 	pipefs_dir = opendir(pipefs_path);
+ 	if (!pipefs_dir) {
+@@ -1047,18 +1104,43 @@ main(int argc, char *argv[])
+ 
+ 	signal(SIGINT, sig_die);
+ 	signal(SIGTERM, sig_die);
+-	signal_set(&sighup_ev, SIGHUP, gssd_scan_cb, NULL);
+-	signal_add(&sighup_ev, NULL);
+-	event_set(&inotify_ev, inotify_fd, EV_READ | EV_PERSIST, gssd_inotify_cb, NULL);
+-	event_add(&inotify_ev, NULL);
++	sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL);
++	evsignal_add(sighup_ev, NULL);
++	inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST,
++			       gssd_inotify_cb, NULL);
++	event_add(inotify_ev, NULL);
+ 
+ 	TAILQ_INIT(&topdir_list);
+ 	gssd_scan();
+ 	daemon_ready();
+ 
+-	event_dispatch();
++	rc = event_base_dispatch(evbase);
+ 
+-	printerr(0, "ERROR: event_dispatch() returned!\n");
+-	return EXIT_FAILURE;
+-}
++	printerr(0, "event_dispatch() returned %i!\n", rc);
++
++	gssd_destroy_krb5_principals(root_uses_machine_creds);
++
++	while (!TAILQ_EMPTY(&topdir_list)) {
++		struct topdir *tdi = TAILQ_FIRST(&topdir_list);
++		TAILQ_REMOVE(&topdir_list, tdi, list);
++		while (!TAILQ_EMPTY(&tdi->clnt_list)) {
++			struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list);
++			TAILQ_REMOVE(&tdi->clnt_list, clp, list);
++			gssd_destroy_client(clp);
++		}
++		free(tdi);
++	}
++
++	event_free(inotify_ev);
++	event_free(sighup_ev);
++	event_base_free(evbase);
++
++	close(inotify_fd);
++	close(pipefs_fd);
++	closedir(pipefs_dir);
+ 
++	free(preferred_realm);
++	free(ccachesearch);
++
++	return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
++}
+diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h
+index f4f59754..1e8c58d4 100644
+--- a/utils/gssd/gssd.h
++++ b/utils/gssd/gssd.h
+@@ -62,13 +62,10 @@ extern int			root_uses_machine_creds;
+ extern unsigned int 		context_timeout;
+ extern unsigned int rpc_timeout;
+ extern char			*preferred_realm;
+-extern pthread_mutex_t ple_lock;
+-extern pthread_cond_t pcond;
+-extern pthread_mutex_t pmutex;
+-extern int thread_started;
+ 
+ struct clnt_info {
+ 	TAILQ_ENTRY(clnt_info)	list;
++	int			refcount;
+ 	int			wd;
+ 	bool			scanned;
+ 	char			*name;
+@@ -79,9 +76,9 @@ struct clnt_info {
+ 	int			vers;
+ 	char			*protocol;
+ 	int			krb5_fd;
+-	struct event		krb5_ev;
++	struct event		*krb5_ev;
+ 	int			gssd_fd;
+-	struct event		gssd_ev;
++	struct event		*gssd_ev;
+ 	struct			sockaddr_storage addr;
+ };
+ 
+@@ -94,6 +91,7 @@ struct clnt_upcall_info {
+ 
+ void handle_krb5_upcall(struct clnt_upcall_info *clp);
+ void handle_gssd_upcall(struct clnt_upcall_info *clp);
++void free_upcall_info(struct clnt_upcall_info *info);
+ 
+ 
+ #endif /* _RPC_GSSD_H_ */
+diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
+index bfcf3f09..ae3ebe81 100644
+--- a/utils/gssd/gssd_proc.c
++++ b/utils/gssd/gssd_proc.c
+@@ -149,9 +149,10 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+ 	char    *buf = NULL, *p = NULL, *end = NULL;
+ 	unsigned int timeout = context_timeout;
+ 	unsigned int buf_size = 0;
++	pthread_t tid = pthread_self();
+ 
+-	printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n",
+-		lifetime_rec, acceptor->length, acceptor->value);
++	printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n",
++		tid, lifetime_rec, acceptor->length, acceptor->value);
+ 	buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) +
+ 		sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length +
+ 		sizeof(context_token->length) + context_token->length +
+@@ -177,7 +178,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd,
+ 	return;
+ out_err:
+ 	free(buf);
+-	printerr(1, "Failed to write downcall!\n");
++	printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid);
+ 	return;
+ }
+ 
+@@ -231,7 +232,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen,
+ 	switch (sa->sa_family) {
+ 	case AF_INET:
+ 		if (s4->sin_port != 0) {
+-			printerr(2, "DEBUG: port already set to %d\n",
++			printerr(4, "DEBUG: port already set to %d\n",
+ 				 ntohs(s4->sin_port));
+ 			return 1;
+ 		}
+@@ -239,7 +240,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen,
+ #ifdef IPV6_SUPPORTED
+ 	case AF_INET6:
+ 		if (s6->sin6_port != 0) {
+-			printerr(2, "DEBUG: port already set to %d\n",
++			printerr(4, "DEBUG: port already set to %d\n",
+ 				 ntohs(s6->sin6_port));
+ 			return 1;
+ 		}
+@@ -544,7 +545,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid,
+ 		uid, tgtname);
+ 
+ 	do {
+-		gssd_refresh_krb5_machine_credential(clp->servername, NULL,
++		gssd_refresh_krb5_machine_credential(clp->servername,
+ 						     service, srchost);
+ 	/*
+ 	 * Get a list of credential cache names and try each
+@@ -726,7 +727,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info)
+ 	printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath);
+ 
+ 	process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL);
+-	free(info);
++	free_upcall_info(info);
+ }
+ 
+ void
+@@ -743,8 +744,10 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
+ 	char			*enctypes = NULL;
+ 	char			*upcall_str;
+ 	char			*pbuf = info->lbuf;
++	pthread_t tid = pthread_self();
+ 
+-	printerr(2, "%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath);
++	printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, 
++		info->lbuf, clp->relpath);
+ 
+ 	upcall_str = strdup(info->lbuf);
+ 	if (upcall_str == NULL) {
+@@ -826,6 +829,6 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
+ out:
+ 	free(upcall_str);
+ out_nomem:
+-	free(info);
++	free_upcall_info(info);
+ 	return;
+ }
+diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
+index 26e51edf..d675c3a4 100644
+--- a/utils/gssd/krb5_util.c
++++ b/utils/gssd/krb5_util.c
+@@ -126,9 +126,28 @@
+ #include "gss_util.h"
+ #include "krb5_util.h"
+ 
++/*
++ * List of principals from our keytab that we
++ * will try to use to obtain credentials
++ * (known as a principal list entry (ple))
++ */
++struct gssd_k5_kt_princ {
++	struct gssd_k5_kt_princ *next;
++	// Only protect against deletion, not modification
++	int refcount;
++	// Only set during creation in new_ple()
++	krb5_principal princ;
++	char *realm;
++	// Modified during usage by gssd_get_single_krb5_cred()
++	char *ccname;
++	krb5_timestamp endtime;
++};
++
++
+ /* Global list of principals/cache file names for machine credentials */
+-struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
+-pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
++static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL;
++/* This mutex protects list modification & ple->ccname */
++static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER;
+ 
+ #ifdef HAVE_SET_ALLOWABLE_ENCTYPES
+ int limit_to_legacy_enctypes = 0;
+@@ -146,6 +165,18 @@ static int gssd_get_single_krb5_cred(krb5_context context,
+ static int query_krb5_ccache(const char* cred_cache, char **ret_princname,
+ 		char **ret_realm);
+ 
++static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple)
++{
++	if (--ple->refcount)
++		return;
++
++	printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm);
++	krb5_free_principal(context, ple->princ);
++	free(ple->ccname);
++	free(ple->realm);
++	free(ple);
++}
++
+ /*
+  * Called from the scandir function to weed out potential krb5
+  * credentials cache files
+@@ -352,12 +383,15 @@ gssd_get_single_krb5_cred(krb5_context context,
+ 	 * 300 because clock skew must be within 300sec for kerberos
+ 	 */
+ 	now += 300;
++	pthread_mutex_lock(&ple_lock);
+ 	if (ple->ccname && ple->endtime > now && !nocache) {
+ 		printerr(3, "INFO: Credentials in CC '%s' are good until %d\n",
+ 			 ple->ccname, ple->endtime);
+ 		code = 0;
++		pthread_mutex_unlock(&ple_lock);
+ 		goto out;
+ 	}
++	pthread_mutex_unlock(&ple_lock);
+ 
+ 	if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) {
+ 		printerr(0, "ERROR: Unable to get keytab name in "
+@@ -410,6 +444,7 @@ gssd_get_single_krb5_cred(krb5_context context,
+ 	 * Initialize cache file which we're going to be using
+ 	 */
+ 
++	pthread_mutex_lock(&ple_lock);
+ 	if (use_memcache)
+ 	    cache_type = "MEMORY";
+ 	else
+@@ -419,15 +454,18 @@ gssd_get_single_krb5_cred(krb5_context context,
+ 		ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX,
+ 		GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm);
+ 	ple->endtime = my_creds.times.endtime;
+-	if (ple->ccname != NULL)
++	if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) {
+ 		free(ple->ccname);
+-	ple->ccname = strdup(cc_name);
+-	if (ple->ccname == NULL) {
+-		printerr(0, "ERROR: no storage to duplicate credentials "
+-			    "cache name '%s'\n", cc_name);
+-		code = ENOMEM;
+-		goto out;
++		ple->ccname = strdup(cc_name);
++		if (ple->ccname == NULL) {
++			printerr(0, "ERROR: no storage to duplicate credentials "
++				    "cache name '%s'\n", cc_name);
++			code = ENOMEM;
++			pthread_mutex_unlock(&ple_lock);
++			goto out;
++		}
+ 	}
++	pthread_mutex_unlock(&ple_lock);
+ 	if ((code = krb5_cc_resolve(context, cc_name, &ccache))) {
+ 		k5err = gssd_k5_err_msg(context, code);
+ 		printerr(0, "ERROR: %s while opening credential cache '%s'\n",
+@@ -465,6 +503,7 @@ gssd_get_single_krb5_cred(krb5_context context,
+ 
+ /*
+  * Given a principal, find a matching ple structure
++ * Called with mutex held
+  */
+ static struct gssd_k5_kt_princ *
+ find_ple_by_princ(krb5_context context, krb5_principal princ)
+@@ -481,6 +520,7 @@ find_ple_by_princ(krb5_context context, krb5_principal princ)
+ 
+ /*
+  * Create, initialize, and add a new ple structure to the global list
++ * Called with mutex held
+  */
+ static struct gssd_k5_kt_princ *
+ new_ple(krb5_context context, krb5_principal princ)
+@@ -532,6 +572,7 @@ new_ple(krb5_context context, krb5_principal princ)
+ 			p->next = ple;
+ 	}
+ 
++	ple->refcount = 1;
+ 	return ple;
+ outerr:
+ 	if (ple) {
+@@ -550,13 +591,14 @@ get_ple_by_princ(krb5_context context, krb5_principal princ)
+ {
+ 	struct gssd_k5_kt_princ *ple;
+ 
+-	/* Need to serialize list if we ever become multi-threaded! */
+-
+ 	pthread_mutex_lock(&ple_lock);
+ 	ple = find_ple_by_princ(context, princ);
+ 	if (ple == NULL) {
+ 		ple = new_ple(context, princ);
+ 	}
++	if (ple != NULL) {
++		ple->refcount++;
++	}
+ 	pthread_mutex_unlock(&ple_lock);
+ 
+ 	return ple;
+@@ -721,6 +763,8 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
+ 				retval = ENOMEM;
+ 				k5_free_kt_entry(context, kte);
+ 			} else {
++				release_ple(context, ple);
++				ple = NULL;
+ 				retval = 0;
+ 				*found = 1;
+ 			}
+@@ -796,12 +840,12 @@ find_keytab_entry(krb5_context context, krb5_keytab kt,
+ 	/* Compute the active directory machine name HOST$ */
+ 	krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name", 
+ 		notsetstr, &adhostoverride);
+-	if (strcmp(adhostoverride, notsetstr) != 0) {
+-	        printerr (1, 
+-				"AD host string overridden with \"%s\" from appdefaults\n", 
+-				adhostoverride);
+-	        /* No overflow: Windows cannot handle strings longer than 19 chars */
+-	        strcpy(myhostad, adhostoverride);
++	if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) {
++		printerr(1,
++			 "AD host string overridden with \"%s\" from appdefaults\n",
++			 adhostoverride);
++		/* No overflow: Windows cannot handle strings longer than 19 chars */
++		strcpy(myhostad, adhostoverride);
+ 	} else {
+ 	        strcpy(myhostad, myhostname);
+ 	        for (i = 0; myhostad[i] != 0; ++i) {
+@@ -928,7 +972,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt,
+ 					tried_upper = 1;
+ 				}
+ 			} else {
+-				printerr(3, "Success getting keytab entry for '%s'\n",spn);
++				printerr(2, "Success getting keytab entry for '%s'\n",spn);
+ 				retval = 0;
+ 				goto out;
+ 			}
+@@ -1053,6 +1097,93 @@ err_cache:
+ 	return (*ret_princname && *ret_realm);
+ }
+ 
++/*
++ * Obtain (or refresh if necessary) Kerberos machine credentials
++ * If a ple is passed in, it's reference will be released
++ */
++static int
++gssd_refresh_krb5_machine_credential_internal(char *hostname,
++				     struct gssd_k5_kt_princ *ple,
++				     char *service, char *srchost)
++{
++	krb5_error_code code = 0;
++	krb5_context context;
++	krb5_keytab kt = NULL;;
++	int retval = 0;
++	char *k5err = NULL;
++	const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
++
++	printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n",
++		__func__, hostname, ple, service, srchost);
++
++	/*
++	 * If a specific service name was specified, use it.
++	 * Otherwise, use the default list.
++	 */
++	if (service != NULL && strcmp(service, "*") != 0) {
++		svcnames[0] = service;
++		svcnames[1] = NULL;
++	}
++	if (hostname == NULL && ple == NULL)
++		return EINVAL;
++
++	code = krb5_init_context(&context);
++	if (code) {
++		k5err = gssd_k5_err_msg(NULL, code);
++		printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
++			 __func__, k5err);
++		retval = code;
++		goto out;
++	}
++
++	if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
++		k5err = gssd_k5_err_msg(context, code);
++		printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
++			 __func__, k5err, keytabfile);
++		goto out_free_context;
++	}
++
++	if (ple == NULL) {
++		krb5_keytab_entry kte;
++
++		code = find_keytab_entry(context, kt, srchost, hostname,
++					 &kte, svcnames);
++		if (code) {
++			printerr(0, "ERROR: %s: no usable keytab entry found "
++				 "in keytab %s for connection with host %s\n",
++				 __FUNCTION__, keytabfile, hostname);
++			retval = code;
++			goto out_free_kt;
++		}
++
++		ple = get_ple_by_princ(context, kte.principal);
++		k5_free_kt_entry(context, &kte);
++		if (ple == NULL) {
++			char *pname;
++			if ((krb5_unparse_name(context, kte.principal, &pname))) {
++				pname = NULL;
++			}
++			printerr(0, "ERROR: %s: Could not locate or create "
++				 "ple struct for principal %s for connection "
++				 "with host %s\n",
++				 __FUNCTION__, pname ? pname : "<unparsable>",
++				 hostname);
++			if (pname) k5_free_unparsed_name(context, pname);
++			goto out_free_kt;
++		}
++	}
++	retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
++out_free_kt:
++	krb5_kt_close(context, kt);
++out_free_context:
++	if (ple)
++		release_ple(context, ple);
++	krb5_free_context(context);
++out:
++	free(k5err);
++	return retval;
++}
++
+ /*==========================*/
+ /*===  External routines ===*/
+ /*==========================*/
+@@ -1146,37 +1277,56 @@ gssd_get_krb5_machine_cred_list(char ***list)
+ 		goto out;
+ 	}
+ 
+-	/* Need to serialize list if we ever become multi-threaded! */
+-
++	pthread_mutex_lock(&ple_lock);
+ 	for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+-		if (ple->ccname) {
+-			/* Make sure cred is up-to-date before returning it */
+-			retval = gssd_refresh_krb5_machine_credential(NULL, ple,
+-								      NULL, NULL);
+-			if (retval)
+-				continue;
+-			if (i + 1 > listsize) {
+-				listsize += listinc;
+-				l = (char **)
+-					realloc(l, listsize * sizeof(char *));
+-				if (l == NULL) {
+-					retval = ENOMEM;
+-					goto out;
+-				}
+-			}
+-			if ((l[i++] = strdup(ple->ccname)) == NULL) {
++		if (!ple->ccname)
++			continue;
++
++		/* Take advantage of the fact we only remove the ple
++		 * from the list during shutdown. If it's modified
++		 * concurrently at worst we'll just miss a new entry
++		 * before the current ple
++		 *
++		 * gssd_refresh_krb5_machine_credential_internal() will
++		 * release the ple refcount
++		 */
++		ple->refcount++;
++		pthread_mutex_unlock(&ple_lock);
++		/* Make sure cred is up-to-date before returning it */
++		retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple,
++								       NULL, NULL);
++		pthread_mutex_lock(&ple_lock);
++		if (gssd_k5_kt_princ_list == NULL) {
++			/* Looks like we did shutdown... abort */
++			l[i] = NULL;
++			gssd_free_krb5_machine_cred_list(l);
++			retval = ENOMEM;
++			goto out_lock;
++		}
++		if (retval)
++			continue;
++		if (i + 1 > listsize) {
++			listsize += listinc;
++			l = (char **)
++				realloc(l, listsize * sizeof(char *));
++			if (l == NULL) {
+ 				retval = ENOMEM;
+-				goto out;
++				goto out_lock;
+ 			}
+ 		}
++		if ((l[i++] = strdup(ple->ccname)) == NULL) {
++			retval = ENOMEM;
++			goto out_lock;
++		}
+ 	}
+ 	if (i > 0) {
+ 		l[i] = NULL;
+ 		*list = l;
+ 		retval = 0;
+-		goto out;
+ 	} else
+ 		free((void *)l);
++out_lock:
++	pthread_mutex_unlock(&ple_lock);
+   out:
+ 	return retval;
+ }
+@@ -1201,7 +1351,7 @@ gssd_free_krb5_machine_cred_list(char **list)
+  * Called upon exit.  Destroys machine credentials.
+  */
+ void
+-gssd_destroy_krb5_machine_creds(void)
++gssd_destroy_krb5_principals(int destroy_machine_creds)
+ {
+ 	krb5_context context;
+ 	krb5_error_code code = 0;
+@@ -1213,33 +1363,38 @@ gssd_destroy_krb5_machine_creds(void)
+ 	if (code) {
+ 		k5err = gssd_k5_err_msg(NULL, code);
+ 		printerr(0, "ERROR: %s while initializing krb5\n", k5err);
+-		goto out;
++		free(k5err);
++		return;
+ 	}
+ 
+-	for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) {
+-		if (!ple->ccname)
+-			continue;
+-		if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
+-			k5err = gssd_k5_err_msg(context, code);
+-			printerr(0, "WARNING: %s while resolving credential "
+-				    "cache '%s' for destruction\n", k5err,
+-				    ple->ccname);
+-			krb5_free_string(context, k5err);
+-			k5err = NULL;
+-			continue;
+-		}
++	pthread_mutex_lock(&ple_lock);
++	while (gssd_k5_kt_princ_list) {
++		ple = gssd_k5_kt_princ_list;
++		gssd_k5_kt_princ_list = ple->next;
+ 
+-		if ((code = krb5_cc_destroy(context, ccache))) {
+-			k5err = gssd_k5_err_msg(context, code);
+-			printerr(0, "WARNING: %s while destroying credential "
+-				    "cache '%s'\n", k5err, ple->ccname);
+-			krb5_free_string(context, k5err);
+-			k5err = NULL;
++		if (destroy_machine_creds && ple->ccname) {
++			if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) {
++				k5err = gssd_k5_err_msg(context, code);
++				printerr(0, "WARNING: %s while resolving credential "
++					    "cache '%s' for destruction\n", k5err,
++					    ple->ccname);
++				free(k5err);
++				k5err = NULL;
++			}
++
++			if (!code && (code = krb5_cc_destroy(context, ccache))) {
++				k5err = gssd_k5_err_msg(context, code);
++				printerr(0, "WARNING: %s while destroying credential "
++					    "cache '%s'\n", k5err, ple->ccname);
++				free(k5err);
++				k5err = NULL;
++			}
+ 		}
++
++		release_ple(context, ple);
+ 	}
++	pthread_mutex_unlock(&ple_lock);
+ 	krb5_free_context(context);
+-  out:
+-	krb5_free_string(context, k5err);
+ }
+ 
+ /*
+@@ -1247,83 +1402,10 @@ gssd_destroy_krb5_machine_creds(void)
+  */
+ int
+ gssd_refresh_krb5_machine_credential(char *hostname,
+-				     struct gssd_k5_kt_princ *ple, 
+ 				     char *service, char *srchost)
+ {
+-	krb5_error_code code = 0;
+-	krb5_context context;
+-	krb5_keytab kt = NULL;;
+-	int retval = 0;
+-	char *k5err = NULL;
+-	const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
+-
+-	printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n",
+-		__func__, hostname, ple, service, srchost);
+-
+-	/*
+-	 * If a specific service name was specified, use it.
+-	 * Otherwise, use the default list.
+-	 */
+-	if (service != NULL && strcmp(service, "*") != 0) {
+-		svcnames[0] = service;
+-		svcnames[1] = NULL;
+-	}
+-	if (hostname == NULL && ple == NULL)
+-		return EINVAL;
+-
+-	code = krb5_init_context(&context);
+-	if (code) {
+-		k5err = gssd_k5_err_msg(NULL, code);
+-		printerr(0, "ERROR: %s: %s while initializing krb5 context\n",
+-			 __func__, k5err);
+-		retval = code;
+-		goto out;
+-	}
+-
+-	if ((code = krb5_kt_resolve(context, keytabfile, &kt))) {
+-		k5err = gssd_k5_err_msg(context, code);
+-		printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n",
+-			 __func__, k5err, keytabfile);
+-		goto out_free_context;
+-	}
+-
+-	if (ple == NULL) {
+-		krb5_keytab_entry kte;
+-
+-		code = find_keytab_entry(context, kt, srchost, hostname,
+-					 &kte, svcnames);
+-		if (code) {
+-			printerr(0, "ERROR: %s: no usable keytab entry found "
+-				 "in keytab %s for connection with host %s\n",
+-				 __FUNCTION__, keytabfile, hostname);
+-			retval = code;
+-			goto out_free_kt;
+-		}
+-
+-		ple = get_ple_by_princ(context, kte.principal);
+-		k5_free_kt_entry(context, &kte);
+-		if (ple == NULL) {
+-			char *pname;
+-			if ((krb5_unparse_name(context, kte.principal, &pname))) {
+-				pname = NULL;
+-			}
+-			printerr(0, "ERROR: %s: Could not locate or create "
+-				 "ple struct for principal %s for connection "
+-				 "with host %s\n",
+-				 __FUNCTION__, pname ? pname : "<unparsable>",
+-				 hostname);
+-			if (pname) k5_free_unparsed_name(context, pname);
+-			goto out_free_kt;
+-		}
+-	}
+-	retval = gssd_get_single_krb5_cred(context, kt, ple, 0);
+-out_free_kt:
+-	krb5_kt_close(context, kt);
+-out_free_context:
+-	krb5_free_context(context);
+-out:
+-	krb5_free_string(context, k5err);
+-	return retval;
++    return gssd_refresh_krb5_machine_credential_internal(hostname, NULL,
++							 service, srchost);
+ }
+ 
+ /*
+diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
+index b000b444..2415205a 100644
+--- a/utils/gssd/krb5_util.h
++++ b/utils/gssd/krb5_util.h
+@@ -9,27 +9,13 @@
+ #include "gss_oids.h"
+ #endif
+ 
+-/*
+- * List of principals from our keytab that we
+- * will try to use to obtain credentials
+- * (known as a principal list entry (ple))
+- */
+-struct gssd_k5_kt_princ {
+-	struct gssd_k5_kt_princ *next;
+-	krb5_principal princ;
+-	char *ccname;
+-	char *realm;
+-	krb5_timestamp endtime;
+-};
+-
+ 
+ int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername,
+ 				     char *dirname);
+ int  gssd_get_krb5_machine_cred_list(char ***list);
+ void gssd_free_krb5_machine_cred_list(char **list);
+-void gssd_destroy_krb5_machine_creds(void);
++void gssd_destroy_krb5_principals(int destroy_machine_creds);
+ int  gssd_refresh_krb5_machine_credential(char *hostname,
+-					  struct gssd_k5_kt_princ *ple, 
+ 					  char *service, char *srchost);
+ char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
+ void gssd_k5_get_default_realm(char **def_realm);
diff --git a/SOURCES/nfs-utils-2.3.3-mountd-pseudofs.patch b/SOURCES/nfs-utils-2.3.3-mountd-pseudofs.patch
new file mode 100644
index 0000000..04eb082
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-mountd-pseudofs.patch
@@ -0,0 +1,61 @@
+diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c
+index d735dbfe..8ec33fb0 100644
+--- a/utils/mountd/v4root.c
++++ b/utils/mountd/v4root.c
+@@ -36,9 +36,9 @@ static nfs_export pseudo_root = {
+ 	.m_export = {
+ 		.e_hostname = "*",
+ 		.e_path = "/",
+-		.e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH
++		.e_flags = NFSEXP_READONLY
+ 				| NFSEXP_NOSUBTREECHECK | NFSEXP_FSID
+-				| NFSEXP_V4ROOT,
++				| NFSEXP_V4ROOT | NFSEXP_INSECURE_PORT,
+ 		.e_anonuid = 65534,
+ 		.e_anongid = 65534,
+ 		.e_squids = NULL,
+@@ -57,15 +57,11 @@ static nfs_export pseudo_root = {
+ };
+ 
+ static void
+-set_pseudofs_security(struct exportent *pseudo, int flags)
++set_pseudofs_security(struct exportent *pseudo)
+ {
+ 	struct flav_info *flav;
+ 	int i;
+ 
+-	if (flags & NFSEXP_INSECURE_PORT)
+-		pseudo->e_flags |= NFSEXP_INSECURE_PORT;
+-	if ((flags & NFSEXP_ROOTSQUASH) == 0)
+-		pseudo->e_flags &= ~NFSEXP_ROOTSQUASH;
+ 	for (flav = flav_map; flav < flav_map + flav_map_size; flav++) {
+ 		struct sec_entry *new;
+ 
+@@ -75,8 +71,7 @@ set_pseudofs_security(struct exportent *pseudo, int flags)
+ 		i = secinfo_addflavor(flav, pseudo);
+ 		new = &pseudo->e_secinfo[i];
+ 
+-		if (flags & NFSEXP_INSECURE_PORT)
+-			new->flags |= NFSEXP_INSECURE_PORT;
++		new->flags |= NFSEXP_INSECURE_PORT;
+ 	}
+ }
+ 
+@@ -95,7 +90,7 @@ v4root_create(char *path, nfs_export *export)
+ 	strncpy(eep.e_path, path, sizeof(eep.e_path)-1);
+ 	if (strcmp(path, "/") != 0)
+ 		eep.e_flags &= ~NFSEXP_FSID;
+-	set_pseudofs_security(&eep, curexp->e_flags);
++	set_pseudofs_security(&eep);
+ 	exp = export_create(&eep, 0);
+ 	if (exp == NULL)
+ 		return NULL;
+@@ -143,7 +138,7 @@ pseudofs_update(char *hostname, char *path, nfs_export *source)
+ 		return 0;
+ 	}
+ 	/* Update an existing V4ROOT export: */
+-	set_pseudofs_security(&exp->m_export, source->m_export.e_flags);
++	set_pseudofs_security(&exp->m_export);
+ 	return 0;
+ }
+ 
diff --git a/SOURCES/nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch b/SOURCES/nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch
new file mode 100644
index 0000000..8d7991a
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch
@@ -0,0 +1,29 @@
+commit 3e81185037cf97990e4598218f56d92dd70d6269
+Author: NeilBrown <neilb@suse.de>
+Date:   Tue Oct 20 13:19:10 2020 -0400
+
+    clddb-tool was recently renamed to nfsdclddb.
+    Unfortunately the nfsdcld man page wasn't told.
+    
+    Signed-off-by: NeilBrown <neilb@suse.de>
+    Signed-off-by: Steve Dickson <steved@redhat.com>
+
+diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man
+index 4c2b1e80..861f1c49 100644
+--- a/utils/nfsdcld/nfsdcld.man
++++ b/utils/nfsdcld/nfsdcld.man
+@@ -209,12 +209,12 @@ not necessary after upgrading \fBnfsdcld\fR, however \fBnfsd\fR will not use a l
+ version until restart.  A restart of \fBnfsd is necessary\fR after downgrading \fBnfsdcld\fR,
+ to ensure that \fBnfsd\fR does not use an upcall version that \fBnfsdcld\fR does not support.
+ Additionally, a downgrade of \fBnfsdcld\fR requires the schema of the on-disk database to
+-be downgraded as well.  That can be accomplished using the \fBclddb-tool\fR(8) utility.
++be downgraded as well.  That can be accomplished using the \fBnfsdclddb\fR(8) utility.
+ .SH FILES
+ .TP
+ .B /var/lib/nfs/nfsdcld/main.sqlite
+ .SH SEE ALSO
+-.BR nfsdcltrack "(8), " clddb-tool (8)
++.BR nfsdcltrack "(8), " nfsdclddb (8)
+ .SH "AUTHORS"
+ .IX Header "AUTHORS"
+ The nfsdcld daemon was developed by Jeff Layton <jlayton@redhat.com>
diff --git a/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch b/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch
new file mode 100644
index 0000000..8e6d1b3
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-nfsiostat-div-zero.patch
@@ -0,0 +1,12 @@
+diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py
+--- nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig	2020-12-10 10:38:26.462195326 -0500
++++ nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py	2020-12-10 10:45:47.210671473 -0500
+@@ -380,6 +380,8 @@ class DeviceData:
+         sends = float(self.__rpc_data['rpcsends'])
+         if sample_time == 0:
+             sample_time = float(self.__nfs_data['age'])
++        if sample_time == 0:
++            sample_time = 1;
+         return (sends / sample_time)
+ 
+     def display_iostats(self, sample_time, which):
diff --git a/SOURCES/nfs-utils-2.3.3-nfsiostat-key-error.patch b/SOURCES/nfs-utils-2.3.3-nfsiostat-key-error.patch
new file mode 100644
index 0000000..e628c09
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-nfsiostat-key-error.patch
@@ -0,0 +1,37 @@
+diff -up nfs-utils-2.3.3/tools/mountstats/mountstats.py.orig nfs-utils-2.3.3/tools/mountstats/mountstats.py
+--- nfs-utils-2.3.3/tools/mountstats/mountstats.py.orig	2020-12-10 10:48:17.319579958 -0500
++++ nfs-utils-2.3.3/tools/mountstats/mountstats.py	2020-12-10 10:52:42.481484160 -0500
+@@ -943,10 +943,11 @@ def print_iostat_summary(old, new, devic
+         if not old or device not in old:
+             stats.display_iostats(time)
+         else:
+-            old_stats = DeviceData()
+-            old_stats.parse_stats(old[device])
+-            diff_stats = stats.compare_iostats(old_stats)
+-            diff_stats.display_iostats(time)
++            if ("fstype autofs" not in str(old[device])) and ("fstype autofs" not in str(new[device])):
++                old_stats = DeviceData()
++                old_stats.parse_stats(old[device])
++                diff_stats = stats.compare_iostats(old_stats)
++                diff_stats.display_iostats(time)
+ 
+ def iostat_command(args):
+     """iostat-like command for NFS mount points
+diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py
+--- nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig	2020-12-10 10:48:17.316579880 -0500
++++ nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py	2020-12-10 10:52:42.481484160 -0500
+@@ -467,10 +467,13 @@ def parse_stats_file(filename):
+ def print_iostat_summary(old, new, devices, time, options):
+     stats = {}
+     diff_stats = {}
++    devicelist = []
+     if old:
+         # Trim device list to only include intersection of old and new data,
+         # this addresses umounts due to autofs mountpoints
+-        devicelist = [x for x in old if x in devices]
++        for device in devices:
++            if "fstype autofs" not in str(old[device]):
++                devicelist.append(device)
+     else:
+         devicelist = devices
+ 
diff --git a/SOURCES/nfs-utils-2.3.3-systemd-exportfs-nofail.patch b/SOURCES/nfs-utils-2.3.3-systemd-exportfs-nofail.patch
new file mode 100644
index 0000000..a41bdd7
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-systemd-exportfs-nofail.patch
@@ -0,0 +1,37 @@
+commit 003000d451833309c963054e58a48fa1df7e767b
+Author: Steve Dickson <steved@redhat.com>
+Date:   Thu Dec 10 13:13:03 2020 -0500
+
+    exportfs: Ingnore export failures in nfs-server.serivce unit
+    
+    With some recent commits, exportfs will continue on trying to
+    export filesystems even when an entry is invalid or does
+    not exist, but will still have a non-zero exit to report
+    the error.
+    
+    This situation should not stop the nfs-server service
+    from comingup so nfs-server.service file should
+    ignore these types of failures
+    
+    Signed-off-by: Steve Dickson <steved@redhat.com>
+
+diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service
+index 06c1adb7..b432f910 100644
+--- a/systemd/nfs-server.service
++++ b/systemd/nfs-server.service
+@@ -21,13 +21,13 @@ After=rpc-gssd.service gssproxy.service rpc-svcgssd.service
+ [Service]
+ Type=oneshot
+ RemainAfterExit=yes
+-ExecStartPre=/usr/sbin/exportfs -r
++ExecStartPre=-/usr/sbin/exportfs -r
+ ExecStart=/usr/sbin/rpc.nfsd
+ ExecStop=/usr/sbin/rpc.nfsd 0
+ ExecStopPost=/usr/sbin/exportfs -au
+ ExecStopPost=/usr/sbin/exportfs -f
+ 
+-ExecReload=/usr/sbin/exportfs -r
++ExecReload=-/usr/sbin/exportfs -r
+ 
+ [Install]
+ WantedBy=multi-user.target
diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec
index 04d3e5d..2d9af3b 100644
--- a/SPECS/nfs-utils.spec
+++ b/SPECS/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://linux-nfs.org/
 Version: 2.3.3
-Release: 35%{?dist}
+Release: 41%{?dist}
 Epoch: 1
 
 # group all 32bit related archs
@@ -61,6 +61,18 @@ Patch027: nfs-utils-2.3.3-nconnect-manpage.patch
 Patch028: nfs-utils-2.3.3-nfsdclddb-rename.patch
 Patch029: nfs-utils-2.3.3-nfsclnts-cmd.patch
 
+#
+# RHEL 8.4
+#
+Patch030: nfs-utils-2.3.3-exportfs-man-labels.patch
+Patch031: nfs-utils-2.3.3-nfsiostat-div-zero.patch
+Patch032: nfs-utils-2.3.3-nfsiostat-key-error.patch
+Patch033: nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch
+Patch034: nfs-utils-2.3.3-systemd-exportfs-nofail.patch
+Patch035: nfs-utils-2.3.3-exports-manpage-outdated.patch
+Patch036: nfs-utils-2.3.3-gssd-multithread-updates.patch
+Patch037: nfs-utils-2.3.3-mountd-pseudofs.patch
+
 Patch100: nfs-utils-1.2.1-statdpath-man.patch
 Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
 Patch102: nfs-utils-2.3.3-idmap-errmsg.patch
@@ -223,8 +235,6 @@ done
 cat /etc/group | cut -d':' -f 1 | grep --quiet rpcuser 2>/dev/null
 if [ "$?" -eq 1 ]; then
     /usr/sbin/groupadd -g %{rpcuser_uid} rpcuser >/dev/null 2>&1 || :
-else
-    /usr/sbin/groupmod -g %{rpcuser_uid} rpcuser >/dev/null 2>&1 || :
 fi
 
 # Create rpcuser uid as long as it does not already exist.
@@ -253,11 +263,6 @@ if [ $? -eq 1 ]; then
 fi
 
 %post
-
-# Enable nfs-convert so if an old configuration 
-# exists a conversion will occur
-/bin/systemctl enable nfs-convert  >/dev/null 2>&1 || :
-
 if [ $1 -eq 1 ] ; then
 	# Initial installation
 	/bin/systemctl enable nfs-client.target >/dev/null 2>&1 || :
@@ -270,8 +275,6 @@ if [ $1 -eq 0 ]; then
 	%systemd_preun nfs-client.target
 	%systemd_preun nfs-server.server
 
-    rm -rf /var/lib/nfs/statd
-    rm -rf /var/lib/nfs/v4recovery
 fi
 
 %postun
@@ -280,6 +283,11 @@ fi
 
 /bin/systemctl --system daemon-reload >/dev/null 2>&1 || :
 
+if [ $1 -eq 0 ] ; then
+    rm -rf /var/lib/nfs/statd
+    rm -rf /var/lib/nfs/v4recovery
+fi
+
 %triggerin -- nfs-utils > 1:2.1.1-3
 /bin/systemctl try-restart gssproxy || :
 
@@ -347,6 +355,29 @@ fi
 %{_libdir}/libnfsidmap.so
 
 %changelog
+* Wed Jan 20 2021 Steve Dickson <steved@redhat.com> 2.3.3-41
+- mountd: never root squash on the pseudofs (bz 1804912)
+
+* Mon Dec 14 2020 Steve Dickson <steved@redhat.com> 2.3.3-40
+- gssd: upstream multithreaded updates (bz 1906792)
+
+* Fri Dec 11 2020 Steve Dickson <steved@redhat.com> 2.3.3-39
+- systemd: Ingnore export failures in nfs-server.serivce unit (bz 1894873)
+- exports.man: Remove some outdated verbiage (bz 1769688)
+
+* Thu Dec 10 2020 Steve Dickson <steved@redhat.com> 2.3.3-38
+- exports man page: warn about subdirectory exports (bz 1652437)
+- Don't modify /etc/group on upgrades (bz 1856881)
+- nfs-iostat: divide by zero with fresh mount (bz 1861823)
+- nfsiostat: Drop autofs entries before calling compare_iostats() (bz 1859130)
+- nfsdclddb: clddb-tool was recently renamed to nfsdclddb (bz 1893599)
+
+* Thu Dec 10 2020 Alice Mitchell <ajmitchell@redhat.com> 2.3.3-37
+- Remove manual enabling of nfs-convert (bz 1683895)
+
+* Fri Oct  9 2020 Alice Mitchell <ajmitchell@redhat.com> 2.3.3-36
+- Fix uninstall warnings (bz 1733170)
+
 * Wed Jun 10 2020 Steve Dickson <steved@redhat.com> 2.3.3-35
 - Fix dependency problems with nfsdclnts (bz 1841502)