[PATCH nfs-utils 16/17] exportfs: use netlink to probe kernel support, skip export_test

Jeff Layton
2026-03-16T15:17:10Z

Refactor can_test() to first try resolving the sunrpc genl family via
netlink. If successful, the kernel supports the new netlink-based cache
interfaces and we can skip the advisory export_test() probe -- mountd
and exportd will validate exports at cache-fill time via check_export().

can_test() now returns:
2 = netlink available (skip export_test)
1 = /proc available (keep export_test as before)
0 = neither available

Update validate_export() to check this return value and skip the
export_test() calls when netlink is available.

Link exportfs against libnl3/libnl-genl-3 (already a mandatory build
dependency for nfs-utils).

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
utils/exportfs/Makefile.am | 6 ++++--
utils/exportfs/exportfs.c | 42 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/utils/exportfs/Makefile.am b/utils/exportfs/Makefile.am
index 7f8ce9faf2b469c8560f5fbee40b81e3443eab78..8db5fdae1741e1c79639bbdfd5d63f8571363e63 100644
--- a/utils/exportfs/Makefile.am
+++ b/utils/exportfs/Makefile.am
@@ -11,8 +11,10 @@ exportfs_LDADD = ../../support/export/libexport.a \
../../support/nfs/libnfs.la \
../../support/misc/libmisc.a \
../../support/reexport/libreexport.a \
- $(LIBWRAP) $(LIBNSL) $(LIBPTHREAD)
+ $(LIBWRAP) $(LIBNSL) $(LIBPTHREAD) \
+ $(LIBNL3_LIBS) $(LIBNLGENL3_LIBS)

-exportfs_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport
+exportfs_CPPFLAGS = $(AM_CPPFLAGS) $(CPPFLAGS) -I$(top_srcdir)/support/reexport \
+ $(LIBNL3_CFLAGS) $(LIBNLGENL3_CFLAGS)

MAINTAINERCLEANFILES = Makefile.in
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 748c38e3e966703c7a870a6aa9589d32708edcd2..04753fa169f97c6b07893613197739ff36e0d09b 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -40,6 +40,15 @@
#include "conffile.h"
#include "reexport.h"

+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+
+#ifdef USE_SYSTEM_SUNRPC_NETLINK_H
+#include <linux/sunrpc_netlink.h>
+#else
+#include "sunrpc_netlink.h"
+#endif
+
static void export_all(int verbose);
static void exportfs(char *arg, char *options, int verbose);
static void unexportfs(char *arg, int verbose);
@@ -476,13 +485,34 @@ unexportfs(char *arg, int verbose)
xlog(L_ERROR, "Invalid export syntax: %s", arg);
}

+/* Return values:
+ * 2 = netlink available (skip export_test)
+ * 1 = /proc available (keep export_test)
+ * 0 = neither available
+ */
static int can_test(void)
{
+ struct nl_sock *sock;
+ int family;
char buf[1024] = { 0 };
int fd;
int n;
size_t bufsiz = sizeof(buf);

+ /* Try netlink first: resolve sunrpc genl family */
+ sock = nl_socket_alloc();
+ if (sock) {
+ if (genl_connect(sock) == 0) {
+ family = genl_ctrl_resolve(sock, SUNRPC_FAMILY_NAME);
+ nl_socket_free(sock);
+ if (family >= 0)
+ return 2;
+ } else {
+ nl_socket_free(sock);
+ }
+ }
+
+ /* Fallback: /proc probe */
fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
if (fd < 0)
return 0;
@@ -522,6 +552,7 @@ validate_export(nfs_export *exp)
char *path = exportent_realpath(&exp->m_export);
struct statfs stf;
int fs_has_fsid = 0;
+ int test_result;

if (stat(path, &stb) < 0) {
xlog(L_ERROR, "Failed to stat %s: %m", path);
@@ -532,7 +563,16 @@ validate_export(nfs_export *exp)
"Remote access will fail", path);
return;
}
- if (!can_test())
+
+ test_result = can_test();
+ if (!test_result)
+ return;
+
+ /*
+ * When netlink is available, skip the export_test() probe.
+ * mountd/exportd will validate exports at cache-fill time.
+ */
+ if (test_result == 2)
return;

if (!statfs(path, &stf) &&

--
2.53.0