diff --git a/SOURCES/nfs-utils-2.3.3-gssd-printerr.patch b/SOURCES/nfs-utils-2.3.3-gssd-printerr.patch
new file mode 100644
index 0000000..0e564b1
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-gssd-printerr.patch
@@ -0,0 +1,14 @@
+diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c
+--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig	2021-11-04 10:13:07.788142847 -0400
++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c	2021-11-04 10:14:10.829841090 -0400
+@@ -647,8 +647,8 @@ get_full_hostname(const char *inhost, ch
+ 	    *c = tolower(*c);
+ 
+ 	if (get_verbosity() && strcmp(inhost, outhost))
+-		printerr(1, "%s(0x%0lx): inhost '%s' different than outhost'%s'\n", 
+-			inhost, outhost);
++		printerr(1, "%s(0x%0lx): inhost '%s' different than outhost '%s'\n", 
++			 __func__, tid, inhost, outhost);
+ 
+ 	retval = 0;
+ out:
diff --git a/SOURCES/nfs-utils-2.3.3-mount-ebusy.patch b/SOURCES/nfs-utils-2.3.3-mount-ebusy.patch
new file mode 100644
index 0000000..890c553
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-mount-ebusy.patch
@@ -0,0 +1,14 @@
+diff -up nfs-utils-2.3.3/utils/mount/stropts.c.orig nfs-utils-2.3.3/utils/mount/stropts.c
+--- nfs-utils-2.3.3/utils/mount/stropts.c.orig	2022-02-14 11:28:51.570084952 -0500
++++ nfs-utils-2.3.3/utils/mount/stropts.c	2022-02-14 11:29:16.174450628 -0500
+@@ -966,7 +966,9 @@ fall_back:
+ 	if ((result = nfs_try_mount_v3v2(mi, FALSE)))
+ 		return result;
+ 
+-	errno = olderrno;
++	if (errno != EBUSY && errno != EACCES)
++		errno = olderrno;
++
+ 	return result;
+ }
+ 
diff --git a/SOURCES/nfs-utils-2.3.3-mountd-v4-logging.patch b/SOURCES/nfs-utils-2.3.3-mountd-v4-logging.patch
new file mode 100644
index 0000000..59c823d
--- /dev/null
+++ b/SOURCES/nfs-utils-2.3.3-mountd-v4-logging.patch
@@ -0,0 +1,976 @@
+diff --git a/nfs.conf b/nfs.conf
+index 05247ff9..86ed7d53 100644
+--- a/nfs.conf
++++ b/nfs.conf
+@@ -38,6 +38,8 @@ use-gss-proxy=1
+ # reverse-lookup=n
+ # state-directory-path=/var/lib/nfs
+ # ha-callout=
++# cache-use-ipaddr=n
++# ttl=1800
+ #
+ [nfsdcld]
+ # debug=0
+diff --git a/support/export/Makefile.am b/support/export/Makefile.am
+index 13f7a49c..d6ee502f 100644
+--- a/support/export/Makefile.am
++++ b/support/export/Makefile.am
+@@ -11,7 +11,8 @@ EXTRA_DIST	= mount.x
+ 
+ noinst_LIBRARIES = libexport.a
+ libexport_a_SOURCES = client.c export.c hostname.c \
+-		      xtab.c mount_clnt.c mount_xdr.c
++		      xtab.c mount_clnt.c mount_xdr.c \
++			  cache.c auth.c v4root.c v4clients.c
+ BUILT_SOURCES 	= $(GENFILES)
+ 
+ noinst_HEADERS = mount.h
+diff --git a/utils/mountd/auth.c b/support/export/auth.c
+similarity index 98%
+rename from utils/mountd/auth.c
+rename to support/export/auth.c
+index 8299256e..73ad6f73 100644
+--- a/utils/mountd/auth.c
++++ b/support/export/auth.c
+@@ -22,7 +22,7 @@
+ #include "misc.h"
+ #include "nfslib.h"
+ #include "exportfs.h"
+-#include "mountd.h"
++#include "export.h"
+ #include "v4root.h"
+ 
+ enum auth_error
+@@ -43,11 +43,13 @@ extern int use_ipaddr;
+ 
+ extern struct state_paths etab;
+ 
++/*
+ void
+ auth_init(void)
+ {
+ 	auth_reload();
+ }
++*/
+ 
+ /*
+  * A client can match many different netgroups and it's tough to know
+@@ -64,6 +66,10 @@ check_useipaddr(void)
+ 	int old_use_ipaddr = use_ipaddr;
+ 	unsigned int len = 0;
+ 
++	if (use_ipaddr > 1)
++		/* fixed - don't check */
++		return;
++
+ 	/* add length of m_hostname + 1 for the comma */
+ 	for (clp = clientlist[MCL_NETGROUP]; clp; clp = clp->m_next)
+ 		len += (strlen(clp->m_hostname) + 1);
+diff --git a/utils/mountd/cache.c b/support/export/cache.c
+similarity index 95%
+rename from utils/mountd/cache.c
+rename to support/export/cache.c
+index c73e29be..98d50828 100644
+--- a/utils/mountd/cache.c
++++ b/support/export/cache.c
+@@ -29,21 +29,18 @@
+ #include "misc.h"
+ #include "nfslib.h"
+ #include "exportfs.h"
+-#include "mountd.h"
+-#include "fsloc.h"
++#include "export.h"
+ #include "pseudoflavors.h"
+ #include "xcommon.h"
+ 
++#ifdef HAVE_JUNCTION_SUPPORT
++#include "../../utils/mountd/fsloc.h"
++#endif
++
+ #ifdef USE_BLKID
+ #include "blkid/blkid.h"
+ #endif
+ 
+-/*
+- * Invoked by RPC service loop
+- */
+-void	cache_set_fds(fd_set *fdset);
+-int	cache_process_req(fd_set *readfds);
+-
+ enum nfsd_fsid {
+ 	FSID_DEV = 0,
+ 	FSID_NUM,
+@@ -63,7 +60,6 @@ enum nfsd_fsid {
+  * Record is terminated with newline.
+  *
+  */
+-static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path);
+ 
+ #define INITIAL_MANAGED_GROUPS 100
+ 
+@@ -81,6 +77,7 @@ static void auth_unix_ip(int f)
+ 	char class[20];
+ 	char ipaddr[INET6_ADDRSTRLEN + 1];
+ 	char *client = NULL;
++	struct addrinfo *ai = NULL;
+ 	struct addrinfo *tmp = NULL;
+ 	char buf[RPC_CHAN_BUF_SIZE], *bp;
+ 	int blen;
+@@ -106,21 +103,26 @@ static void auth_unix_ip(int f)
+ 
+ 	auth_reload();
+ 
+-	/* addr is a valid, interesting address, find the domain name... */
+-	if (!use_ipaddr) {
+-		struct addrinfo *ai = NULL;
+-
+-		ai = client_resolve(tmp->ai_addr);
+-		if (ai) {
+-			client = client_compose(ai);
+-			freeaddrinfo(ai);
+-		}
++	/* addr is a valid address, find the domain name... */
++	ai = client_resolve(tmp->ai_addr);
++	if (ai) {
++		client = client_compose(ai);
++		freeaddrinfo(ai);
+ 	}
++	if (!client)
++		xlog(D_AUTH, "failed authentication for IP %s", ipaddr);
++	else if	(!use_ipaddr)
++		xlog(D_AUTH, "successful authentication for IP %s as %s",
++		     ipaddr, *client ? client : "DEFAULT");
++	else
++		xlog(D_AUTH, "successful authentication for IP %s",
++			     ipaddr);
++
+ 	bp = buf; blen = sizeof(buf);
+ 	qword_add(&bp, &blen, "nfsd");
+ 	qword_add(&bp, &blen, ipaddr);
+-	qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
+-	if (use_ipaddr) {
++	qword_adduint(&bp, &blen, time(0) + default_ttl);
++	if (use_ipaddr && client) {
+ 		memmove(ipaddr + 1, ipaddr, strlen(ipaddr) + 1);
+ 		ipaddr[0] = '$';
+ 		qword_add(&bp, &blen, ipaddr);
+@@ -192,7 +194,7 @@ static void auth_unix_gid(int f)
+ 
+ 	bp = buf; blen = sizeof(buf);
+ 	qword_adduint(&bp, &blen, uid);
+-	qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL);
++	qword_adduint(&bp, &blen, time(0) + default_ttl);
+ 	if (rv >= 0) {
+ 		qword_adduint(&bp, &blen, ngroups);
+ 		for (i=0; i<ngroups; i++)
+@@ -688,7 +690,6 @@ static void nfsd_fh(int f)
+ 	char *found_path = NULL;
+ 	nfs_export *exp;
+ 	int i;
+-	int dev_missing = 0;
+ 	char buf[RPC_CHAN_BUF_SIZE], *bp;
+ 	int blen;
+ 
+@@ -755,11 +756,6 @@ static void nfsd_fh(int f)
+ 			if (!is_ipaddr_client(dom)
+ 					&& !namelist_client_matches(exp, dom))
+ 				continue;
+-			if (exp->m_export.e_mountpoint &&
+-			    !is_mountpoint(exp->m_export.e_mountpoint[0]?
+-					   exp->m_export.e_mountpoint:
+-					   exp->m_export.e_path))
+-				dev_missing ++;
+ 
+ 			if (!match_fsid(&parsed, exp, path))
+ 				continue;
+@@ -794,7 +790,7 @@ static void nfsd_fh(int f)
+ 	    !is_mountpoint(found->e_mountpoint[0]?
+ 			   found->e_mountpoint:
+ 			   found->e_path)) {
+-		/* Cannot export this yet 
++		/* Cannot export this yet
+ 		 * should log a warning, but need to rate limit
+ 		   xlog(L_WARNING, "%s not exported as %d not a mountpoint",
+ 		   found->e_path, found->e_mountpoint);
+@@ -802,16 +798,6 @@ static void nfsd_fh(int f)
+ 		/* FIXME we need to make sure we re-visit this later */
+ 		goto out;
+ 	}
+-	if (!found && dev_missing) {
+-		/* The missing dev could be what we want, so just be
+-		 * quite rather than returning stale yet
+-		 */
+-		goto out;
+-	}
+-
+-	if (found)
+-		if (cache_export_ent(buf, sizeof(buf), dom, found, found_path) < 0)
+-			found = 0;
+ 
+ 	bp = buf; blen = sizeof(buf);
+ 	qword_add(&bp, &blen, dom);
+@@ -831,6 +817,8 @@ static void nfsd_fh(int f)
+ 	qword_addeol(&bp, &blen);
+ 	if (blen <= 0 || write(f, buf, bp - buf) != bp - buf)
+ 		xlog(L_ERROR, "nfsd_fh: error writing reply");
++	if (!found)
++		xlog(D_AUTH, "denied access to %s", *dom == '$' ? dom+1 : dom);
+ out:
+ 	if (found_path)
+ 		free(found_path);
+@@ -839,6 +827,7 @@ out:
+ 	xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
+ }
+ 
++#ifdef HAVE_JUNCTION_SUPPORT
+ static void write_fsloc(char **bp, int *blen, struct exportent *ep)
+ {
+ 	struct servers *servers;
+@@ -861,7 +850,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep)
+ 	qword_addint(bp, blen, servers->h_referral);
+ 	release_replicas(servers);
+ }
+-
++#endif
+ static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask)
+ {
+ 	struct sec_entry *p;
+@@ -890,7 +879,7 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain,
+ 	time_t now = time(0);
+ 
+ 	if (ttl <= 1)
+-		ttl = DEFAULT_TTL;
++		ttl = default_ttl;
+ 
+ 	qword_add(&bp, &blen, domain);
+ 	qword_add(&bp, &blen, path);
+@@ -903,7 +892,10 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain,
+ 		qword_addint(&bp, &blen, exp->e_anonuid);
+ 		qword_addint(&bp, &blen, exp->e_anongid);
+ 		qword_addint(&bp, &blen, exp->e_fsid);
++
++#ifdef HAVE_JUNCTION_SUPPORT
+ 		write_fsloc(&bp, &blen, exp);
++#endif
+ 		write_secinfo(&bp, &blen, exp, flag_mask);
+ 		if (exp->e_uuid == NULL || different_fs) {
+ 			char u[16];
+@@ -917,8 +909,13 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain,
+ 			qword_add(&bp, &blen, "uuid");
+ 			qword_addhex(&bp, &blen, u, 16);
+ 		}
+-	} else
++		xlog(D_AUTH, "granted access to %s for %s",
++		     path, *domain == '$' ? domain+1 : domain);
++	} else {
+ 		qword_adduint(&bp, &blen, now + ttl);
++		xlog(D_AUTH, "denied access to %s for %s",
++		     path, *domain == '$' ? domain+1 : domain);
++	}
+ 	qword_addeol(&bp, &blen);
+ 	if (blen <= 0) return -1;
+ 	if (write(f, buf, bp - buf) != bp - buf) return -1;
+@@ -1421,6 +1418,40 @@ int cache_process_req(fd_set *readfds)
+ 	return cnt;
+ }
+ 
++/**
++ * cache_process_loop - process incoming upcalls
++ */
++void cache_process_loop(void)
++{
++	fd_set	readfds;
++	int	selret;
++
++	FD_ZERO(&readfds);
++
++	for (;;) {
++
++		cache_set_fds(&readfds);
++		v4clients_set_fds(&readfds);
++
++		selret = select(FD_SETSIZE, &readfds,
++				(void *) 0, (void *) 0, (struct timeval *) 0);
++
++
++		switch (selret) {
++		case -1:
++			if (errno == EINTR || errno == ECONNREFUSED
++			 || errno == ENETUNREACH || errno == EHOSTUNREACH)
++				continue;
++			xlog(L_ERROR, "my_svc_run() - select: %m");
++			return;
++
++		default:
++			cache_process_req(&readfds);
++			v4clients_process(&readfds);
++		}
++	}
++}
++
+ 
+ /*
+  * Give IP->domain and domain+path->options to kernel
+diff --git a/support/export/export.h b/support/export/export.h
+new file mode 100644
+index 00000000..8d5a0d30
+--- /dev/null
++++ b/support/export/export.h
+@@ -0,0 +1,41 @@
++/*
++ * Copyright (C) 2021 Red Hat <nfs@redhat.com>
++ *
++ * support/export/export.h
++ *
++ * Declarations for export support
++ */
++
++#ifndef EXPORT_H
++#define EXPORT_H
++
++#include "nfslib.h"
++#include "exportfs.h"
++
++unsigned int	auth_reload(void);
++nfs_export *	auth_authenticate(const char *what,
++					const struct sockaddr *caller,
++					const char *path);
++
++void		cache_open(void);
++void		cache_set_fds(fd_set *fdset);
++int		cache_process_req(fd_set *readfds);
++void		cache_process_loop(void);
++
++void		v4clients_init(void);
++void		v4clients_set_fds(fd_set *fdset);
++int		v4clients_process(fd_set *fdset);
++
++struct nfs_fh_len *
++		cache_get_filehandle(nfs_export *exp, int len, char *p);
++int		cache_export(nfs_export *exp, char *path);
++
++bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai);
++bool namelist_client_matches(nfs_export *exp, char *dom);
++bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai);
++
++static inline bool is_ipaddr_client(char *dom)
++{
++	return dom[0] == '$';
++}
++#endif /* EXPORT__H */
+diff --git a/support/export/v4clients.c b/support/export/v4clients.c
+new file mode 100644
+index 00000000..dd985463
+--- /dev/null
++++ b/support/export/v4clients.c
+@@ -0,0 +1,227 @@
++/*
++ * support/export/v4clients.c
++ *
++ * Montior clients appearing in, and disappearing from, /proc/fs/nfsd/clients
++ * and log relevant information.
++ */
++
++#include <unistd.h>
++#include <stdlib.h>
++#include <sys/inotify.h>
++#include <errno.h>
++#include "export.h"
++
++/* search.h declares 'struct entry' and nfs_prot.h
++ * does too.  Easiest fix is to trick search.h into
++ * calling its struct "struct Entry".
++ */
++#define entry Entry
++#include <search.h>
++#undef entry
++
++static int clients_fd = -1;
++
++void v4clients_init(void)
++{
++	if (clients_fd >= 0)
++		return;
++	clients_fd = inotify_init1(IN_NONBLOCK);
++	if (clients_fd < 0) {
++		xlog_err("Unable to initialise v4clients watcher: %s\n",
++			 strerror(errno));
++		return;
++	}
++	if (inotify_add_watch(clients_fd, "/proc/fs/nfsd/clients",
++			      IN_CREATE | IN_DELETE) < 0) {
++		xlog_err("Unable to watch /proc/fs/nfsd/clients: %s\n",
++			 strerror(errno));
++		close(clients_fd);
++		clients_fd = -1;
++		return;
++	}
++}
++
++void v4clients_set_fds(fd_set *fdset)
++{
++	if (clients_fd >= 0)
++		FD_SET(clients_fd, fdset);
++}
++
++static void *tree_root;
++static int have_unconfirmed;
++
++struct ent {
++	unsigned long num;
++	char *clientid;
++	char *addr;
++	int vers;
++	int unconfirmed;
++	int wid;
++};
++
++static int ent_cmp(const void *av, const void *bv)
++{
++	const struct ent *a = av;
++	const struct ent *b = bv;
++
++	if (a->num < b->num)
++		return -1;
++	if (a->num > b->num)
++		return 1;
++	return 0;
++}
++
++static void free_ent(struct ent *ent)
++{
++	free(ent->clientid);
++	free(ent->addr);
++	free(ent);
++}
++
++static char *dup_line(char *line)
++{
++	char *ret;
++	char *e = strchr(line, '\n');
++	if (!e)
++		e = line + strlen(line);
++	ret = malloc(e - line + 1);
++	if (ret) {
++		memcpy(ret, line, e - line);
++		ret[e-line] = 0;
++	}
++	return ret;
++}
++
++static void read_info(struct ent *key)
++{
++	char buf[2048];
++	char *path;
++	int was_unconfirmed = key->unconfirmed;
++	FILE *f;
++
++	if (asprintf(&path, "/proc/fs/nfsd/clients/%lu/info", key->num) < 0)
++		return;
++
++	f = fopen(path, "r");
++	if (!f) {
++		free(path);
++		return;
++	}
++	if (key->wid < 0)
++		key->wid = inotify_add_watch(clients_fd, path, IN_MODIFY);
++
++	while (fgets(buf, sizeof(buf), f)) {
++		if (strncmp(buf, "clientid: ", 10) == 0) {
++			free(key->clientid);
++			key->clientid = dup_line(buf+10);
++		}
++		if (strncmp(buf, "address: ", 9) == 0) {
++			free(key->addr);
++			key->addr = dup_line(buf+9);
++		}
++		if (strncmp(buf, "minor version: ", 15) == 0)
++			key->vers = atoi(buf+15);
++		if (strncmp(buf, "status: ", 8) == 0 &&
++		    strstr(buf, " unconfirmed") != NULL) {
++			key->unconfirmed = 1;
++			have_unconfirmed = 1;
++		}
++		if (strncmp(buf, "status: ", 8) == 0 &&
++		    strstr(buf, " confirmed") != NULL)
++			key->unconfirmed = 0;
++	}
++	fclose(f);
++	free(path);
++
++	if (was_unconfirmed && !key->unconfirmed)
++		xlog(L_NOTICE, "v4.%d client attached: %s from %s",
++		     key->vers, key->clientid ?: "-none-",
++		     key->addr ?: "-none-");
++	if (!key->unconfirmed && key->wid >= 0) {
++		inotify_rm_watch(clients_fd, key->wid);
++		key->wid = -1;
++	}
++}
++
++static void add_id(int id)
++{
++	struct ent **ent;
++	struct ent *key;
++
++	key = calloc(1, sizeof(*key));
++	if (!key) {
++		return;
++	}
++	key->num = id;
++	key->wid = -1;
++
++	ent = tsearch(key, &tree_root, ent_cmp);
++
++	if (!ent || *ent != key)
++		/* Already existed, or insertion failed */
++		free_ent(key);
++	else
++		read_info(key);
++}
++
++static void del_id(unsigned long id)
++{
++	struct ent key = {.num = id};
++	struct ent **e, *ent;
++
++	e = tfind(&key, &tree_root, ent_cmp);
++	if (!e || !*e)
++		return;
++	ent = *e;
++	tdelete(ent, &tree_root, ent_cmp);
++	if (!ent->unconfirmed)
++		xlog(L_NOTICE, "v4.%d client detached: %s from %s",
++		     ent->vers, ent->clientid, ent->addr);
++	if (ent->wid >= 0)
++		inotify_rm_watch(clients_fd, ent->wid);
++	free_ent(ent);
++}
++
++static void check_id(unsigned long id)
++{
++	struct ent key = {.num = id};
++	struct ent **e, *ent;
++
++	e = tfind(&key, &tree_root, ent_cmp);
++	if (!e || !*e)
++		return;
++	ent = *e;
++	if (ent->unconfirmed)
++		read_info(ent);
++}
++
++int v4clients_process(fd_set *fdset)
++{
++	char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
++	const struct inotify_event *ev;
++	ssize_t len;
++	char *ptr;
++
++	if (clients_fd < 0 ||
++	    !FD_ISSET(clients_fd, fdset))
++		return 0;
++
++	while ((len = read(clients_fd, buf, sizeof(buf))) > 0) {
++		for (ptr = buf; ptr < buf + len;
++		     ptr += sizeof(struct inotify_event) + ev->len) {
++			int id;
++			ev = (const struct inotify_event *)ptr;
++
++			id = atoi(ev->name);
++			if (id <= 0)
++				continue;
++			if (ev->mask & IN_CREATE)
++				add_id(id);
++			if (ev->mask & IN_DELETE)
++				del_id(id);
++			if (ev->mask & IN_MODIFY)
++				check_id(id);
++		}
++	}
++	return 1;
++}
+diff --git a/utils/mountd/v4root.c b/support/export/v4root.c
+similarity index 99%
+rename from utils/mountd/v4root.c
+rename to support/export/v4root.c
+index 8ec33fb0..4d33117f 100644
+--- a/utils/mountd/v4root.c
++++ b/support/export/v4root.c
+@@ -47,7 +47,7 @@ static nfs_export pseudo_root = {
+ 		.e_nsqgids = 0,
+ 		.e_fsid = 0,
+ 		.e_mountpoint = NULL,
+-		.e_ttl = DEFAULT_TTL,
++		.e_ttl = 0,
+ 	},
+ 	.m_exported = 0,
+ 	.m_xtabent = 1,
+@@ -86,6 +86,7 @@ v4root_create(char *path, nfs_export *export)
+ 	struct exportent *curexp = &export->m_export;
+ 
+ 	dupexportent(&eep, &pseudo_root.m_export);
++	eep.e_ttl = default_ttl;
+ 	eep.e_hostname = curexp->e_hostname;
+ 	strncpy(eep.e_path, path, sizeof(eep.e_path)-1);
+ 	if (strcmp(path, "/") != 0)
+diff --git a/support/include/exportfs.h b/support/include/exportfs.h
+index 4e0d9d13..bfae1957 100644
+--- a/support/include/exportfs.h
++++ b/support/include/exportfs.h
+@@ -105,7 +105,8 @@ typedef struct mexport {
+ } nfs_export;
+ 
+ #define HASH_TABLE_SIZE 1021
+-#define DEFAULT_TTL	(30 * 60)
++
++extern int default_ttl;
+ 
+ typedef struct _exp_hash_entry {
+ 	nfs_export * p_first;
+diff --git a/support/nfs/exports.c b/support/nfs/exports.c
+index a7582cae..4dd2e5d3 100644
+--- a/support/nfs/exports.c
++++ b/support/nfs/exports.c
+@@ -47,6 +47,8 @@ struct flav_info flav_map[] = {
+ 
+ const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]);
+ 
++int default_ttl = 30 * 60;
++
+ static char	*efname = NULL;
+ static XFILE	*efp = NULL;
+ static int	first;
+@@ -100,7 +102,7 @@ static void init_exportent (struct exportent *ee, int fromkernel)
+ 	ee->e_nsquids = 0;
+ 	ee->e_nsqgids = 0;
+ 	ee->e_uuid = NULL;
+-	ee->e_ttl = DEFAULT_TTL;
++	ee->e_ttl = default_ttl;
+ }
+ 
+ struct exportent *
+diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
+index 498d93a9..aa4630bb 100644
+--- a/systemd/nfs.conf.man
++++ b/systemd/nfs.conf.man
+@@ -157,6 +157,8 @@ Recognized values:
+ .BR port ,
+ .BR threads ,
+ .BR reverse-lookup ,
++.BR cache-use-upaddr ,
++.BR ttl ,
+ .BR state-directory-path ,
+ .BR ha-callout .
+ 
+@@ -166,6 +168,14 @@ section, are used to configure mountd.  See
+ .BR rpc.mountd (8)
+ for details.
+ 
++Note that setting 
++.B "\[dq]debug = auth\[dq]"
++for
++.B mountd
++is equivalent to providing the
++.B \-\-log\-auth
++option.
++
+ The
+ .B state-directory-path
+ value in the
+diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am
+index 73eeb3f3..c41f06de 100644
+--- a/utils/mountd/Makefile.am
++++ b/utils/mountd/Makefile.am
+@@ -13,8 +13,8 @@ KPREFIX		= @kprefix@
+ sbin_PROGRAMS	= mountd
+ 
+ noinst_HEADERS = fsloc.h
+-mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \
+-		 svc_run.c fsloc.c v4root.c mountd.h
++mountd_SOURCES = mountd.c mount_dispatch.c rmtab.c \
++		 svc_run.c fsloc.c mountd.h
+ mountd_LDADD = ../../support/export/libexport.a \
+ 	       ../../support/nfs/libnfs.la \
+ 	       ../../support/misc/libmisc.a \
+diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
+index 0b891121..2b342377 100644
+--- a/utils/mountd/mountd.c
++++ b/utils/mountd/mountd.c
+@@ -30,6 +30,7 @@
+ #include "rpcmisc.h"
+ #include "pseudoflavors.h"
+ #include "nfslib.h"
++#include "export.h"
+ 
+ extern void my_svc_run(void);
+ 
+@@ -73,8 +74,12 @@ static struct option longopts[] =
+ 	{ "reverse-lookup", 0, 0, 'r' },
+ 	{ "manage-gids", 0, 0, 'g' },
+ 	{ "no-udp", 0, 0, 'u' },
++	{ "log-auth", 0, 0, 'l'},
++	{ "cache-use-ipaddr", 0, 0, 'i'},
++	{ "ttl", 1, 0, 'T'},
+ 	{ NULL, 0, 0, 0 }
+ };
++static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:";
+ 
+ #define NFSVERSBIT(vers)	(0x1 << (vers - 1))
+ #define NFSVERSBIT_ALL		(NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4))
+@@ -669,6 +674,7 @@ main(int argc, char **argv)
+ 	int	port = 0;
+ 	int	descriptors = 0;
+ 	int	c;
++	int	ttl;
+ 	int	vers;
+ 	struct sigaction sa;
+ 	struct rlimit rlim;
+@@ -687,6 +693,8 @@ main(int argc, char **argv)
+ 	num_threads = conf_get_num("mountd", "threads", num_threads);
+ 	reverse_resolve = conf_get_bool("mountd", "reverse-lookup", reverse_resolve);
+ 	ha_callout_prog = conf_get_str("mountd", "ha-callout");
++	if (conf_get_bool("mountd", "cache-use-ipaddr", 0))
++		use_ipaddr = 2;
+ 
+ 	s = conf_get_str("mountd", "state-directory-path");
+ 	if (s && !state_setup_basedir(argv[0], s))
+@@ -710,10 +718,13 @@ main(int argc, char **argv)
+ 			NFSCTL_VERUNSET(nfs_version, vers);
+ 	}
+ 
++	ttl = conf_get_num("mountd", "ttl", default_ttl);
++	if (ttl > 0)
++		default_ttl = ttl;
+ 
+ 	/* Parse the command line options and arguments. */
+ 	opterr = 0;
+-	while ((c = getopt_long(argc, argv, "o:nFd:p:P:hH:N:V:vurs:t:g", longopts, NULL)) != EOF)
++	while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF)
+ 		switch (c) {
+ 		case 'g':
+ 			manage_gids = 1;
+@@ -784,6 +795,21 @@ main(int argc, char **argv)
+ 		case 'u':
+ 			NFSCTL_UDPUNSET(_rpcprotobits);
+ 			break;
++		case 'l':
++			xlog_sconfig("auth", 1);
++			break;
++		case 'i':
++			use_ipaddr = 2;
++			break;
++		case 'T':
++			ttl = atoi(optarg);
++			if (ttl <= 0) {
++				fprintf(stderr, "%s: bad ttl number of seconds: %s\n",
++					argv[0], optarg);
++				usage(argv[0], 1);
++			}
++			default_ttl = ttl;
++			break;
+ 		case 0:
+ 			break;
+ 		case '?':
+@@ -888,6 +914,8 @@ main(int argc, char **argv)
+ 	if (num_threads > 1)
+ 		fork_workers();
+ 
++	v4clients_init();
++
+ 	xlog(L_NOTICE, "Version " VERSION " starting");
+ 	my_svc_run();
+ 
+@@ -903,6 +931,7 @@ usage(const char *prog, int n)
+ {
+ 	fprintf(stderr,
+ "Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n"
++"	[-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n"
+ "	[-o num|--descriptors num]\n"
+ "	[-p|--port port] [-V version|--nfs-version version]\n"
+ "	[-N version|--no-nfs-version version] [-n|--no-tcp]\n"
+diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
+index f058f01d..d3077531 100644
+--- a/utils/mountd/mountd.h
++++ b/utils/mountd/mountd.h
+@@ -60,9 +60,4 @@ bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai);
+ bool namelist_client_matches(nfs_export *exp, char *dom);
+ bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai);
+ 
+-static inline bool is_ipaddr_client(char *dom)
+-{
+-	return dom[0] == '$';
+-}
+-
+ #endif /* MOUNTD_H */
+diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
+index 8a7943f8..2a91e193 100644
+--- a/utils/mountd/mountd.man
++++ b/utils/mountd/mountd.man
+@@ -13,24 +13,24 @@ The
+ .B rpc.mountd
+ daemon implements the server side of the NFS MOUNT protocol,
+ an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813].
++It also responds to requests from the Linux kernel to authenticate
++clients and provides details of access permissions.
+ .PP
+-An NFS server maintains a table of local physical file systems
+-that are accessible to NFS clients.
+-Each file system in this table is referred to as an
+-.IR "exported file system" ,
+-or
+-.IR export ,
+-for short.
+-.PP
+-Each file system in the export table has an access control list.
+-.B rpc.mountd
+-uses these access control lists to determine
+-whether an NFS client is permitted to access a given file system.
+-For details on how to manage your NFS server's export table, see the
+-.BR exports (5)
+-and
+-.BR exportfs (8)
+-man pages.
++The NFS server
++.RI ( nfsd )
++maintains a cache of authentication and authorization information which
++is used to identify the source of each request, and then what access
++permissions that source has to any local filesystem.  When required
++information is not found in the cache, the server sends a request to
++.B mountd
++to fill in the missing information.  Mountd uses a table of information
++stored in
++.B /var/lib/nfs/etab
++and maintained by
++.BR exportfs (8),
++possibly based on the contents of 
++.BR exports (5),
++to respond to each request.
+ .SS Mounting exported NFS File Systems
+ The NFS MOUNT protocol has several procedures.
+ The most important of these are
+@@ -78,11 +78,69 @@ A client may continue accessing an export even after invoking UMNT.
+ If the client reboots without sending a UMNT request, stale entries
+ remain for that client in
+ .IR /var/lib/nfs/rmtab .
++.SS Mounting File Systems with NFSv4
++Version 4 (and later) of NFS does not use a separate NFS MOUNT
++protocol.  Instead mounting is performed using regular NFS requests
++handled by the NFS server in the Linux kernel
++.RI ( nfsd ).
++Consequently
++.I /var/lib/nfs/rmtab
++is not updated to reflect any NFSv4 activity.
+ .SH OPTIONS
+ .TP
+ .B \-d kind " or " \-\-debug kind
+ Turn on debugging. Valid kinds are: all, auth, call, general and parse.
+ .TP
++.BR \-l " or " \-\-log\-auth
++Enable logging of responses to authentication and access requests from
++nfsd.  Each response is then cached by the kernel for 30 minutes (or as set by
++.B \-\-ttl
++below), and will be refreshed after 15 minutes (half the ttl time) if
++the relevant client remains active.
++Note that
++.B -l
++is equivalent to
++.B "-d auth"
++and so can be enabled in
++.B /etc/nfs.conf
++with
++.B "\[dq]debug = auth\[dq]"
++in the
++.B "[mountd]"
++section.
++.IP
++.B rpc.mountd
++will always log authentication responses to MOUNT requests when NFSv3 is
++used, but to get similar logs for NFSv4, this option is required.
++.TP
++.BR \-i " or " \-\-cache\-use\-ipaddr
++Normally each client IP address is matched against each host identifier
++(name, wildcard, netgroup etc) found in
++.B /etc/exports
++and a combined identity is formed from all matching identifiers.
++Often many clients will map to the same combined identity so performing
++this mapping reduces the number of distinct access details that the
++kernel needs to store.
++Specifying the
++.B \-i
++option suppresses this mapping so that access to each filesystem is
++requested and cached separately for each client IP address.  Doing this
++can increase the burden of updating the cache slightly, but can make the
++log messages produced by the
++.B -l
++option easier to read.
++.TP
++.B \-T " or " \-\-ttl
++Provide a time-to-live (TTL) for cached information given to the kernel.
++The kernel will normally request an update if the information is needed
++after half of this time has expired.  Increasing the provided number,
++which is in seconds, reduces the rate of cache update requests, and this
++is particularly noticeable when these requests are logged with
++.BR \-l .
++However increasing also means that changes to hostname to address
++mappings can take longer to be noticed.
++The default TTL is 1800 (30 minutes).
++.TP
+ .B \-F " or " \-\-foreground
+ Run in foreground (do not daemonize)
+ .TP
+@@ -213,9 +271,11 @@ Values recognized in the
+ .B [mountd]
+ section include
+ .BR manage-gids ,
++.BR cache\-use\-ipaddr ,
+ .BR descriptors ,
+ .BR port ,
+ .BR threads ,
++.BR ttl ,
+ .BR reverse-lookup ", and"
+ .BR state-directory-path ,
+ .B ha-callout
+@@ -265,5 +325,9 @@ table of clients accessing server's exports
+ RFC 1094 - "NFS: Network File System Protocol Specification"
+ .br
+ RFC 1813 - "NFS Version 3 Protocol Specification"
++.br
++RFC 7530 - "Network File System (NFS) Version 4 Protocol"
++.br
++RFC 8881 - "Network File System (NFS) Version 4 Minor Version 1 Protocol"
+ .SH AUTHOR
+ Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
+diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c
+index 41b96d7f..167b9757 100644
+--- a/utils/mountd/svc_run.c
++++ b/utils/mountd/svc_run.c
+@@ -56,10 +56,9 @@
+ #ifdef HAVE_LIBTIRPC
+ #include <rpc/rpc_com.h>
+ #endif
++#include "export.h"
+ 
+ void my_svc_run(void);
+-void cache_set_fds(fd_set *fdset);
+-int cache_process_req(fd_set *readfds);
+ 
+ #if defined(__GLIBC__) && LONG_MAX != INT_MAX
+ /* bug in glibc 2.3.6 and earlier, we need
+@@ -101,6 +100,7 @@ my_svc_run(void)
+ 
+ 		readfds = svc_fdset;
+ 		cache_set_fds(&readfds);
++		v4clients_set_fds(&readfds);
+ 
+ 		selret = select(FD_SETSIZE, &readfds,
+ 				(void *) 0, (void *) 0, (struct timeval *) 0);
+@@ -116,6 +116,7 @@ my_svc_run(void)
+ 
+ 		default:
+ 			selret -= cache_process_req(&readfds);
++			selret -= v4clients_process(&readfds);
+ 			if (selret)
+ 				svc_getreqset(&readfds);
+ 		}
diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec
index 345fa9f..f8fe8a2 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: 46%{?dist}
+Release: 50%{?dist}
 Epoch: 1
 
 # group all 32bit related archs
@@ -85,6 +85,11 @@ Patch043: nfs-utils-2.3.3-gssd-timeout-thread.patch
 Patch044: nfs-utils-2.3.3-gssd-debug-cleanup.patch
 Patch045: nfs-utils-2.3.3-gssd-mutex-refcnt.patch
 
+#
+# RHEL 8.6
+Patch046: nfs-utils-2.3.3-mountd-v4-logging.patch
+Patch047: nfs-utils-2.3.3-gssd-printerr.patch
+Patch048: nfs-utils-2.3.3-mount-ebusy.patch
 
 Patch100: nfs-utils-1.2.1-statdpath-man.patch
 Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
@@ -360,6 +365,18 @@ fi
 %{_libdir}/libnfsidmap.so
 
 %changelog
+* Sat Feb 19 2022 Steve Dickson <steved@redhat.com> 2.3.3-50
+- mount.nfs: Fix Typo auto negotiating code. (bz 1946346)
+
+* Mon Feb 14 2022 Steve Dickson <steved@redhat.com> 2.3.3-49
+- mount.nfs Fix error reporting for already mounted shares (bz 1946346)
+
+* Thu Nov 4 2021 Steve Dickson <steved@redhat.com> 2.3.3-48
+- gssd: fix crash in debug message (bz 1988283)
+
+* Tue Nov 2 2021 Steve Dickson <steved@redhat.com> 2.3.3-47
+- Enable logging for NFSv4 mount requests (bz 2004151)
+
 * Wed Jul 28 2021 Steve Dickson <steved@redhat.com> 2.3.3-46
 - mount.nfs: Fix the sloppy option processing (bz 1967883)