public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/nfs-utils] rawhide: Updated to the latest RC release: nfs-utils-2-9-2-rc4
@ 2026-05-31 12:23 Steve Dickson
0 siblings, 0 replies; only message in thread
From: Steve Dickson @ 2026-05-31 12:23 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/nfs-utils
Branch : rawhide
Commit : 1a9c20bc3cc9b8597cadddca241d4a310827fbd8
Author : Steve Dickson <steved@redhat.com>
Date : 2026-05-31T08:22:11-04:00
Stats : +1142/-716 in 3 file(s)
URL : https://src.fedoraproject.org/rpms/nfs-utils/c/1a9c20bc3cc9b8597cadddca241d4a310827fbd8?branch=rawhide
Log:
Updated to the latest RC release: nfs-utils-2-9-2-rc4
Signed-off-by: Steve Dickson <steved@redhat.com>
---
diff --git a/nfs-utils-2.9.2-rc3.patch b/nfs-utils-2.9.2-rc3.patch
deleted file mode 100644
index 3e1e005..0000000
--- a/nfs-utils-2.9.2-rc3.patch
+++ /dev/null
@@ -1,714 +0,0 @@
-diff --git a/configure.ac b/configure.ac
-index 115c611f..8ca06fd6 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -256,6 +256,8 @@ PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1)
- PKG_CHECK_MODULES(LIBNLGENL3, libnl-genl-3.0 >= 3.1)
-
- AC_CHECK_HEADERS(linux/nfsd_netlink.h)
-+AC_DEFINE([HAVE_NFSD_NETLINK], 1,
-+ [Define to 1 if nfsd generic netlink support is available])
-
- # ensure we have the expkey attributes
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
-diff --git a/support/export/cache.c b/support/export/cache.c
-index 2f128d7d..65008f51 100644
---- a/support/export/cache.c
-+++ b/support/export/cache.c
-@@ -40,7 +40,7 @@
- #include <netlink/genl/ctrl.h>
- #include <netlink/msg.h>
- #include <netlink/attr.h>
--#include <linux/netlink.h>
-+#include "compat.h"
-
- #ifdef USE_SYSTEM_NFSD_NETLINK_H
- #include <linux/nfsd_netlink.h>
-diff --git a/support/export/cache_flush.c b/support/export/cache_flush.c
-index ed7b964f..2a24dec7 100644
---- a/support/export/cache_flush.c
-+++ b/support/export/cache_flush.c
-@@ -38,6 +38,8 @@ extern int no_netlink;
- #include "sunrpc_netlink.h"
- #endif
-
-+#include "compat.h"
-+
- static int nl_send_flush(struct nl_sock *sock, int family, int cmd)
- {
- struct nl_msg *msg;
-diff --git a/support/export/xtab.c b/support/export/xtab.c
-index 282f15bc..0a966051 100644
---- a/support/export/xtab.c
-+++ b/support/export/xtab.c
-@@ -33,11 +33,8 @@ int v4root_needed;
- static void cond_rename(char *newfile, char *oldfile);
-
- static int
--xtab_read(char *xtab, char *lockfn, int is_export)
-+xtab_read(char *xtab, char *lockfn)
- {
-- /* is_export == 0 => reading /proc/fs/nfs/exports - we know these things are exported to kernel
-- * is_export == 1 => reading /var/lib/nfs/etab - these things are allowed to be exported
-- */
- struct exportent *xp;
- nfs_export *exp;
- int lockid;
-@@ -45,11 +42,10 @@ xtab_read(char *xtab, char *lockfn, int is_export)
- if ((lockid = xflock(lockfn, "r")) < 0)
- return 0;
- setexportent(xtab, "r");
-- if (is_export == 1)
-- v4root_needed = 1;
-- while ((xp = getexportent(is_export==0)) != NULL) {
-- if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) &&
-- !(exp = export_create(xp, is_export!=1))) {
-+ v4root_needed = 1;
-+ while ((xp = getexportent(0)) != NULL) {
-+ if (!(exp = export_lookup(xp->e_hostname, xp->e_path, 0)) &&
-+ !(exp = export_create(xp, 0))) {
- if(xp->e_hostname) {
- free(xp->e_hostname);
- xp->e_hostname=NULL;
-@@ -60,17 +56,10 @@ xtab_read(char *xtab, char *lockfn, int is_export)
- }
- continue;
- }
-- switch (is_export) {
-- case 0:
-- exp->m_exported = 1;
-- break;
-- case 1:
-- exp->m_xtabent = 1;
-- exp->m_mayexport = 1;
-- if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
-- v4root_needed = 0;
-- break;
-- }
-+ exp->m_xtabent = 1;
-+ exp->m_mayexport = 1;
-+ if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
-+ v4root_needed = 0;
- if(xp->e_hostname) {
- free(xp->e_hostname);
- xp->e_hostname=NULL;
-@@ -90,7 +79,7 @@ xtab_read(char *xtab, char *lockfn, int is_export)
- int
- xtab_export_read(void)
- {
-- return xtab_read(etab.statefn, etab.lockfn, 1);
-+ return xtab_read(etab.statefn, etab.lockfn);
- }
-
- /*
-@@ -100,7 +89,7 @@ xtab_export_read(void)
- * fix the auth_reload logic as well...
- */
- static int
--xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
-+xtab_write(char *xtab, char *xtabtmp, char *lockfn)
- {
- struct exportent xe;
- nfs_export *exp;
-@@ -114,9 +103,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
-
- for (i = 0; i < MCL_MAXTYPES; i++) {
- for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-- if (is_export && !exp->m_xtabent)
-- continue;
-- if (!is_export && ! exp->m_exported)
-+ if (!exp->m_xtabent)
- continue;
-
- /* write out the export entry using the FQDN */
-@@ -137,7 +124,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
- int
- xtab_export_write(void)
- {
-- return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1);
-+ return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn);
- }
-
- /*
-diff --git a/support/include/compat.h b/support/include/compat.h
-new file mode 100644
-index 00000000..83229b65
---- /dev/null
-+++ b/support/include/compat.h
-@@ -0,0 +1,10 @@
-+#ifndef COMPAT_H
-+#define COMPAT_H
-+
-+#include <linux/netlink.h>
-+
-+#ifndef NETLINK_EXT_ACK
-+#define NETLINK_EXT_ACK 11
-+#endif
-+
-+#endif /* COMPAT_H */
-diff --git a/support/include/nfsd_netlink.h b/support/include/nfsd_netlink.h
-index 2d708d24..a6a83186 100644
---- a/support/include/nfsd_netlink.h
-+++ b/support/include/nfsd_netlink.h
-@@ -128,6 +128,27 @@ enum {
- NFSD_A_POOL_MODE_MAX = (__NFSD_A_POOL_MODE_MAX - 1)
- };
-
-+enum {
-+ NFSD_A_UNLOCK_IP_ADDRESS = 1,
-+
-+ __NFSD_A_UNLOCK_IP_MAX,
-+ NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1)
-+};
-+
-+enum {
-+ NFSD_A_UNLOCK_FILESYSTEM_PATH = 1,
-+
-+ __NFSD_A_UNLOCK_FILESYSTEM_MAX,
-+ NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1)
-+};
-+
-+enum {
-+ NFSD_A_UNLOCK_EXPORT_PATH = 1,
-+
-+ __NFSD_A_UNLOCK_EXPORT_MAX,
-+ NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
-+};
-+
- enum {
- NFSD_A_FSLOCATION_HOST = 1,
- NFSD_A_FSLOCATION_PATH,
-@@ -229,6 +250,9 @@ enum {
- NFSD_CMD_EXPKEY_GET_REQS,
- NFSD_CMD_EXPKEY_SET_REQS,
- NFSD_CMD_CACHE_FLUSH,
-+ NFSD_CMD_UNLOCK_IP,
-+ NFSD_CMD_UNLOCK_FILESYSTEM,
-+ NFSD_CMD_UNLOCK_EXPORT,
-
- __NFSD_CMD_MAX,
- NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
-diff --git a/support/include/nfsdnl.h b/support/include/nfsdnl.h
-new file mode 100644
-index 00000000..352801e5
---- /dev/null
-+++ b/support/include/nfsdnl.h
-@@ -0,0 +1,34 @@
-+/*
-+ * Helper for sending nfsd generic netlink commands.
-+ *
-+ * Used by both nfsdctl and exportfs.
-+ */
-+
-+#ifndef NFS_UTILS_NFSDNL_H
-+#define NFS_UTILS_NFSDNL_H
-+
-+#ifdef HAVE_NFSD_NETLINK
-+
-+/**
-+ * nfsd_nl_cmd_str - send an nfsd netlink command carrying a string attribute
-+ * @cmd: NFSD_CMD_* command number
-+ * @attr: NFSD_A_* attribute number
-+ * @value: NUL-terminated string value for the attribute
-+ *
-+ * Opens a genetlink connection, resolves the "nfsd" family, sends a
-+ * single "do" command with one string attribute, waits for the ACK,
-+ * and cleans up.
-+ *
-+ * Returns 0 on success or a negative errno on failure.
-+ */
-+int nfsd_nl_cmd_str(int cmd, int attr, const char *value);
-+
-+#else
-+
-+static inline int nfsd_nl_cmd_str(int cmd, int attr, const char *value)
-+{
-+ return -ENOSYS;
-+}
-+
-+#endif /* HAVE_NFSD_NETLINK */
-+#endif /* NFS_UTILS_NFSDNL_H */
-diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
-index 5bfd71a9..64ad5d07 100644
---- a/support/nfs/Makefile.am
-+++ b/support/nfs/Makefile.am
-@@ -11,6 +11,13 @@ libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
- libnfs_la_LIBADD = libnfsconf.la -luuid
- libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
-
-+if CONFIG_NFSDCTL
-+libnfs_la_SOURCES += nfsdnl.c
-+libnfs_la_CPPFLAGS += $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS) \
-+ -I$(top_srcdir)/utils/nfsdctl
-+libnfs_la_LIBADD += $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS)
-+endif
-+
- libnfsconf_la_SOURCES = conffile.c xlog.c
-
- MAINTAINERCLEANFILES = Makefile.in
-diff --git a/support/nfs/fh_key_file.c b/support/nfs/fh_key_file.c
-index 5f5eafc1..81ea1500 100644
---- a/support/nfs/fh_key_file.c
-+++ b/support/nfs/fh_key_file.c
-@@ -26,6 +26,7 @@
- #include <sys/types.h>
- #include <unistd.h>
- #include <errno.h>
-+#include <string.h>
- #include <uuid/uuid.h>
-
- #include "nfslib.h"
-diff --git a/support/nfs/nfsdnl.c b/support/nfs/nfsdnl.c
-new file mode 100644
-index 00000000..ece0b57a
---- /dev/null
-+++ b/support/nfs/nfsdnl.c
-@@ -0,0 +1,124 @@
-+/*
-+ * nfsdnl.c -- send nfsd generic netlink commands
-+ *
-+ * Helper shared by nfsdctl and exportfs.
-+ */
-+
-+#ifdef HAVE_CONFIG_H
-+#include <config.h>
-+#endif
-+
-+#include <errno.h>
-+#include <string.h>
-+
-+#include <netlink/genl/genl.h>
-+#include <netlink/genl/ctrl.h>
-+#include <netlink/msg.h>
-+#include <netlink/attr.h>
-+
-+#include "xlog.h"
-+#include "nfsdnl.h"
-+
-+#ifdef USE_SYSTEM_NFSD_NETLINK_H
-+#include <linux/nfsd_netlink.h>
-+#else
-+#include "nfsd_netlink.h"
-+#endif
-+
-+#define NFSDNL_BUFSIZE (4096)
-+
-+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
-+ void *arg)
-+{
-+ int *ret = arg;
-+ *ret = err->error;
-+ return NL_STOP;
-+}
-+
-+static int finish_handler(struct nl_msg *msg, void *arg)
-+{
-+ int *ret = arg;
-+ *ret = 0;
-+ return NL_SKIP;
-+}
-+
-+static int ack_handler(struct nl_msg *msg __attribute__((unused)),
-+ void *arg)
-+{
-+ int *ret = arg;
-+ *ret = 0;
-+ return NL_STOP;
-+}
-+
-+/**
-+ * nfsd_nl_cmd_str - send an nfsd netlink command carrying a string attribute
-+ * @cmd: NFSD_CMD_* command number
-+ * @attr: NFSD_A_* attribute number
-+ * @value: NUL-terminated string value for the attribute
-+ *
-+ * Returns 0 on success or a negative errno on failure.
-+ */
-+int nfsd_nl_cmd_str(int cmd, int attr, const char *value)
-+{
-+ struct genlmsghdr *ghdr;
-+ struct nl_sock *sock;
-+ struct nl_msg *msg;
-+ struct nl_cb *cb;
-+ int family;
-+ int ret;
-+
-+ sock = nl_socket_alloc();
-+ if (!sock)
-+ return -ENOMEM;
-+ if (genl_connect(sock)) {
-+ ret = -ECONNREFUSED;
-+ goto out_sock;
-+ }
-+ nl_socket_set_buffer_size(sock, NFSDNL_BUFSIZE, NFSDNL_BUFSIZE);
-+
-+ family = genl_ctrl_resolve(sock, NFSD_FAMILY_NAME);
-+ if (family < 0) {
-+ ret = family;
-+ goto out_sock;
-+ }
-+
-+ msg = nlmsg_alloc();
-+ if (!msg) {
-+ ret = -ENOMEM;
-+ goto out_sock;
-+ }
-+ if (!genlmsg_put(msg, 0, 0, family, 0, 0, 0, 0)) {
-+ ret = -ENOMEM;
-+ goto out_msg;
-+ }
-+
-+ ghdr = nlmsg_data(nlmsg_hdr(msg));
-+ ghdr->cmd = (__u8)cmd;
-+ nla_put_string(msg, attr, value);
-+
-+ cb = nl_cb_alloc(NL_CB_CUSTOM);
-+ if (!cb) {
-+ ret = -ENOMEM;
-+ goto out_msg;
-+ }
-+
-+ ret = nl_send_auto(sock, msg);
-+ if (ret < 0)
-+ goto out_cb;
-+
-+ ret = 1;
-+ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
-+ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &ret);
-+ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
-+
-+ while (ret > 0)
-+ nl_recvmsgs(sock, cb);
-+
-+out_cb:
-+ nl_cb_put(cb);
-+out_msg:
-+ nlmsg_free(msg);
-+out_sock:
-+ nl_socket_free(sock);
-+ return ret;
-+}
-diff --git a/support/reexport/backend_sqlite.c b/support/reexport/backend_sqlite.c
-index 0eb5ea37..a1e981e4 100644
---- a/support/reexport/backend_sqlite.c
-+++ b/support/reexport/backend_sqlite.c
-@@ -9,6 +9,8 @@
- #include <string.h>
- #include <unistd.h>
-
-+#include <sys/syscall.h>
-+
- #ifdef HAVE_GETRANDOM
- # include <sys/random.h>
- # if !defined(SYS_getrandom) && defined(__NR_getrandom)
-diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
-index 69d24a11..af04aac5 100755
---- a/tools/nfs-iostat/nfs-iostat.py
-+++ b/tools/nfs-iostat/nfs-iostat.py
-@@ -327,7 +327,7 @@ class DeviceData:
- print()
- print('%d congestion waits' % congestionwaits)
-
-- def __print_rpc_op_stats(self, op, sample_time):
-+ def __print_rpc_op_stats(self, op, sample_time, use_mb=False):
- """Print generic stats for one RPC op
- """
- if op not in self.__rpc_data:
-@@ -343,9 +343,19 @@ class DeviceData:
- if len(rpc_stats) >= 9:
- errs = float(rpc_stats[8])
-
-+ # scale to MB if requested
-+ if use_mb:
-+ throughput = kilobytes / 1024.0
-+ throughput_label = 'MB/s'
-+ per_op_label = 'MB/op'
-+ else:
-+ throughput = kilobytes
-+ throughput_label = 'kB/s'
-+ per_op_label = 'kB/op'
-+
- # prevent floating point exceptions
- if ops != 0:
-- kb_per_op = kilobytes / ops
-+ unit_per_op = throughput / ops
- retrans_percent = (retrans * 100) / ops
- rtt_per_op = rtt / ops
- exe_per_op = exe / ops
-@@ -353,7 +363,7 @@ class DeviceData:
- if len(rpc_stats) >= 9:
- errs_percent = (errs * 100) / ops
- else:
-- kb_per_op = 0.0
-+ unit_per_op = 0.0
- retrans_percent = 0.0
- rtt_per_op = 0.0
- exe_per_op = 0.0
-@@ -364,8 +374,8 @@ class DeviceData:
- op += ':'
- print(format(op.lower(), '<16s'), end='')
- print(format('ops/s', '>8s'), end='')
-- print(format('kB/s', '>16s'), end='')
-- print(format('kB/op', '>16s'), end='')
-+ print(format(throughput_label, '>16s'), end='')
-+ print(format(per_op_label, '>16s'), end='')
- print(format('retrans', '>16s'), end='')
- print(format('avg RTT (ms)', '>16s'), end='')
- print(format('avg exe (ms)', '>16s'), end='')
-@@ -375,8 +385,8 @@ class DeviceData:
- print()
-
- print(format((ops / sample_time), '>24.3f'), end='')
-- print(format((kilobytes / sample_time), '>16.3f'), end='')
-- print(format(kb_per_op, '>16.3f'), end='')
-+ print(format((throughput / sample_time), '>16.3f'), end='')
-+ print(format(unit_per_op, '>16.3f'), end='')
- retransmits = '{0:>10.0f} ({1:>3.1f}%)'.format(retrans, retrans_percent).strip()
- print(format(retransmits, '>16'), end='')
- print(format(rtt_per_op, '>16.3f'), end='')
-@@ -395,9 +405,11 @@ class DeviceData:
- sample_time = 1;
- return (sends / sample_time)
-
-- def display_iostats(self, sample_time, which):
-+ def display_iostats(self, sample_time, options):
- """Display NFS and RPC stats in an iostat-like way
- """
-+ which = options.which
-+ use_mb = options.megabytes
- sends = float(self.__rpc_data['rpcsends'])
- if sample_time == 0:
- sample_time = float(self.__nfs_data['age'])
-@@ -423,21 +435,21 @@ class DeviceData:
- print()
-
- if which == 0:
-- self.__print_rpc_op_stats('READ', sample_time)
-- self.__print_rpc_op_stats('WRITE', sample_time)
-+ self.__print_rpc_op_stats('READ', sample_time, use_mb)
-+ self.__print_rpc_op_stats('WRITE', sample_time, use_mb)
- elif which == 1:
-- self.__print_rpc_op_stats('GETATTR', sample_time)
-- self.__print_rpc_op_stats('ACCESS', sample_time)
-+ self.__print_rpc_op_stats('GETATTR', sample_time, use_mb)
-+ self.__print_rpc_op_stats('ACCESS', sample_time, use_mb)
- self.__print_attr_cache_stats(sample_time)
- elif which == 2:
-- self.__print_rpc_op_stats('LOOKUP', sample_time)
-- self.__print_rpc_op_stats('READDIR', sample_time)
-+ self.__print_rpc_op_stats('LOOKUP', sample_time, use_mb)
-+ self.__print_rpc_op_stats('READDIR', sample_time, use_mb)
- if 'READDIRPLUS' in self.__rpc_data:
-- self.__print_rpc_op_stats('READDIRPLUS', sample_time)
-+ self.__print_rpc_op_stats('READDIRPLUS', sample_time, use_mb)
- self.__print_dir_cache_stats(sample_time)
- elif which == 3:
-- self.__print_rpc_op_stats('READ', sample_time)
-- self.__print_rpc_op_stats('WRITE', sample_time)
-+ self.__print_rpc_op_stats('READ', sample_time, use_mb)
-+ self.__print_rpc_op_stats('WRITE', sample_time, use_mb)
- self.__print_page_stats(sample_time)
-
- sys.stdout.flush()
-@@ -500,7 +512,7 @@ def print_iostat_summary(old, new, devices, time, options):
-
- count = 1
- for device in devices:
-- display_stats[device].display_iostats(time, options.which)
-+ display_stats[device].display_iostats(time, options)
-
- count += 1
- if (count > options.list):
-@@ -585,6 +597,11 @@ client are listed.
- type="int",
- dest="list",
- help="only print stats for first LIST mount points")
-+ displaygroup.add_option('-m', '--megabytes',
-+ action="store_true",
-+ dest="megabytes",
-+ default=False,
-+ help="display throughput in megabytes per second (MB/s) instead of kilobytes per second (kB/s)")
- parser.add_option_group(displaygroup)
-
- (options, args) = parser.parse_args(sys.argv)
-diff --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man
-index 104c7ab4..4f24318d 100644
---- a/tools/nfs-iostat/nfsiostat.man
-+++ b/tools/nfs-iostat/nfsiostat.man
-@@ -56,16 +56,16 @@ This is the length of the backlog queue.
- .RE
- .RE
- .RS 8
--- \fBkB/s\fR
-+- \fBkB/s (MB/s)\fR
- .RS
--This is the number of kB written/read per second.
-+This is the number of kB (or MB) written/read per second.
- .RE
- .RE
- .RE
- .RS 8
--- \fBkB/op\fR
-+- \fBkB/op (MB/op)\fR
- .RS
--This is the number of kB written/read per each operation.
-+This is the number of kB (or MB) written/read per each operation.
- .RE
- .RE
- .RE
-@@ -122,6 +122,9 @@ shows help message and exit
- .B \-l LIST or " \-\-list=LIST
- only print stats for first LIST mount points
- .TP
-+.B \-m or " \-\-megabytes
-+display throughput in megabytes per second
-+.TP
- .B \-p " or " \-\-page
- displays statistics related to the page cache
- .TP
-diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
-index 93f0bcd7..768d2db7 100644
---- a/utils/exportfs/exportfs.c
-+++ b/utils/exportfs/exportfs.c
-@@ -39,6 +39,15 @@
- #include "xlog.h"
- #include "conffile.h"
- #include "reexport.h"
-+#include "nfsdnl.h"
-+
-+#ifdef HAVE_NFSD_NETLINK
-+#ifdef USE_SYSTEM_NFSD_NETLINK_H
-+#include <linux/nfsd_netlink.h>
-+#else
-+#include "nfsd_netlink.h"
-+#endif
-+#endif
-
- #include <netlink/genl/genl.h>
- #include <netlink/genl/ctrl.h>
-@@ -63,6 +72,7 @@ static void release_lockfile(void);
-
- static const char *lockfile = EXP_LOCKFILE;
- static int _lockfd = -1;
-+static int f_unexport_all;
-
- /*
- * If we aren't careful, changes made by exportfs can be lost
-@@ -246,7 +256,8 @@ main(int argc, char **argv)
- * don't care about what should be exported, as that
- * may require DNS lookups..
- */
-- if (! ( !f_export && f_all)) {
-+ f_unexport_all = !f_export && f_all;
-+ if (!f_unexport_all) {
- /* note: xtab_*_read does not update entries if they already exist,
- * so this will not lose new options
- */
-@@ -380,6 +391,26 @@ exportfs(char *arg, char *options, int verbose)
- xlog(L_ERROR, "Invalid export syntax: %s", arg);
- }
-
-+/*
-+ * Check whether any active export remains for the given path across
-+ * all client types. Returns true if at least one export still has
-+ * m_xtabent set.
-+ */
-+static int
-+path_still_exported(const char *path, size_t nlen)
-+{
-+ nfs_export *exp;
-+ int i;
-+
-+ for (i = 0; i < MCL_MAXTYPES; i++)
-+ for (exp = exportlist[i].p_head; exp; exp = exp->m_next)
-+ if (exp->m_xtabent &&
-+ strlen(exp->m_export.e_path) == nlen &&
-+ strncmp(path, exp->m_export.e_path, nlen) == 0)
-+ return 1;
-+ return 0;
-+}
-+
- static void
- unexportfs_parsed(char *hname, char *path, int verbose)
- {
-@@ -434,9 +465,39 @@ unexportfs_parsed(char *hname, char *path, int verbose)
- exp->m_mayexport = 0;
- success = 1;
- }
-- if (!success)
-+ if (!success) {
- xlog(L_ERROR, "Could not find '%s:%s' to unexport.", hname, path);
-+ goto out;
-+ }
-
-+ /*
-+ * If no exports remain for this path, ask the kernel to
-+ * revoke any NFSv4 state and close cached file handles
-+ * associated with exports of this path. This enables the
-+ * underlying filesystem to be unmounted.
-+ *
-+ * Skip this during "exportfs -ua" -- that is a shutdown
-+ * operation. Clients should wait for nfsd to restart and
-+ * reclaim state through the grace period rather than
-+ * receiving NFS4ERR_ADMIN_REVOKED.
-+ */
-+#ifdef HAVE_NFSD_NETLINK
-+ if (!f_unexport_all && !path_still_exported(path, nlen)) {
-+ char pathbuf[NFS_MAXPATHLEN + 1];
-+ int ret;
-+
-+ memcpy(pathbuf, path, nlen);
-+ pathbuf[nlen] = '\0';
-+ ret = nfsd_nl_cmd_str(NFSD_CMD_UNLOCK_EXPORT,
-+ NFSD_A_UNLOCK_EXPORT_PATH,
-+ pathbuf);
-+ if (ret && ret != -ENOSYS)
-+ xlog(L_WARNING,
-+ "Failed to release state for %s: %s",
-+ pathbuf, strerror(-ret));
-+ }
-+#endif
-+out:
- nfs_freeaddrinfo(ai);
- }
-
-diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
-index 3737ee81..b5e0c63a 100644
---- a/utils/exportfs/exportfs.man
-+++ b/utils/exportfs/exportfs.man
-@@ -256,6 +256,23 @@ pair. This deletes the specified entry from
- .I /var/lib/nfs/etab
- and removes the corresponding kernel entry (if any).
- .PP
-+When the last client for a given export path is unexported,
-+.B exportfs
-+signals the kernel to revoke NFSv4 state (opens, locks, and
-+delegations) and release cached state for that path.
-+Without this revocation, retained state would prevent the
-+underlying filesystem from being unmounted.
-+Affected clients receive
-+.B NFS4ERR_ADMIN_REVOKED
-+errors for operations that use revoked state.
-+.PP
-+.B "exportfs \-ua"
-+does not revoke NFSv4 state, however.
-+If
-+.B nfsd
-+is then restarted, clients may reclaim state during the
-+grace period.
-+.PP
- .SS Dumping the Export Table
- Invoking
- .B exportfs
-diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
-index 016dd2eb..c7126748 100644
---- a/utils/nfsdctl/nfsdctl.c
-+++ b/utils/nfsdctl/nfsdctl.c
-@@ -26,6 +26,7 @@
- #include <netlink/msg.h>
- #include <netlink/attr.h>
- #include <linux/netlink.h>
-+#include "compat.h"
-
- #include <readline/readline.h>
- #include <readline/history.h>
diff --git a/nfs-utils-2.9.2-rc4.patch b/nfs-utils-2.9.2-rc4.patch
new file mode 100644
index 0000000..f9dcb51
--- /dev/null
+++ b/nfs-utils-2.9.2-rc4.patch
@@ -0,0 +1,1137 @@
+diff --git a/configure.ac b/configure.ac
+index 115c611f..8ca06fd6 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -256,6 +256,8 @@ PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1)
+ PKG_CHECK_MODULES(LIBNLGENL3, libnl-genl-3.0 >= 3.1)
+
+ AC_CHECK_HEADERS(linux/nfsd_netlink.h)
++AC_DEFINE([HAVE_NFSD_NETLINK], 1,
++ [Define to 1 if nfsd generic netlink support is available])
+
+ # ensure we have the expkey attributes
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]],
+diff --git a/support/export/cache.c b/support/export/cache.c
+index 2f128d7d..65008f51 100644
+--- a/support/export/cache.c
++++ b/support/export/cache.c
+@@ -40,7 +40,7 @@
+ #include <netlink/genl/ctrl.h>
+ #include <netlink/msg.h>
+ #include <netlink/attr.h>
+-#include <linux/netlink.h>
++#include "compat.h"
+
+ #ifdef USE_SYSTEM_NFSD_NETLINK_H
+ #include <linux/nfsd_netlink.h>
+diff --git a/support/export/cache_flush.c b/support/export/cache_flush.c
+index ed7b964f..2a24dec7 100644
+--- a/support/export/cache_flush.c
++++ b/support/export/cache_flush.c
+@@ -38,6 +38,8 @@ extern int no_netlink;
+ #include "sunrpc_netlink.h"
+ #endif
+
++#include "compat.h"
++
+ static int nl_send_flush(struct nl_sock *sock, int family, int cmd)
+ {
+ struct nl_msg *msg;
+diff --git a/support/export/xtab.c b/support/export/xtab.c
+index 282f15bc..0a966051 100644
+--- a/support/export/xtab.c
++++ b/support/export/xtab.c
+@@ -33,11 +33,8 @@ int v4root_needed;
+ static void cond_rename(char *newfile, char *oldfile);
+
+ static int
+-xtab_read(char *xtab, char *lockfn, int is_export)
++xtab_read(char *xtab, char *lockfn)
+ {
+- /* is_export == 0 => reading /proc/fs/nfs/exports - we know these things are exported to kernel
+- * is_export == 1 => reading /var/lib/nfs/etab - these things are allowed to be exported
+- */
+ struct exportent *xp;
+ nfs_export *exp;
+ int lockid;
+@@ -45,11 +42,10 @@ xtab_read(char *xtab, char *lockfn, int is_export)
+ if ((lockid = xflock(lockfn, "r")) < 0)
+ return 0;
+ setexportent(xtab, "r");
+- if (is_export == 1)
+- v4root_needed = 1;
+- while ((xp = getexportent(is_export==0)) != NULL) {
+- if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) &&
+- !(exp = export_create(xp, is_export!=1))) {
++ v4root_needed = 1;
++ while ((xp = getexportent(0)) != NULL) {
++ if (!(exp = export_lookup(xp->e_hostname, xp->e_path, 0)) &&
++ !(exp = export_create(xp, 0))) {
+ if(xp->e_hostname) {
+ free(xp->e_hostname);
+ xp->e_hostname=NULL;
+@@ -60,17 +56,10 @@ xtab_read(char *xtab, char *lockfn, int is_export)
+ }
+ continue;
+ }
+- switch (is_export) {
+- case 0:
+- exp->m_exported = 1;
+- break;
+- case 1:
+- exp->m_xtabent = 1;
+- exp->m_mayexport = 1;
+- if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
+- v4root_needed = 0;
+- break;
+- }
++ exp->m_xtabent = 1;
++ exp->m_mayexport = 1;
++ if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0)
++ v4root_needed = 0;
+ if(xp->e_hostname) {
+ free(xp->e_hostname);
+ xp->e_hostname=NULL;
+@@ -90,7 +79,7 @@ xtab_read(char *xtab, char *lockfn, int is_export)
+ int
+ xtab_export_read(void)
+ {
+- return xtab_read(etab.statefn, etab.lockfn, 1);
++ return xtab_read(etab.statefn, etab.lockfn);
+ }
+
+ /*
+@@ -100,7 +89,7 @@ xtab_export_read(void)
+ * fix the auth_reload logic as well...
+ */
+ static int
+-xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
++xtab_write(char *xtab, char *xtabtmp, char *lockfn)
+ {
+ struct exportent xe;
+ nfs_export *exp;
+@@ -114,9 +103,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
+
+ for (i = 0; i < MCL_MAXTYPES; i++) {
+ for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
+- if (is_export && !exp->m_xtabent)
+- continue;
+- if (!is_export && ! exp->m_exported)
++ if (!exp->m_xtabent)
+ continue;
+
+ /* write out the export entry using the FQDN */
+@@ -137,7 +124,7 @@ xtab_write(char *xtab, char *xtabtmp, char *lockfn, int is_export)
+ int
+ xtab_export_write(void)
+ {
+- return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn, 1);
++ return xtab_write(etab.statefn, etab.tmpfn, etab.lockfn);
+ }
+
+ /*
+diff --git a/support/include/compat.h b/support/include/compat.h
+new file mode 100644
+index 00000000..83229b65
+--- /dev/null
++++ b/support/include/compat.h
+@@ -0,0 +1,10 @@
++#ifndef COMPAT_H
++#define COMPAT_H
++
++#include <linux/netlink.h>
++
++#ifndef NETLINK_EXT_ACK
++#define NETLINK_EXT_ACK 11
++#endif
++
++#endif /* COMPAT_H */
+diff --git a/support/include/nfsd_netlink.h b/support/include/nfsd_netlink.h
+index 2d708d24..3d076d17 100644
+--- a/support/include/nfsd_netlink.h
++++ b/support/include/nfsd_netlink.h
+@@ -151,15 +151,6 @@ enum {
+ NFSD_A_AUTH_FLAVOR_MAX = (__NFSD_A_AUTH_FLAVOR_MAX - 1)
+ };
+
+-enum {
+- NFSD_A_SVC_EXPORT_REQ_SEQNO = 1,
+- NFSD_A_SVC_EXPORT_REQ_CLIENT,
+- NFSD_A_SVC_EXPORT_REQ_PATH,
+-
+- __NFSD_A_SVC_EXPORT_REQ_MAX,
+- NFSD_A_SVC_EXPORT_REQ_MAX = (__NFSD_A_SVC_EXPORT_REQ_MAX - 1)
+-};
+-
+ enum {
+ NFSD_A_SVC_EXPORT_SEQNO = 1,
+ NFSD_A_SVC_EXPORT_CLIENT,
+@@ -213,6 +204,61 @@ enum {
+ NFSD_A_CACHE_FLUSH_MAX = (__NFSD_A_CACHE_FLUSH_MAX - 1)
+ };
+
++enum {
++ NFSD_A_UNLOCK_IP_ADDRESS = 1,
++
++ __NFSD_A_UNLOCK_IP_MAX,
++ NFSD_A_UNLOCK_IP_MAX = (__NFSD_A_UNLOCK_IP_MAX - 1)
++};
++
++enum {
++ NFSD_A_UNLOCK_FILESYSTEM_PATH = 1,
++
++ __NFSD_A_UNLOCK_FILESYSTEM_MAX,
++ NFSD_A_UNLOCK_FILESYSTEM_MAX = (__NFSD_A_UNLOCK_FILESYSTEM_MAX - 1)
++};
++
++enum {
++ NFSD_A_UNLOCK_EXPORT_PATH = 1,
++
++ __NFSD_A_UNLOCK_EXPORT_MAX,
++ NFSD_A_UNLOCK_EXPORT_MAX = (__NFSD_A_UNLOCK_EXPORT_MAX - 1)
++};
++
++enum {
++ NFSD_A_SERVER_PROC_ENTRY_OP = 1,
++ NFSD_A_SERVER_PROC_ENTRY_COUNT,
++ NFSD_A_SERVER_PROC_ENTRY_PAD,
++
++ __NFSD_A_SERVER_PROC_ENTRY_MAX,
++ NFSD_A_SERVER_PROC_ENTRY_MAX = (__NFSD_A_SERVER_PROC_ENTRY_MAX - 1)
++};
++
++enum {
++ NFSD_A_SERVER_STATS_RC_HITS = 1,
++ NFSD_A_SERVER_STATS_RC_MISSES,
++ NFSD_A_SERVER_STATS_RC_NOCACHE,
++ NFSD_A_SERVER_STATS_PAD,
++ NFSD_A_SERVER_STATS_FH_STALE,
++ NFSD_A_SERVER_STATS_IO_READ,
++ NFSD_A_SERVER_STATS_IO_WRITE,
++ NFSD_A_SERVER_STATS_NETCNT,
++ NFSD_A_SERVER_STATS_NETUDPCNT,
++ NFSD_A_SERVER_STATS_NETTCPCNT,
++ NFSD_A_SERVER_STATS_NETTCPCONN,
++ NFSD_A_SERVER_STATS_RPCCNT,
++ NFSD_A_SERVER_STATS_RPCBADFMT,
++ NFSD_A_SERVER_STATS_RPCBADAUTH,
++ NFSD_A_SERVER_STATS_RPCBADCLNT,
++ NFSD_A_SERVER_STATS_PROC2_OPS,
++ NFSD_A_SERVER_STATS_PROC3_OPS,
++ NFSD_A_SERVER_STATS_PROC4_OPS,
++ NFSD_A_SERVER_STATS_PROC4OPS_OPS,
++
++ __NFSD_A_SERVER_STATS_MAX,
++ NFSD_A_SERVER_STATS_MAX = (__NFSD_A_SERVER_STATS_MAX - 1)
++};
++
+ enum {
+ NFSD_CMD_RPC_STATUS_GET = 1,
+ NFSD_CMD_THREADS_SET,
+@@ -229,6 +275,10 @@ enum {
+ NFSD_CMD_EXPKEY_GET_REQS,
+ NFSD_CMD_EXPKEY_SET_REQS,
+ NFSD_CMD_CACHE_FLUSH,
++ NFSD_CMD_UNLOCK_IP,
++ NFSD_CMD_UNLOCK_FILESYSTEM,
++ NFSD_CMD_UNLOCK_EXPORT,
++ NFSD_CMD_SERVER_STATS_GET,
+
+ __NFSD_CMD_MAX,
+ NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)
+diff --git a/support/include/nfsdnl.h b/support/include/nfsdnl.h
+new file mode 100644
+index 00000000..352801e5
+--- /dev/null
++++ b/support/include/nfsdnl.h
+@@ -0,0 +1,34 @@
++/*
++ * Helper for sending nfsd generic netlink commands.
++ *
++ * Used by both nfsdctl and exportfs.
++ */
++
++#ifndef NFS_UTILS_NFSDNL_H
++#define NFS_UTILS_NFSDNL_H
++
++#ifdef HAVE_NFSD_NETLINK
++
++/**
++ * nfsd_nl_cmd_str - send an nfsd netlink command carrying a string attribute
++ * @cmd: NFSD_CMD_* command number
++ * @attr: NFSD_A_* attribute number
++ * @value: NUL-terminated string value for the attribute
++ *
++ * Opens a genetlink connection, resolves the "nfsd" family, sends a
++ * single "do" command with one string attribute, waits for the ACK,
++ * and cleans up.
++ *
++ * Returns 0 on success or a negative errno on failure.
++ */
++int nfsd_nl_cmd_str(int cmd, int attr, const char *value);
++
++#else
++
++static inline int nfsd_nl_cmd_str(int cmd, int attr, const char *value)
++{
++ return -ENOSYS;
++}
++
++#endif /* HAVE_NFSD_NETLINK */
++#endif /* NFS_UTILS_NFSDNL_H */
+diff --git a/support/nfs/Makefile.am b/support/nfs/Makefile.am
+index 5bfd71a9..64ad5d07 100644
+--- a/support/nfs/Makefile.am
++++ b/support/nfs/Makefile.am
+@@ -11,6 +11,13 @@ libnfs_la_SOURCES = exports.c rmtab.c xio.c rpcmisc.c rpcdispatch.c \
+ libnfs_la_LIBADD = libnfsconf.la -luuid
+ libnfs_la_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
+
++if CONFIG_NFSDCTL
++libnfs_la_SOURCES += nfsdnl.c
++libnfs_la_CPPFLAGS += $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS) \
++ -I$(top_srcdir)/utils/nfsdctl
++libnfs_la_LIBADD += $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS)
++endif
++
+ libnfsconf_la_SOURCES = conffile.c xlog.c
+
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/support/nfs/fh_key_file.c b/support/nfs/fh_key_file.c
+index 5f5eafc1..81ea1500 100644
+--- a/support/nfs/fh_key_file.c
++++ b/support/nfs/fh_key_file.c
+@@ -26,6 +26,7 @@
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <errno.h>
++#include <string.h>
+ #include <uuid/uuid.h>
+
+ #include "nfslib.h"
+diff --git a/support/nfs/getport.c b/support/nfs/getport.c
+index 813f7bf9..608e185b 100644
+--- a/support/nfs/getport.c
++++ b/support/nfs/getport.c
+@@ -452,11 +452,12 @@ char *nfs_sockaddr2universal(const struct sockaddr *sap)
+ uint16_t port;
+ size_t count;
+ char *result;
+- int len;
++ int len = sizeof(struct sockaddr);
+
+ switch (sap->sa_family) {
+ case AF_LOCAL:
+- return strndup(sun->sun_path, sizeof(sun->sun_path));
++ size_t path_len = len - offsetof(struct sockaddr_un, sun_path);
++ return strndup(sun->sun_path, path_len);
+ case AF_INET:
+ if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr,
+ buf, (socklen_t)sizeof(buf)) == NULL)
+diff --git a/support/nfs/nfsdnl.c b/support/nfs/nfsdnl.c
+new file mode 100644
+index 00000000..ded035b9
+--- /dev/null
++++ b/support/nfs/nfsdnl.c
+@@ -0,0 +1,125 @@
++/*
++ * nfsdnl.c -- send nfsd generic netlink commands
++ *
++ * Helper shared by nfsdctl and exportfs.
++ */
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <errno.h>
++#include <string.h>
++
++#include <netlink/genl/genl.h>
++#include <netlink/genl/ctrl.h>
++#include <netlink/msg.h>
++#include <netlink/attr.h>
++
++#include "nfslib.h"
++#include "xlog.h"
++#include "nfsdnl.h"
++
++#ifdef USE_SYSTEM_NFSD_NETLINK_H
++#include <linux/nfsd_netlink.h>
++#else
++#include "nfsd_netlink.h"
++#endif
++
++#define NFSDNL_BUFSIZE (4096)
++
++static int error_handler(struct sockaddr_nl *UNUSED(nla), struct nlmsgerr *err,
++ void *arg)
++{
++ int *ret = arg;
++ *ret = err->error;
++ return NL_STOP;
++}
++
++static int finish_handler(struct nl_msg *UNUSED(msg), void *arg)
++{
++ int *ret = arg;
++ *ret = 0;
++ return NL_SKIP;
++}
++
++static int ack_handler(struct nl_msg *UNUSED(msg),
++ void *arg)
++{
++ int *ret = arg;
++ *ret = 0;
++ return NL_STOP;
++}
++
++/**
++ * nfsd_nl_cmd_str - send an nfsd netlink command carrying a string attribute
++ * @cmd: NFSD_CMD_* command number
++ * @attr: NFSD_A_* attribute number
++ * @value: NUL-terminated string value for the attribute
++ *
++ * Returns 0 on success or a negative errno on failure.
++ */
++int nfsd_nl_cmd_str(int cmd, int attr, const char *value)
++{
++ struct genlmsghdr *ghdr;
++ struct nl_sock *sock;
++ struct nl_msg *msg;
++ struct nl_cb *cb;
++ int family;
++ int ret;
++
++ sock = nl_socket_alloc();
++ if (!sock)
++ return -ENOMEM;
++ if (genl_connect(sock)) {
++ ret = -ECONNREFUSED;
++ goto out_sock;
++ }
++ nl_socket_set_buffer_size(sock, NFSDNL_BUFSIZE, NFSDNL_BUFSIZE);
++
++ family = genl_ctrl_resolve(sock, NFSD_FAMILY_NAME);
++ if (family < 0) {
++ ret = family;
++ goto out_sock;
++ }
++
++ msg = nlmsg_alloc();
++ if (!msg) {
++ ret = -ENOMEM;
++ goto out_sock;
++ }
++ if (!genlmsg_put(msg, 0, 0, family, 0, 0, 0, 0)) {
++ ret = -ENOMEM;
++ goto out_msg;
++ }
++
++ ghdr = nlmsg_data(nlmsg_hdr(msg));
++ ghdr->cmd = (__u8)cmd;
++ nla_put_string(msg, attr, value);
++
++ cb = nl_cb_alloc(NL_CB_CUSTOM);
++ if (!cb) {
++ ret = -ENOMEM;
++ goto out_msg;
++ }
++
++ ret = nl_send_auto(sock, msg);
++ if (ret < 0)
++ goto out_cb;
++
++ ret = 1;
++ nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &ret);
++ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &ret);
++ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &ret);
++
++ while (ret > 0)
++ nl_recvmsgs(sock, cb);
++
++out_cb:
++ nl_cb_put(cb);
++out_msg:
++ nlmsg_free(msg);
++out_sock:
++ nl_socket_free(sock);
++ return ret;
++}
+diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c
+index e1475879..5377a0ee 100644
+--- a/support/nfsidmap/libnfsidmap.c
++++ b/support/nfsidmap/libnfsidmap.c
+@@ -409,15 +409,14 @@ int nfs4_init_name_mapping(char *conffile)
+ if (idmap_verbosity >= 1) {
+ struct conf_list_node *r;
+ char *buf = NULL;
+- int siz=0;
++ size_t siz = 1;
+
+ if (local_realms) {
+ TAILQ_FOREACH(r, &local_realms->fields, link) {
+ siz += (strlen(r->field)+4);
+ }
+- buf = malloc(siz);
++ buf = calloc(1, siz);
+ if (buf) {
+- *buf = 0;
+ TAILQ_FOREACH(r, &local_realms->fields, link) {
+ sprintf(buf+strlen(buf), "'%s' ", r->field);
+ }
+diff --git a/support/reexport/backend_sqlite.c b/support/reexport/backend_sqlite.c
+index 0eb5ea37..a1e981e4 100644
+--- a/support/reexport/backend_sqlite.c
++++ b/support/reexport/backend_sqlite.c
+@@ -9,6 +9,8 @@
+ #include <string.h>
+ #include <unistd.h>
+
++#include <sys/syscall.h>
++
+ #ifdef HAVE_GETRANDOM
+ # include <sys/random.h>
+ # if !defined(SYS_getrandom) && defined(__NR_getrandom)
+diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py
+index 69d24a11..af04aac5 100755
+--- a/tools/nfs-iostat/nfs-iostat.py
++++ b/tools/nfs-iostat/nfs-iostat.py
+@@ -327,7 +327,7 @@ class DeviceData:
+ print()
+ print('%d congestion waits' % congestionwaits)
+
+- def __print_rpc_op_stats(self, op, sample_time):
++ def __print_rpc_op_stats(self, op, sample_time, use_mb=False):
+ """Print generic stats for one RPC op
+ """
+ if op not in self.__rpc_data:
+@@ -343,9 +343,19 @@ class DeviceData:
+ if len(rpc_stats) >= 9:
+ errs = float(rpc_stats[8])
+
++ # scale to MB if requested
++ if use_mb:
++ throughput = kilobytes / 1024.0
++ throughput_label = 'MB/s'
++ per_op_label = 'MB/op'
++ else:
++ throughput = kilobytes
++ throughput_label = 'kB/s'
++ per_op_label = 'kB/op'
++
+ # prevent floating point exceptions
+ if ops != 0:
+- kb_per_op = kilobytes / ops
++ unit_per_op = throughput / ops
+ retrans_percent = (retrans * 100) / ops
+ rtt_per_op = rtt / ops
+ exe_per_op = exe / ops
+@@ -353,7 +363,7 @@ class DeviceData:
+ if len(rpc_stats) >= 9:
+ errs_percent = (errs * 100) / ops
+ else:
+- kb_per_op = 0.0
++ unit_per_op = 0.0
+ retrans_percent = 0.0
+ rtt_per_op = 0.0
+ exe_per_op = 0.0
+@@ -364,8 +374,8 @@ class DeviceData:
+ op += ':'
+ print(format(op.lower(), '<16s'), end='')
+ print(format('ops/s', '>8s'), end='')
+- print(format('kB/s', '>16s'), end='')
+- print(format('kB/op', '>16s'), end='')
++ print(format(throughput_label, '>16s'), end='')
++ print(format(per_op_label, '>16s'), end='')
+ print(format('retrans', '>16s'), end='')
+ print(format('avg RTT (ms)', '>16s'), end='')
+ print(format('avg exe (ms)', '>16s'), end='')
+@@ -375,8 +385,8 @@ class DeviceData:
+ print()
+
+ print(format((ops / sample_time), '>24.3f'), end='')
+- print(format((kilobytes / sample_time), '>16.3f'), end='')
+- print(format(kb_per_op, '>16.3f'), end='')
++ print(format((throughput / sample_time), '>16.3f'), end='')
++ print(format(unit_per_op, '>16.3f'), end='')
+ retransmits = '{0:>10.0f} ({1:>3.1f}%)'.format(retrans, retrans_percent).strip()
+ print(format(retransmits, '>16'), end='')
+ print(format(rtt_per_op, '>16.3f'), end='')
+@@ -395,9 +405,11 @@ class DeviceData:
+ sample_time = 1;
+ return (sends / sample_time)
+
+- def display_iostats(self, sample_time, which):
++ def display_iostats(self, sample_time, options):
+ """Display NFS and RPC stats in an iostat-like way
+ """
++ which = options.which
++ use_mb = options.megabytes
+ sends = float(self.__rpc_data['rpcsends'])
+ if sample_time == 0:
+ sample_time = float(self.__nfs_data['age'])
+@@ -423,21 +435,21 @@ class DeviceData:
+ print()
+
+ if which == 0:
+- self.__print_rpc_op_stats('READ', sample_time)
+- self.__print_rpc_op_stats('WRITE', sample_time)
++ self.__print_rpc_op_stats('READ', sample_time, use_mb)
++ self.__print_rpc_op_stats('WRITE', sample_time, use_mb)
+ elif which == 1:
+- self.__print_rpc_op_stats('GETATTR', sample_time)
+- self.__print_rpc_op_stats('ACCESS', sample_time)
++ self.__print_rpc_op_stats('GETATTR', sample_time, use_mb)
++ self.__print_rpc_op_stats('ACCESS', sample_time, use_mb)
+ self.__print_attr_cache_stats(sample_time)
+ elif which == 2:
+- self.__print_rpc_op_stats('LOOKUP', sample_time)
+- self.__print_rpc_op_stats('READDIR', sample_time)
++ self.__print_rpc_op_stats('LOOKUP', sample_time, use_mb)
++ self.__print_rpc_op_stats('READDIR', sample_time, use_mb)
+ if 'READDIRPLUS' in self.__rpc_data:
+- self.__print_rpc_op_stats('READDIRPLUS', sample_time)
++ self.__print_rpc_op_stats('READDIRPLUS', sample_time, use_mb)
+ self.__print_dir_cache_stats(sample_time)
+ elif which == 3:
+- self.__print_rpc_op_stats('READ', sample_time)
+- self.__print_rpc_op_stats('WRITE', sample_time)
++ self.__print_rpc_op_stats('READ', sample_time, use_mb)
++ self.__print_rpc_op_stats('WRITE', sample_time, use_mb)
+ self.__print_page_stats(sample_time)
+
+ sys.stdout.flush()
+@@ -500,7 +512,7 @@ def print_iostat_summary(old, new, devices, time, options):
+
+ count = 1
+ for device in devices:
+- display_stats[device].display_iostats(time, options.which)
++ display_stats[device].display_iostats(time, options)
+
+ count += 1
+ if (count > options.list):
+@@ -585,6 +597,11 @@ client are listed.
+ type="int",
+ dest="list",
+ help="only print stats for first LIST mount points")
++ displaygroup.add_option('-m', '--megabytes',
++ action="store_true",
++ dest="megabytes",
++ default=False,
++ help="display throughput in megabytes per second (MB/s) instead of kilobytes per second (kB/s)")
+ parser.add_option_group(displaygroup)
+
+ (options, args) = parser.parse_args(sys.argv)
+diff --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man
+index 104c7ab4..4f24318d 100644
+--- a/tools/nfs-iostat/nfsiostat.man
++++ b/tools/nfs-iostat/nfsiostat.man
+@@ -56,16 +56,16 @@ This is the length of the backlog queue.
+ .RE
+ .RE
+ .RS 8
+-- \fBkB/s\fR
++- \fBkB/s (MB/s)\fR
+ .RS
+-This is the number of kB written/read per second.
++This is the number of kB (or MB) written/read per second.
+ .RE
+ .RE
+ .RE
+ .RS 8
+-- \fBkB/op\fR
++- \fBkB/op (MB/op)\fR
+ .RS
+-This is the number of kB written/read per each operation.
++This is the number of kB (or MB) written/read per each operation.
+ .RE
+ .RE
+ .RE
+@@ -122,6 +122,9 @@ shows help message and exit
+ .B \-l LIST or " \-\-list=LIST
+ only print stats for first LIST mount points
+ .TP
++.B \-m or " \-\-megabytes
++display throughput in megabytes per second
++.TP
+ .B \-p " or " \-\-page
+ displays statistics related to the page cache
+ .TP
+diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
+index 93f0bcd7..768d2db7 100644
+--- a/utils/exportfs/exportfs.c
++++ b/utils/exportfs/exportfs.c
+@@ -39,6 +39,15 @@
+ #include "xlog.h"
+ #include "conffile.h"
+ #include "reexport.h"
++#include "nfsdnl.h"
++
++#ifdef HAVE_NFSD_NETLINK
++#ifdef USE_SYSTEM_NFSD_NETLINK_H
++#include <linux/nfsd_netlink.h>
++#else
++#include "nfsd_netlink.h"
++#endif
++#endif
+
+ #include <netlink/genl/genl.h>
+ #include <netlink/genl/ctrl.h>
+@@ -63,6 +72,7 @@ static void release_lockfile(void);
+
+ static const char *lockfile = EXP_LOCKFILE;
+ static int _lockfd = -1;
++static int f_unexport_all;
+
+ /*
+ * If we aren't careful, changes made by exportfs can be lost
+@@ -246,7 +256,8 @@ main(int argc, char **argv)
+ * don't care about what should be exported, as that
+ * may require DNS lookups..
+ */
+- if (! ( !f_export && f_all)) {
++ f_unexport_all = !f_export && f_all;
++ if (!f_unexport_all) {
+ /* note: xtab_*_read does not update entries if they already exist,
+ * so this will not lose new options
+ */
+@@ -380,6 +391,26 @@ exportfs(char *arg, char *options, int verbose)
+ xlog(L_ERROR, "Invalid export syntax: %s", arg);
+ }
+
++/*
++ * Check whether any active export remains for the given path across
++ * all client types. Returns true if at least one export still has
++ * m_xtabent set.
++ */
++static int
++path_still_exported(const char *path, size_t nlen)
++{
++ nfs_export *exp;
++ int i;
++
++ for (i = 0; i < MCL_MAXTYPES; i++)
++ for (exp = exportlist[i].p_head; exp; exp = exp->m_next)
++ if (exp->m_xtabent &&
++ strlen(exp->m_export.e_path) == nlen &&
++ strncmp(path, exp->m_export.e_path, nlen) == 0)
++ return 1;
++ return 0;
++}
++
+ static void
+ unexportfs_parsed(char *hname, char *path, int verbose)
+ {
+@@ -434,9 +465,39 @@ unexportfs_parsed(char *hname, char *path, int verbose)
+ exp->m_mayexport = 0;
+ success = 1;
+ }
+- if (!success)
++ if (!success) {
+ xlog(L_ERROR, "Could not find '%s:%s' to unexport.", hname, path);
++ goto out;
++ }
+
++ /*
++ * If no exports remain for this path, ask the kernel to
++ * revoke any NFSv4 state and close cached file handles
++ * associated with exports of this path. This enables the
++ * underlying filesystem to be unmounted.
++ *
++ * Skip this during "exportfs -ua" -- that is a shutdown
++ * operation. Clients should wait for nfsd to restart and
++ * reclaim state through the grace period rather than
++ * receiving NFS4ERR_ADMIN_REVOKED.
++ */
++#ifdef HAVE_NFSD_NETLINK
++ if (!f_unexport_all && !path_still_exported(path, nlen)) {
++ char pathbuf[NFS_MAXPATHLEN + 1];
++ int ret;
++
++ memcpy(pathbuf, path, nlen);
++ pathbuf[nlen] = '\0';
++ ret = nfsd_nl_cmd_str(NFSD_CMD_UNLOCK_EXPORT,
++ NFSD_A_UNLOCK_EXPORT_PATH,
++ pathbuf);
++ if (ret && ret != -ENOSYS)
++ xlog(L_WARNING,
++ "Failed to release state for %s: %s",
++ pathbuf, strerror(-ret));
++ }
++#endif
++out:
+ nfs_freeaddrinfo(ai);
+ }
+
+diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
+index 3737ee81..b5e0c63a 100644
+--- a/utils/exportfs/exportfs.man
++++ b/utils/exportfs/exportfs.man
+@@ -256,6 +256,23 @@ pair. This deletes the specified entry from
+ .I /var/lib/nfs/etab
+ and removes the corresponding kernel entry (if any).
+ .PP
++When the last client for a given export path is unexported,
++.B exportfs
++signals the kernel to revoke NFSv4 state (opens, locks, and
++delegations) and release cached state for that path.
++Without this revocation, retained state would prevent the
++underlying filesystem from being unmounted.
++Affected clients receive
++.B NFS4ERR_ADMIN_REVOKED
++errors for operations that use revoked state.
++.PP
++.B "exportfs \-ua"
++does not revoke NFSv4 state, however.
++If
++.B nfsd
++is then restarted, clients may reclaim state during the
++grace period.
++.PP
+ .SS Dumping the Export Table
+ Invoking
+ .B exportfs
+diff --git a/utils/nfsdctl/nfsdctl.c b/utils/nfsdctl/nfsdctl.c
+index 016dd2eb..c7126748 100644
+--- a/utils/nfsdctl/nfsdctl.c
++++ b/utils/nfsdctl/nfsdctl.c
+@@ -26,6 +26,7 @@
+ #include <netlink/msg.h>
+ #include <netlink/attr.h>
+ #include <linux/netlink.h>
++#include "compat.h"
+
+ #include <readline/readline.h>
+ #include <readline/history.h>
+diff --git a/utils/nfsstat/Makefile.am b/utils/nfsstat/Makefile.am
+index d1555a7d..8b121c48 100644
+--- a/utils/nfsstat/Makefile.am
++++ b/utils/nfsstat/Makefile.am
+@@ -5,8 +5,10 @@ EXTRA_DIST = $(man8_MANS)
+
+ sbin_PROGRAMS = nfsstat
+ nfsstat_SOURCES = nfsstat.c
++nfsstat_CFLAGS = $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS)
+ nfsstat_LDADD = ../../support/export/libexport.a \
+ ../../support/nfs/libnfs.la \
+- ../../support/misc/libmisc.a
++ ../../support/misc/libmisc.a \
++ $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS)
+
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
+index ca845325..f09e1d6a 100644
+--- a/utils/nfsstat/nfsstat.c
++++ b/utils/nfsstat/nfsstat.c
+@@ -23,6 +23,17 @@
+ #include <signal.h>
+ #include <time.h>
+
++#include <netlink/genl/genl.h>
++#include <netlink/genl/ctrl.h>
++#include <netlink/msg.h>
++#include <netlink/attr.h>
++
++#ifdef USE_SYSTEM_NFSD_NETLINK_H
++#include <linux/nfsd_netlink.h>
++#else
++#include "nfsd_netlink.h"
++#endif
++
+ #define MAXNRVALS 32
+
+ enum {
+@@ -271,6 +282,7 @@ static statinfo *get_stat_info(const char *, struct statinfo *);
+
+ static int mounts(const char *);
+
++static int get_stats_netlink(struct statinfo *);
+ static void get_stats(const char *, struct statinfo *, int *, int,
+ int);
+ static int has_stats(const unsigned int *, int);
+@@ -1051,6 +1063,272 @@ mounts(const char *name)
+ return 1;
+ }
+
++/*
++ * Netlink helpers for fetching server stats via Generic Netlink.
++ */
++static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
++ void *arg)
++{
++ int *ret = arg;
++
++ *ret = err->error;
++ return NL_SKIP;
++}
++
++static int nl_finish_handler(struct nl_msg *msg, void *arg)
++{
++ int *ret = arg;
++
++ *ret = 0;
++ return NL_SKIP;
++}
++
++static int nl_ack_handler(struct nl_msg *msg, void *arg)
++{
++ int *ret = arg;
++
++ *ret = 0;
++ return NL_STOP;
++}
++
++static void parse_one_proc_entry(struct nlattr *nest, unsigned int *info,
++ unsigned int max_ops)
++{
++ struct nlattr *tb[NFSD_A_SERVER_PROC_ENTRY_MAX + 1];
++ unsigned int op, count;
++
++ nla_parse_nested(tb, NFSD_A_SERVER_PROC_ENTRY_MAX, nest, NULL);
++ if (!tb[NFSD_A_SERVER_PROC_ENTRY_OP] ||
++ !tb[NFSD_A_SERVER_PROC_ENTRY_COUNT])
++ return;
++
++ op = nla_get_u32(tb[NFSD_A_SERVER_PROC_ENTRY_OP]);
++ count = (unsigned int)nla_get_u64(tb[NFSD_A_SERVER_PROC_ENTRY_COUNT]);
++ if (op < max_ops) {
++ info[0] = max_ops;
++ info[op + 1] = count;
++ }
++}
++
++static int stats_nl_handler(struct nl_msg *msg, void *arg)
++{
++ struct statinfo *info = arg;
++ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++ struct nlattr *attr;
++ statinfo *si;
++ int rem;
++
++ nla_for_each_attr(attr, genlmsg_attrdata(gnlh, 0),
++ genlmsg_attrlen(gnlh, 0), rem) {
++ int type = nla_type(attr);
++
++ switch (type) {
++ /* Reply cache */
++ case NFSD_A_SERVER_STATS_RC_HITS:
++ si = get_stat_info("rc", info);
++ if (si)
++ si->valptr[0] = nla_get_u64(attr);
++ break;
++ case NFSD_A_SERVER_STATS_RC_MISSES:
++ si = get_stat_info("rc", info);
++ if (si)
++ si->valptr[1] = nla_get_u64(attr);
++ break;
++ case NFSD_A_SERVER_STATS_RC_NOCACHE:
++ si = get_stat_info("rc", info);
++ if (si)
++ si->valptr[2] = nla_get_u64(attr);
++ break;
++
++ /* Filehandle */
++ case NFSD_A_SERVER_STATS_FH_STALE:
++ si = get_stat_info("fh", info);
++ if (si)
++ si->valptr[0] = nla_get_u64(attr);
++ break;
++
++ /* IO */
++ case NFSD_A_SERVER_STATS_IO_READ:
++ si = get_stat_info("io", info);
++ if (si)
++ si->valptr[0] = nla_get_u64(attr);
++ break;
++ case NFSD_A_SERVER_STATS_IO_WRITE:
++ si = get_stat_info("io", info);
++ if (si)
++ si->valptr[1] = nla_get_u64(attr);
++ break;
++
++ /* Network */
++ case NFSD_A_SERVER_STATS_NETCNT:
++ si = get_stat_info("net", info);
++ if (si)
++ si->valptr[0] = nla_get_u32(attr);
++ break;
++ case NFSD_A_SERVER_STATS_NETUDPCNT:
++ si = get_stat_info("net", info);
++ if (si)
++ si->valptr[1] = nla_get_u32(attr);
++ break;
++ case NFSD_A_SERVER_STATS_NETTCPCNT:
++ si = get_stat_info("net", info);
++ if (si)
++ si->valptr[2] = nla_get_u32(attr);
++ break;
++ case NFSD_A_SERVER_STATS_NETTCPCONN:
++ si = get_stat_info("net", info);
++ if (si)
++ si->valptr[3] = nla_get_u32(attr);
++ break;
++
++ /* RPC */
++ case NFSD_A_SERVER_STATS_RPCCNT:
++ si = get_stat_info("rpc", info);
++ if (si)
++ si->valptr[0] = nla_get_u32(attr);
++ break;
++ case NFSD_A_SERVER_STATS_RPCBADFMT:
++ si = get_stat_info("rpc", info);
++ if (si)
++ si->valptr[2] = nla_get_u32(attr);
++ break;
++ case NFSD_A_SERVER_STATS_RPCBADAUTH:
++ si = get_stat_info("rpc", info);
++ if (si)
++ si->valptr[3] = nla_get_u32(attr);
++ break;
++ case NFSD_A_SERVER_STATS_RPCBADCLNT:
++ si = get_stat_info("rpc", info);
++ if (si)
++ si->valptr[4] = nla_get_u32(attr);
++ break;
++
++ /* Per-version procedure counts (multi-attr) */
++ case NFSD_A_SERVER_STATS_PROC2_OPS:
++ si = get_stat_info("proc2", info);
++ if (si)
++ parse_one_proc_entry(attr, si->valptr,
++ SRVPROC2_SZ);
++ break;
++ case NFSD_A_SERVER_STATS_PROC3_OPS:
++ si = get_stat_info("proc3", info);
++ if (si)
++ parse_one_proc_entry(attr, si->valptr,
++ SRVPROC3_SZ);
++ break;
++ case NFSD_A_SERVER_STATS_PROC4_OPS:
++ si = get_stat_info("proc4", info);
++ if (si)
++ parse_one_proc_entry(attr, si->valptr,
++ SRVPROC4_SZ);
++ break;
++ case NFSD_A_SERVER_STATS_PROC4OPS_OPS:
++ si = get_stat_info("proc4ops", info);
++ if (si)
++ parse_one_proc_entry(attr, si->valptr,
++ SRVPROC4OPS_SZ);
++ break;
++ }
++ }
++
++ return NL_OK;
++}
++
++/*
++ * Fetch server stats via Generic Netlink.
++ * Returns 0 on success, -1 on failure.
++ */
++static int
++get_stats_netlink(struct statinfo *info)
++{
++ struct nl_sock *sock;
++ struct nl_msg *msg;
++ struct nl_cb *cb;
++ int family, ret;
++ statinfo *si;
++
++ sock = nl_socket_alloc();
++ if (!sock)
++ return -1;
++
++ if (genl_connect(sock)) {
++ nl_socket_free(sock);
++ return -1;
++ }
++
++ family = genl_ctrl_resolve(sock, NFSD_FAMILY_NAME);
++ if (family < 0) {
++ nl_socket_free(sock);
++ return -1;
++ }
++
++ msg = nlmsg_alloc();
++ if (!msg) {
++ nl_socket_free(sock);
++ return -1;
++ }
++
++ if (!genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0,
++ NLM_F_DUMP, NFSD_CMD_SERVER_STATS_GET, 0)) {
++ nlmsg_free(msg);
++ nl_socket_free(sock);
++ return -1;
++ }
++
++ cb = nl_cb_alloc(NL_CB_CUSTOM);
++ if (!cb) {
++ nlmsg_free(msg);
++ nl_socket_free(sock);
++ return -1;
++ }
++
++ ret = nl_send_auto(sock, msg);
++ if (ret < 0) {
++ nl_cb_put(cb);
++ nlmsg_free(msg);
++ nl_socket_free(sock);
++ return -1;
++ }
++
++ ret = 1;
++ nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &ret);
++ nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &ret);
++ nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &ret);
++ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, stats_nl_handler, info);
++
++ while (ret > 0)
++ nl_recvmsgs(sock, cb);
++
++ nl_cb_put(cb);
++ nlmsg_free(msg);
++ nl_socket_free(sock);
++
++ if (ret < 0)
++ return -1;
++
++ /*
++ * Compute derived fields. The proc file emits "rpc rpccnt
++ * badcalls badfmt badauth badclnt" where badcalls is the sum
++ * of badfmt+badauth+badclnt. The netlink interface sends the
++ * components individually, so recompute the sum here.
++ */
++ si = get_stat_info("rpc", info);
++ if (si)
++ si->valptr[1] = si->valptr[2] + si->valptr[3] + si->valptr[4];
++
++ /* Compute totals for each stat category */
++ for (si = info; si->tag; si++) {
++ unsigned int total = 0;
++ int i;
++
++ for (i = 0; i < si->nrvals - 1; i++)
++ total += si->valptr[i];
++ si->valptr[si->nrvals - 1] = total;
++ }
++
++ return 0;
++}
++
+ static void
+ get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
+ int is_srv)
+@@ -1060,6 +1338,10 @@ get_stats(const char *file, struct statinfo *info, int *opt, int other_opt,
+ int err = 1;
+ char *label = is_srv ? "Server" : "Client";
+
++ /* Try netlink first for server stats */
++ if (is_srv && get_stats_netlink(info) == 0)
++ return;
++
+ /* try to guess what type of stat file we're dealing with */
+ if ((fp = fopen(file, "r")) == NULL)
+ goto out;
diff --git a/nfs-utils.spec b/nfs-utils.spec
index d31d606..316e681 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.9.1
-Release: 3.rc3%{?dist}
+Release: 3.rc4%{?dist}
Epoch: 1
# group all 32bit related archs
@@ -15,7 +15,7 @@ Source3: 24-nfs-server.conf
Source4: 10-nfsv4.conf
Source5: 10-nfsv3.conf
-Patch001: nfs-utils-2.9.2-rc3.patch
+Patch001: nfs-utils-2.9.2-rc4.patch
Patch100: nfs-utils-1.2.1-statdpath-man.patch
Patch102: nfs-utils-1.2.5-idmap-errmsg.patch
@@ -476,6 +476,9 @@ rm -f %{_sysconfdir}/nfsmount.conf.d/10-nfsv4.conf
%{_mandir}/*/rpcctl.8.gz
%changelog
+* Sat May 30 2026 Steve Dickson <steved@redhat.com> 2.9.1-2-rc4
+- Updated to the latest RC release: nfs-utils-2-9-2-rc4
+
* Wed May 20 2026 Steve Dickson <steved@redhat.com> 2.9.1-3-rc3
- Add %%ghost to clean up 10-nfsv4.conf and 10-nfsv3.conf declarations
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-31 12:23 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-31 12:23 [rpms/nfs-utils] rawhide: Updated to the latest RC release: nfs-utils-2-9-2-rc4 Steve Dickson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox