From 94b4863eff31caae8c57361cb1307d6cc6a2431b Mon Sep 17 00:00:00 2001
From: Steve Dickson <steved@redhat.com>
Date: Wed, 2 Mar 2022 16:07:05 -0500
Subject: [PATCH] Updated to the latest RC release: nfs-utils-2-6-2-rc3

Signed-off-by: Steve Dickson <steved@redhat.com>
---
 nfs-utils-2.3.3-man-tcpwrappers.patch |  22 +-
 nfs-utils-2.6.2-rc3.patch             | 565 ++++++++++++++++++++++++++
 nfs-utils.spec                        |   9 +-
 3 files changed, 583 insertions(+), 13 deletions(-)
 create mode 100644 nfs-utils-2.6.2-rc3.patch

diff --git a/nfs-utils-2.3.3-man-tcpwrappers.patch b/nfs-utils-2.3.3-man-tcpwrappers.patch
index 65fee3a..44e4edc 100644
--- a/nfs-utils-2.3.3-man-tcpwrappers.patch
+++ b/nfs-utils-2.3.3-man-tcpwrappers.patch
@@ -1,8 +1,8 @@
-diff -up nfs-utils-2.3.3/utils/mountd/mountd.man.orig nfs-utils-2.3.3/utils/mountd/mountd.man
---- nfs-utils-2.3.3/utils/mountd/mountd.man.orig	2018-09-06 14:09:08.000000000 -0400
-+++ nfs-utils-2.3.3/utils/mountd/mountd.man	2018-10-26 09:53:10.005127368 -0400
-@@ -232,36 +232,7 @@ section include
- which each have same same meaning as given by
+diff -up nfs-utils-2.6.1/utils/mountd/mountd.man.orig nfs-utils-2.6.1/utils/mountd/mountd.man
+--- nfs-utils-2.6.1/utils/mountd/mountd.man.orig	2022-03-02 15:53:18.898475385 -0500
++++ nfs-utils-2.6.1/utils/mountd/mountd.man	2022-03-02 15:54:02.362872285 -0500
+@@ -291,36 +291,7 @@ section include
+ which each have the same meaning as given by
  .BR rpc.nfsd (8).
  
 -.SH TCP_WRAPPERS SUPPORT
@@ -39,7 +39,7 @@ diff -up nfs-utils-2.3.3/utils/mountd/mountd.man.orig nfs-utils-2.3.3/utils/moun
  TI-RPC is a pre-requisite for supporting NFS on IPv6.
  If TI-RPC support is built into
  .BR rpc.mountd ,
-@@ -288,7 +259,6 @@ table of clients accessing server's expo
+@@ -347,7 +318,6 @@ table of clients accessing server's expo
  .BR nfs (5),
  .BR nfs.conf (5),
  .BR tcpd (8),
@@ -47,10 +47,10 @@ diff -up nfs-utils-2.3.3/utils/mountd/mountd.man.orig nfs-utils-2.3.3/utils/moun
  .BR iptables (8),
  .BR netconfig (5)
  .sp
-diff -up nfs-utils-2.3.3/utils/statd/statd.man.orig nfs-utils-2.3.3/utils/statd/statd.man
---- nfs-utils-2.3.3/utils/statd/statd.man.orig	2018-10-26 09:52:27.609358805 -0400
-+++ nfs-utils-2.3.3/utils/statd/statd.man	2018-10-26 09:53:37.345978117 -0400
-@@ -319,28 +319,6 @@ chooses, simply use
+diff -up nfs-utils-2.6.1/utils/statd/statd.man.orig nfs-utils-2.6.1/utils/statd/statd.man
+--- nfs-utils-2.6.1/utils/statd/statd.man.orig	2022-03-02 15:53:18.899475395 -0500
++++ nfs-utils-2.6.1/utils/statd/statd.man	2022-03-02 15:54:02.363872294 -0500
+@@ -325,28 +325,6 @@ chooses, simply use
  .BR chown (1)
  to set the owner of
  the state directory.
@@ -79,7 +79,7 @@ diff -up nfs-utils-2.3.3/utils/statd/statd.man.orig nfs-utils-2.3.3/utils/statd/
  .SH ADDITIONAL NOTES
  Lock recovery after a reboot is critical to maintaining data integrity
  and preventing unnecessary application hangs.
-@@ -445,7 +423,6 @@ network transport capability database
+@@ -451,7 +429,6 @@ network transport capability database
  .BR rpc.nfsd (8),
  .BR rpcbind (8),
  .BR tcpd (8),
diff --git a/nfs-utils-2.6.2-rc3.patch b/nfs-utils-2.6.2-rc3.patch
new file mode 100644
index 0000000..a6ed921
--- /dev/null
+++ b/nfs-utils-2.6.2-rc3.patch
@@ -0,0 +1,565 @@
+diff --git a/configure.ac b/configure.ac
+index 50e9b32..e0f5a93 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -737,6 +737,7 @@ AC_CONFIG_FILES([
+ 	tools/rpcgen/Makefile
+ 	tools/mountstats/Makefile
+ 	tools/nfs-iostat/Makefile
++	tools/rpcctl/Makefile
+ 	tools/nfsdclnts/Makefile
+ 	tools/nfsconf/Makefile
+ 	tools/nfsdclddb/Makefile
+diff --git a/support/nfs/rpcdispatch.c b/support/nfs/rpcdispatch.c
+index f7c27c9..7329f41 100644
+--- a/support/nfs/rpcdispatch.c
++++ b/support/nfs/rpcdispatch.c
+@@ -26,12 +26,13 @@ rpc_dispatch(struct svc_req *rqstp, SVCXPRT *transp,
+ 			void *argp, void *resp)
+ {
+ 	struct rpc_dentry	*dent;
++	int rq_vers = (int)rqstp->rq_vers;
+ 
+-	if (((int)rqstp->rq_vers) > nvers) {
++	if (rq_vers < 1 || rq_vers > nvers) {
+ 		svcerr_progvers(transp, 1, nvers);
+ 		return;
+ 	}
+-	dtable += (rqstp->rq_vers - 1);
++	dtable += (rq_vers - 1);
+ 	if (rqstp->rq_proc > dtable->nproc) {
+ 		svcerr_noproc(transp);
+ 		return;
+diff --git a/support/nfsidmap/idmapd.conf.5 b/support/nfsidmap/idmapd.conf.5
+index f5b1816..87e39bb 100644
+--- a/support/nfsidmap/idmapd.conf.5
++++ b/support/nfsidmap/idmapd.conf.5
+@@ -198,8 +198,8 @@ With this group names of a central directory can be shortened for an isolated or
+ .TP
+ .B Group-Name-No-Prefix-Regex
+ Case-insensitive regular expression to exclude groups from adding and removing the prefix set by
+-.B Group-Name-Prefix
+-. The regular expression must match both the remote and local group names. Multiple expressions may be concatenated with '|'.
++.BR Group-Name-Prefix .
++The regular expression must match both the remote and local group names. Multiple expressions may be concatenated with '|'.
+ (Default: none)
+ .\"
+ .\" -------------------------------------------------------------------
+diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man
+index 4436a38..be487a1 100644
+--- a/systemd/nfs.conf.man
++++ b/systemd/nfs.conf.man
+@@ -171,7 +171,6 @@ Recognized values:
+ .BR lease-time ,
+ .BR udp ,
+ .BR tcp ,
+-.BR vers2 ,
+ .BR vers3 ,
+ .BR vers4 ,
+ .BR vers4.0 ,
+diff --git a/systemd/rpc-pipefs-generator.c b/systemd/rpc-pipefs-generator.c
+index c24db56..7b2bb4f 100644
+--- a/systemd/rpc-pipefs-generator.c
++++ b/systemd/rpc-pipefs-generator.c
+@@ -28,11 +28,12 @@ static int generate_mount_unit(const char *pipefs_path, const char *pipefs_unit,
+ {
+ 	char	*path;
+ 	FILE	*f;
++	size_t size = (strlen(dirname) + 1 + strlen(pipefs_unit));
+ 
+-	path = malloc(strlen(dirname) + 1 + strlen(pipefs_unit));
++	path = malloc(size);
+ 	if (!path)
+ 		return 1;
+-	sprintf(path, "%s/%s", dirname, pipefs_unit);
++	snprintf(path, size, "%s/%s", dirname, pipefs_unit);
+ 	f = fopen(path, "w");
+ 	if (!f)
+ 	{
+diff --git a/tests/t0001-statd-basic-mon-unmon.sh b/tests/t0001-statd-basic-mon-unmon.sh
+index 92517a1..e1065e7 100755
+--- a/tests/t0001-statd-basic-mon-unmon.sh
++++ b/tests/t0001-statd-basic-mon-unmon.sh
+@@ -21,8 +21,9 @@
+ 
+ . ./test-lib.sh
+ 
+-# This test needs root privileges
++# This test needs root privileges and /dev/log
+ check_root
++check_dev_log
+ 
+ start_statd
+ if [ $? -ne 0 ]; then
+diff --git a/tests/test-lib.sh b/tests/test-lib.sh
+index e47ad13..b62ac2a 100644
+--- a/tests/test-lib.sh
++++ b/tests/test-lib.sh
+@@ -37,6 +37,15 @@ check_root() {
+ 	fi
+ }
+ 
++# Most tests require /dev/log. Skip the test if it doesn't exist in this
++# environment.
++check_dev_log() {
++	if ! [ -e /dev/log ]; then
++		echo "*** Skipping this tests as it requires /dev/log ***"
++		exit 77
++	fi
++}
++
+ # is lockd registered as a service?
+ lockd_registered() {
+ 	rpcinfo -p | grep -q nlockmgr
+diff --git a/tools/Makefile.am b/tools/Makefile.am
+index 9b4b080..c3feabb 100644
+--- a/tools/Makefile.am
++++ b/tools/Makefile.am
+@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD
+ OPTDIRS += nfsdclddb
+ endif
+ 
+-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS)
++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS)
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/tools/rpcctl/Makefile.am b/tools/rpcctl/Makefile.am
+new file mode 100644
+index 0000000..33fb431
+--- /dev/null
++++ b/tools/rpcctl/Makefile.am
+@@ -0,0 +1,13 @@
++## Process this file with automake to produce Makefile.in
++PYTHON_FILES =  rpcctl.py
++
++man8_MANS = rpcctl.man
++
++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES)
++
++all-local: $(PYTHON_FILES)
++
++install-data-hook:
++	$(INSTALL) -m 755 rpcctl.py $(DESTDIR)$(sbindir)/rpcctl
++
++MAINTAINERCLEANFILES=Makefile.in
+diff --git a/tools/rpcctl/rpcctl.man b/tools/rpcctl/rpcctl.man
+new file mode 100644
+index 0000000..b87ba0d
+--- /dev/null
++++ b/tools/rpcctl/rpcctl.man
+@@ -0,0 +1,67 @@
++.\"
++.\" rpcctl(8)
++.\"
++.TH rpcctl 8 "15 Feb 2022"
++.SH NAME
++rpcctl \- Displays SunRPC connection information
++.SH SYNOPSIS
++.nf
++.BR rpcctl " [ \fB\-h \fR| \fB\-\-help \fR] { \fBclient \fR| \fBswitch \fR| \fBxprt \fR}"
++.P
++.BR "rpcctl client" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBshow \fR}"
++.BR "rpcctl client show " "\fR[ \fB\-h \f| \fB\-\-help \fR] [ \fIXPRT \fR]"
++.P
++.BR "rpcctl switch" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBset \fR| \fBshow \fR}"
++.BR "rpcctl switch set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fISWITCH \fBdstaddr \fINEWADDR"
++.BR "rpcctl switch show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fISWITCH \fR]"
++.P
++.BR "rpcctl xprt" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBremove \fR| \fBset \fR| \fBshow \fR}"
++.BR "rpcctl xprt remove" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT"
++.BR "rpcctl xprt set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT \fR{ \fBdstaddr \fINEWADDR \fR| \fBoffline \fR| \fBonline \fR}"
++.BR "rpcctl xprt show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fIXPRT \fR]"
++.fi
++.SH DESCRIPTION
++.RB "The " rpcctl " command displays information collected in the SunRPC sysfs files about the system's SunRPC objects.
++.P
++.SS rpcctl client \fR- \fBCommands operating on RPC clients
++.IP "\fBshow \fR[ \fICLIENT \fR] \fB(default)"
++Show detailed information about the RPC clients on this system.
++If \fICLIENT \fRwas provided, then only show information about a single RPC client.
++.P
++.SS rpcctl switch \fR- \fBCommands operating on groups of transports
++.IP "\fBset \fISWITCH \fBdstaddr \fINEWADDR"
++Change the destination address of all transports in the \fISWITCH \fRto \fINEWADDR\fR.
++\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3).
++.IP "\fBshow \fR[ \fISWITCH \fR] \fB(default)"
++Show detailed information about groups of transports on this system.
++If \fISWITCH \fRwas provided, then only show information about a single transport group.
++.P
++.SS rpcctl xprt \fR- \fBCommands operating on individual transports
++.IP "\fBremove \fIXPRT"
++Removes the specified \fIXPRT \fRfrom the system.
++Note that "main" transports cannot be removed.
++.P
++.IP "\fBset \fIXPRT \fBdstaddr \fINEWADDR"
++Change the destination address of the specified \fIXPRT \fR to \fINEWADDR\fR.
++\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3).
++.P
++.IP "\fBset \fIXPRT \fBoffline"
++Sets the specified \fIXPRT\fR's state to offline.
++.P
++.IP "\fBset \fIXPRT \fBonline"
++Sets the specified \fIXPRT\fR's state to online.
++.IP "\fBshow \fR[ \fIXPRT \fR] \fB(default)"
++Show detailed information about this system's transports.
++If \fIXPRT \fRwas provided, then only show information about a single transport.
++.SH EXAMPLES
++.IP "\fBrpcctl switch show switch-2"
++Show details about the RPC switch named "switch-2".
++.IP "\fBrpcctl xprt remove xprt-4"
++Remove the xprt named "xprt-4" from the system.
++.IP "\fBrpcctl xprt set xprt-3 dstaddr https://linux-nfs.org
++Change the dstaddr of the xprt named "xprt-3" to point to linux-nfs.org
++.SH DIRECTORY
++.TP
++.B /sys/kernel/sunrpc/
++.SH AUTHOR
++Anna Schumaker <Anna.Schumaker@Netapp.com>
+diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py
+new file mode 100755
+index 0000000..b8df556
+--- /dev/null
++++ b/tools/rpcctl/rpcctl.py
+@@ -0,0 +1,255 @@
++#!/usr/bin/python3
++import argparse
++import collections
++import errno
++import os
++import pathlib
++import socket
++import sys
++
++with open("/proc/mounts", 'r') as f:
++    mount = [ line.split()[1] for line in f if "sysfs" in line ]
++    if len(mount) == 0:
++        print("ERROR: sysfs is not mounted")
++        sys.exit(1)
++
++sunrpc = pathlib.Path(mount[0]) / "kernel" / "sunrpc"
++if not sunrpc.is_dir():
++    print("ERROR: sysfs does not have sunrpc directory")
++    sys.exit(1)
++
++def read_addr_file(path):
++    try:
++        with open(path, 'r') as f:
++            return f.readline().strip()
++    except:
++        return "(enoent)"
++
++def write_addr_file(path, newaddr):
++    with open(path, 'w') as f:
++        f.write(newaddr)
++    return read_addr_file(path)
++
++def read_info_file(path):
++    res = collections.defaultdict(int)
++    try:
++        with open(path) as info:
++            lines = [ l.split("=", 1) for l in info if "=" in l ]
++            res.update({ key:int(val.strip()) for (key, val) in lines })
++    finally:
++        return res
++
++
++class Xprt:
++    def __init__(self, path):
++        self.path = path
++        self.name = path.stem.rsplit("-", 1)[0]
++        self.type = path.stem.split("-")[2]
++        self.info = read_info_file(path / "xprt_info")
++        self.dstaddr = read_addr_file(path / "dstaddr")
++        self.srcaddr = read_addr_file(path / "srcaddr")
++        self.read_state()
++
++    def __lt__(self, rhs):
++        return self.name < rhs.name
++
++    def _xprt(self):
++        main = ", main" if self.info.get("main_xprt") else ""
++        return f"{self.name}: {self.type}, {self.dstaddr}, " \
++               f"port {self.info['dst_port']}, state <{self.state}>{main}"
++
++    def _src_reqs(self):
++        return f"	Source: {self.srcaddr}, port {self.info['src_port']}, " \
++               f"Requests: {self.info['num_reqs']}"
++
++    def _cong_slots(self):
++        return f"	Congestion: cur {self.info['cur_cong']}, win {self.info['cong_win']}, " \
++               f"Slots: min {self.info['min_num_slots']}, max {self.info['max_num_slots']}"
++
++    def _queues(self):
++        return f"	Queues: binding {self.info['binding_q_len']}, " \
++               f"sending {self.info['sending_q_len']}, pending {self.info['pending_q_len']}, " \
++               f"backlog {self.info['backlog_q_len']}, tasks {self.info['tasks_queuelen']}"
++
++    def __str__(self):
++        if not self.path.exists():
++            return f"{self.name}: has been removed"
++        return "\n".join([self._xprt(), self._src_reqs(),
++                          self._cong_slots(), self._queues() ])
++
++    def read_state(self):
++        if self.path.exists():
++            with open(self.path / "xprt_state") as f:
++                self.state = ','.join(f.readline().split()[1:])
++
++    def small_str(self):
++        main = " [main]" if self.info.get("main_xprt") else ""
++        return f"{self.name}: {self.type}, {self.dstaddr}{main}"
++
++    def set_dstaddr(self, newaddr):
++        self.dstaddr = write_addr_file(self.path / "dstaddr", newaddr)
++
++    def set_state(self, state):
++        with open(self.path / "xprt_state", 'w') as f:
++            f.write(state)
++        self.read_state()
++
++    def add_command(subparser):
++        parser = subparser.add_parser("xprt", help="Commands for individual xprts")
++        parser.set_defaults(func=Xprt.show, xprt=None)
++        subparser = parser.add_subparsers()
++
++        remove = subparser.add_parser("remove", help="Remove an xprt")
++        remove.add_argument("xprt", metavar="XPRT", nargs=1,
++                            help="Name of the xprt to remove")
++        remove.set_defaults(func=Xprt.set_property, property="remove")
++
++        show = subparser.add_parser("show", help="Show xprts")
++        show.add_argument("xprt", metavar="XPRT", nargs='?',
++                          help="Name of a specific xprt to show")
++        show.set_defaults(func=Xprt.show)
++
++        set = subparser.add_parser("set", help="Change an xprt property")
++        set.add_argument("xprt", metavar="XPRT", nargs=1,
++                         help="Name of a specific xprt to modify")
++        subparser = set.add_subparsers(required=True)
++        online = subparser.add_parser("online", help="Set an xprt online")
++        online.set_defaults(func=Xprt.set_property, property="online")
++        offline = subparser.add_parser("offline", help="Set an xprt offline")
++        offline.set_defaults(func=Xprt.set_property, property="offline")
++        dstaddr = subparser.add_parser("dstaddr", help="Change an xprt's dstaddr")
++        dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
++                             help="The new address for the xprt")
++        dstaddr.set_defaults(func=Xprt.set_property, property="dstaddr")
++
++    def get_by_name(name):
++        glob = f"**/{name}-*" if name else "**/xprt-*"
++        res = [ Xprt(x) for x in (sunrpc / "xprt-switches").glob(glob) ]
++        if name and len(res) == 0:
++            raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
++                                    f"{sunrpc / 'xprt-switches' / glob}")
++        return sorted(res)
++
++    def show(args):
++        for xprt in Xprt.get_by_name(args.xprt):
++            print(xprt)
++
++    def set_property(args):
++        for xprt in Xprt.get_by_name(args.xprt[0]):
++            if args.property == "dstaddr":
++                xprt.set_dstaddr(socket.gethostbyname(args.newaddr[0]))
++            elif args.property == "remove":
++                xprt.set_state("offline")
++                xprt.set_state("remove")
++            else:
++                args.set_state(args.property)
++        print(xprt)
++
++
++class XprtSwitch:
++    def __init__(self, path, sep=":"):
++        self.path = path
++        self.name = path.stem
++        self.info = read_info_file(path / "xprt_switch_info")
++        self.xprts = sorted([ Xprt(p) for p in self.path.iterdir() if p.is_dir() ])
++        self.sep = sep
++
++    def __lt__(self, rhs):
++        return self.name < rhs.name
++
++    def __str__(self):
++        switch =  f"{self.name}{self.sep} " \
++                  f"xprts {self.info['num_xprts']}, " \
++                  f"active {self.info['num_active']}, " \
++                  f"queue {self.info['queue_len']}"
++        xprts = [ f"	{x.small_str()}" for x in self.xprts ]
++        return "\n".join([ switch ] + xprts)
++
++    def add_command(subparser):
++        parser = subparser.add_parser("switch", help="Commands for xprt switches")
++        parser.set_defaults(func=XprtSwitch.show, switch=None)
++        subparser = parser.add_subparsers()
++
++        show = subparser.add_parser("show", help="Show xprt switches")
++        show.add_argument("switch", metavar="SWITCH", nargs='?',
++                          help="Name of a specific switch to show")
++        show.set_defaults(func=XprtSwitch.show)
++
++        set = subparser.add_parser("set", help="Change an xprt switch property")
++        set.add_argument("switch", metavar="SWITCH", nargs=1,
++                         help="Name of a specific xprt switch to modify")
++        subparser = set.add_subparsers(required=True)
++        dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr")
++        dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1,
++                             help="The new address for the xprt switch")
++        dstaddr.set_defaults(func=XprtSwitch.set_property, property="dstaddr")
++
++    def get_by_name(name):
++        xprt_switches = sunrpc / "xprt-switches"
++        if name:
++            return [ XprtSwitch(xprt_switches / name) ]
++        return [ XprtSwitch(f) for f in sorted(xprt_switches.iterdir()) ]
++
++    def show(args):
++        for switch in XprtSwitch.get_by_name(args.switch):
++            print(switch)
++
++    def set_property(args):
++        for switch in XprtSwitch.get_by_name(args.switch[0]):
++            resolved = socket.gethostbyname(args.newaddr[0])
++            for xprt in switch.xprts:
++                xprt.set_dstaddr(resolved)
++        print(switch)
++
++
++class RpcClient:
++    def __init__(self, path):
++        self.path = path
++        self.name = path.stem
++        self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",")
++
++    def __lt__(self, rhs):
++        return self.name < rhs.name
++
++    def __str__(self):
++        return f"{self.name}: {self.switch}"
++
++    def add_command(subparser):
++        parser = subparser.add_parser("client", help="Commands for rpc clients")
++        parser.set_defaults(func=RpcClient.show, client=None)
++        subparser = parser.add_subparsers()
++
++        show = subparser.add_parser("show", help="Show rpc clients")
++        show.add_argument("client", metavar="CLIENT", nargs='?',
++                          help="Name of a specific rpc client to show")
++        parser.set_defaults(func=RpcClient.show)
++
++    def get_by_name(name):
++        rpc_clients = sunrpc / "rpc-clients"
++        if name:
++            return [ RpcClient(rpc_clients / name) ]
++        return [ RpcClient(f) for f in sorted(rpc_clients.iterdir()) ]
++
++    def show(args):
++        for client in RpcClient.get_by_name(args.client):
++            print(client)
++
++
++parser = argparse.ArgumentParser()
++
++def show_small_help(args):
++    parser.print_usage()
++    print("sunrpc dir:", sunrpc)
++parser.set_defaults(func=show_small_help)
++
++subparser = parser.add_subparsers(title="commands")
++RpcClient.add_command(subparser)
++XprtSwitch.add_command(subparser)
++Xprt.add_command(subparser)
++
++args = parser.parse_args()
++try:
++    args.func(args)
++except Exception as e:
++    print(str(e))
++    sys.exit(1)
+diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
+index e2c160e..e79c124 100644
+--- a/utils/idmapd/idmapd.c
++++ b/utils/idmapd/idmapd.c
+@@ -474,7 +474,7 @@ main(int argc, char **argv)
+ 		event_free(svrdirev);
+ 	event_base_free(evbase);
+ 
+-	return 1;
++	return 0;
+ }
+ 
+ static void
+diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man
+index 5f34d2b..8215d25 100644
+--- a/utils/idmapd/idmapd.man
++++ b/utils/idmapd/idmapd.man
+@@ -24,13 +24,13 @@ the NFSv4 kernel client and server, to which it communicates via
+ upcalls, by translating user and group IDs to names, and vice versa.
+ .Pp
+ The system derives the
+-.I user
++.Em user
+ part of the string by performing a password or group lookup.
+ The lookup mechanism is configured in
+ .Pa /etc/idmapd.conf
+ .Pp
+ By default, the
+-.I domain
++.Em domain
+ part of the string is the system's DNS domain name.
+ It can also be specified in
+ .Pa /etc/idmapd.conf
+diff --git a/utils/mount/nfsmount.conf.man b/utils/mount/nfsmount.conf.man
+index 73c3e11..34879c8 100644
+--- a/utils/mount/nfsmount.conf.man
++++ b/utils/mount/nfsmount.conf.man
+@@ -1,4 +1,4 @@
+-."@(#)nfsmount.conf.5"
++.\" @(#)nfsmount.conf.5"
+ .TH NFSMOUNT.CONF 5 "16 December 2020"
+ .SH NAME
+ nfsmount.conf - Configuration file for NFS mounts
+diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
+index 3c4e218..dbdd11e 100644
+--- a/utils/mount/stropts.c
++++ b/utils/mount/stropts.c
+@@ -973,7 +973,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/utils/mountd/mountd.man b/utils/mountd/mountd.man
+index 77e6299..a206a3e 100644
+--- a/utils/mountd/mountd.man
++++ b/utils/mountd/mountd.man
+@@ -286,10 +286,9 @@ The values recognized in the
+ section include
+ .BR TCP ,
+ .BR UDP ,
+-.BR vers2 ,
+ .BR vers3 ", and"
+ .B vers4
+-which each have same same meaning as given by
++which each have the same meaning as given by
+ .BR rpc.nfsd (8).
+ 
+ .SH TCP_WRAPPERS SUPPORT
+diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man
+index 716f538..634b8a6 100644
+--- a/utils/nfsd/nfsd.man
++++ b/utils/nfsd/nfsd.man
+@@ -156,8 +156,6 @@ Enable (with "on" or "yes" etc) or disable ("off", "no") UDP support.
+ .B TCP
+ Enable or disable TCP support.
+ .TP
+-.B vers2
+-.TP
+ .B vers3
+ .TP
+ .B vers4
diff --git a/nfs-utils.spec b/nfs-utils.spec
index 521a508..1837092 100644
--- a/nfs-utils.spec
+++ b/nfs-utils.spec
@@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser
 Name: nfs-utils
 URL: http://linux-nfs.org/
 Version: 2.6.1
-Release: 0%{?dist}
+Release: 0.rc3%{?dist}
 Epoch: 1
 
 # group all 32bit related archs
@@ -17,7 +17,7 @@ Source5: nfsconvert.sh
 Source6: nfs-convert.service
 Source7: 10-nfsv4.conf
 
-Patch001: nfs-utils-2.6.1-overflow.patch
+Patch001: nfs-utils-2.6.2-rc3.patch
 
 Patch100: nfs-utils-1.2.1-statdpath-man.patch
 Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch
@@ -31,6 +31,7 @@ Provides: exportfs    = %{epoch}:%{version}-%{release}
 Provides: nfsstat     = %{epoch}:%{version}-%{release}
 Provides: showmount   = %{epoch}:%{version}-%{release}
 Provides: rpcdebug    = %{epoch}:%{version}-%{release}
+Provides: rpcctl      = %{epoch}:%{version}-%{release}
 Provides: rpc.idmapd  = %{epoch}:%{version}-%{release}
 Provides: rpc.mountd  = %{epoch}:%{version}-%{release}
 Provides: rpc.nfsd    = %{epoch}:%{version}-%{release}
@@ -328,6 +329,7 @@ fi
 %{_sbindir}/exportfs
 %{_sbindir}/nfsstat
 %{_sbindir}/rpcdebug
+%{_sbindir}/rpcctl
 %{_sbindir}/rpc.mountd
 %{_sbindir}/rpc.nfsd
 %{_sbindir}/showmount
@@ -453,6 +455,9 @@ fi
 %{_mandir}/*/nfsiostat.8.gz
 
 %changelog
+* Wed Mar  2 2022 Steve Dickson <steved@redhat.com> 2.6.1-0.rc3
+- Updated to the latest RC release: nfs-utils-2-6-2-rc3
+
 * Wed Feb 23 2022 Steve Dickson <steved@redhat.com> 2.6.1-0
 - Updated to the latest upstream release: nfs-utils-2-6-1 (bz 2022136)