Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package nfs-utils for openSUSE:Factory checked in at 2026-08-05 17:46:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/nfs-utils (Old) and /work/SRC/openSUSE:Factory/.nfs-utils.new.16738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "nfs-utils" Wed Aug 5 17:46:00 2026 rev:193 rq:1369307 version:unknown Changes: -------- --- /work/SRC/openSUSE:Factory/nfs-utils/nfs-utils.changes 2026-06-03 20:22:32.813498112 +0200 +++ /work/SRC/openSUSE:Factory/.nfs-utils.new.16738/nfs-utils.changes 2026-08-05 17:46:16.245354701 +0200 @@ -1,0 +2,57 @@ +Mon Aug 3 15:33:21 UTC 2026 - Dominique Leuenberger <[email protected]> + +- Require python3-PyYAML on openSUSE and SLE: the python-* symbol + is valid in both cases. On openSUSE, this is provided by the + module targetting the primary interpreter, which matches the + shebang of the python scripts lines being /usr/bin/python3. + +------------------------------------------------------------------- +Mon Aug 3 09:57:15 UTC 2026 - Anthony Iliopoulos <[email protected]> + +- nfs-client: make the rpcctl util executable (bsc#1273197) + + The specfile intentionally clears the execute bit for all python-based + utilities during the build, in order to prevent generating a package + dependency to the python interpreter. It subsequently restores the + execute bit explicitly for every single script. In the case of rpcctl + this was overlooked when the utility was introduced, and therefore it + was packaged without execute permissions. Fix this by restoring the + permissions. + +------------------------------------------------------------------- +Mon Aug 3 02:36:59 UTC 2026 - Lidong Zhong <[email protected]> + +- Introduce new sub-package nfs-tools-extra, and move all python-based + tools into that (bsc#1268167). + + The nfs-utils base packages previously suggested python-base as a soft + dependency, as some of the less used tools rely on that. Some of those + have further specific python dependencies (such as nfsdclnts which + needs pyyaml). + + We do not want to pull in python as a strict requirement to any of the + base nfs-utils packages to keep the base as minimal as possible. Thus + we introduce a new package (nfs-tools-extra) that includes all those + optional python-based tools, and make that package require pyyaml and + python transitively. + + Also, the base packages now suggest nfs-tools-extra, instead of + python. + +------------------------------------------------------------------- +Fri Jul 31 20:59:19 UTC 2026 - Dirk Müller <[email protected]> + +- update to 2.9.2: + * nfsd: fix memory overflow for haddr + * gssd: fix memory leak in gssd_free_client + * Pass ignore_hosts to export_create() in export_read() + * mountd/exportd: disable netlink when falling back to /proc + * nfs.conf: add no-netlink option to exportd and mountd stanzas + * nfsstat: display NFSv4 callback operation statistics + * libnfsidmap: avoid malloc(0) for empty Local-Realms + * exportfs: drop unused is_export parameter from xtab_read() and xtab_write() + * support/backend_sqlite.c: fix getrandom() fallback + * nfs-iostat: add option to display throughput in MB/s + * exportfs: release NFSv4 state when last client is unexported + +------------------------------------------------------------------- Old: ---- nfs-utils-2.9.1.tar.sign nfs-utils-2.9.1.tar.xz New: ---- nfs-utils-2.9.2.tar.sign nfs-utils-2.9.2.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ nfs-utils.spec ++++++ --- /var/tmp/diff_new_pack.pxF0SY/_old 2026-08-05 17:46:17.105384823 +0200 +++ /var/tmp/diff_new_pack.pxF0SY/_new 2026-08-05 17:46:17.109384964 +0200 @@ -1,7 +1,6 @@ # # spec file for package nfs-utils # -# Copyright (c) 2026 SUSE LLC # Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties @@ -23,7 +22,7 @@ %endif Name: nfs-utils -Version: 2.9.1 +Version: 2.9.2 Release: 0 Summary: Support Utilities for Kernel nfsd License: GPL-2.0-or-later @@ -63,7 +62,7 @@ BuildRequires: pkgconfig(mount) BuildRequires: pkgconfig(readline) BuildRequires: pkgconfig(sqlite3) -Suggests: python-base +Suggests: nfs-tools-extra %{?systemd_ordering} %description @@ -106,6 +105,15 @@ USE_KERNEL_NFSD_NUMBER. For quota over NFS support, install the quota package. +%package -n nfs-tools-extra +Summary: Extra support utils for NFS +Group: Productivity/Networking/NFS +Requires: python3-PyYAML + +%description -n nfs-tools-extra +This package contains optional utilities for the client and kernel based +server. + %package -n libnfsidmap1 Summary: NFSv4 ID Mapping Library Group: Productivity/Networking/NFS @@ -259,15 +267,12 @@ %{_sbindir}/mount.nfs4 %{_sbindir}/umount.nfs %{_sbindir}/umount.nfs4 -%attr(0755,root,root) %{_sbindir}/mountstats -%attr(0755,root,root) %{_sbindir}/nfsiostat %{_sbindir}/nfsdcld %{_sbindir}/nfsidmap %{_sbindir}/nfsstat %{_sbindir}/rpc.gssd %{_sbindir}/rpc.idmapd %{_sbindir}/rpc.statd -%{_sbindir}/rpcctl %{_sbindir}/rpcdebug %{_sbindir}/showmount %{_sbindir}/sm-notify @@ -308,20 +313,15 @@ %{_mandir}/man8/gssd.8%{ext_man} %{_mandir}/man8/idmapd.8%{ext_man} %{_mandir}/man8/mount.nfs.8%{ext_man} -%{_mandir}/man8/mountstats.8%{ext_man} %{_mandir}/man8/nfsconf.8%{ext_man} %{_mandir}/man8/nfsdcld.8%{ext_man} -%{_mandir}/man8/nfsdclddb.8%{ext_man} -%{_mandir}/man8/nfsdclnts.8%{ext_man} %{_mandir}/man8/nfsidmap.8%{ext_man} -%{_mandir}/man8/nfsiostat.8%{ext_man} %{_mandir}/man8/nfsstat.8%{ext_man} %{_mandir}/man8/rpc.gssd.8%{ext_man} %{_mandir}/man8/rpc.idmapd.8%{ext_man} %{_mandir}/man8/rpc.sm-notify.8%{ext_man} %{_mandir}/man8/rpc.statd.8%{ext_man} %{_mandir}/man8/rpc.svcgssd.8%{ext_man} -%{_mandir}/man8/rpcctl.8%{ext_man} %{_mandir}/man8/rpcdebug.8%{ext_man} %{_mandir}/man8/showmount.8%{ext_man} %{_mandir}/man8/sm-notify.8%{ext_man} @@ -354,8 +354,6 @@ %{_sbindir}/nfsdcltrack %{_sbindir}/nfsdctl %{_sbindir}/nfsref -%attr(0755,root,root) %{_sbindir}/nfsdclddb -%attr(0755,root,root) %{_sbindir}/nfsdclnts %{_mandir}/man5/exports.5%{ext_man} %{_mandir}/man7/nfsd.7%{ext_man} %{_mandir}/man8/exportfs.8%{ext_man} @@ -367,6 +365,19 @@ %{_mandir}/man8/nfsdctl.8%{ext_man} %{_mandir}/man8/nfsdcltrack.8%{ext_man} +%files -n nfs-tools-extra +%attr(0755,root,root) %{_sbindir}/mountstats +%attr(0755,root,root) %{_sbindir}/nfsdclddb +%attr(0755,root,root) %{_sbindir}/nfsdclnts +%attr(0755,root,root) %{_sbindir}/nfsiostat +%attr(0755,root,root) %{_sbindir}/rpcctl +%{_sbindir}/rpcctl +%{_mandir}/man8/mountstats.8%{ext_man} +%{_mandir}/man8/nfsdclddb.8%{ext_man} +%{_mandir}/man8/nfsdclnts.8%{ext_man} +%{_mandir}/man8/nfsiostat.8%{ext_man} +%{_mandir}/man8/rpcctl.8%{ext_man} + %files -n libnfsidmap1 %{_libdir}/libnfsidmap-1.0.0/ %{_libdir}/libnfsidmap.so.1* ++++++ nfs-utils-2.9.1.tar.xz -> nfs-utils-2.9.2.tar.xz ++++++ ++++ 1948 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/configure.ac new/nfs-utils-2.9.2/configure.ac --- old/nfs-utils-2.9.1/configure.ac 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/configure.ac 2026-07-23 17:47:22.000000000 +0200 @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. dnl -AC_INIT([linux nfs-utils],[2.9.1],[[email protected]],[nfs-utils]) +AC_INIT([linux nfs-utils],[2.9.2],[[email protected]],[nfs-utils]) AC_CANONICAL_BUILD([]) AC_CANONICAL_HOST([]) AC_CONFIG_MACRO_DIR(aclocal) @@ -256,10 +256,12 @@ 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 +# ensure the system netlink headers have the latest features AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/nfsd_netlink.h>]], - [[int foo = NFSD_A_EXPKEY_SEQNO;]])], + [[int foo = NFSD_CMD_SERVER_STATS_GET;]])], [AC_DEFINE([USE_SYSTEM_NFSD_NETLINK_H], 1, ["Use system's linux/nfsd_netlink.h"])]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <linux/lockd_netlink.h>]], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/nfs.conf new/nfs-utils-2.9.2/nfs.conf --- old/nfs-utils-2.9.1/nfs.conf 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/nfs.conf 2026-07-23 17:47:22.000000000 +0200 @@ -43,6 +43,8 @@ # threads=1 # cache-use-ipaddr=n # ttl=1800 +# no-netlink=0 +# [mountd] # debug="all|auth|call|general|parse" # apply-root-cred=n @@ -55,6 +57,7 @@ # ha-callout= # cache-use-ipaddr=n # ttl=1800 +# no-netlink=0 # [nfsdcld] # debug=0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/export/cache.c new/nfs-utils-2.9.2/support/export/cache.c --- old/nfs-utils-2.9.1/support/export/cache.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/export/cache.c 2026-07-23 17:47:22.000000000 +0200 @@ -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> @@ -3072,6 +3072,8 @@ * were queued before we opened the socket. */ auth_reload(); + if (no_netlink) + goto fallback; cache_nl_process_export(); cache_nl_process_expkey(); cache_nl_process_ip_map(); @@ -3079,11 +3081,16 @@ cache_nl_process_unix_gid(); return; } +fallback: xlog(L_NOTICE, "sunrpc netlink family unavailable, falling back to /proc"); nl_socket_free(nfsd_nl_notify_sock); nfsd_nl_notify_sock = NULL; nl_socket_free(nfsd_nl_cmd_sock); nfsd_nl_cmd_sock = NULL; + nl_socket_free(sunrpc_nl_notify_sock); + sunrpc_nl_notify_sock = NULL; + nl_socket_free(sunrpc_nl_cmd_sock); + sunrpc_nl_cmd_sock = NULL; } for (i=0; cachelist[i].cache_name; i++ ) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/export/cache_flush.c new/nfs-utils-2.9.2/support/export/cache_flush.c --- old/nfs-utils-2.9.1/support/export/cache_flush.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/export/cache_flush.c 2026-07-23 17:47:22.000000000 +0200 @@ -38,6 +38,8 @@ #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; @@ -162,5 +164,6 @@ return; } /* Fallback: /proc path */ + no_netlink = 1; cache_proc_flush(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/export/export.c new/nfs-utils-2.9.2/support/export/export.c --- old/nfs-utils-2.9.1/support/export/export.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/export/export.c 2026-07-23 17:47:22.000000000 +0200 @@ -122,7 +122,7 @@ while ((eep = getexportent(0)) != NULL) { exp = export_lookup(eep->e_hostname, eep->e_path, ignore_hosts); if (!exp) { - if (export_create(eep, 0)) + if (export_create(eep, ignore_hosts)) /* possible complaints already logged */ volumes++; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/export/xtab.c new/nfs-utils-2.9.2/support/export/xtab.c --- old/nfs-utils-2.9.1/support/export/xtab.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/export/xtab.c 2026-07-23 17:47:22.000000000 +0200 @@ -33,11 +33,8 @@ 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 @@ 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 @@ } 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 @@ int xtab_export_read(void) { - return xtab_read(etab.statefn, etab.lockfn, 1); + return xtab_read(etab.statefn, etab.lockfn); } /* @@ -100,7 +89,7 @@ * 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 @@ 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/include/compat.h new/nfs-utils-2.9.2/support/include/compat.h --- old/nfs-utils-2.9.1/support/include/compat.h 1970-01-01 01:00:00.000000000 +0100 +++ new/nfs-utils-2.9.2/support/include/compat.h 2026-07-23 17:47:22.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/include/config.h.in new/nfs-utils-2.9.2/support/include/config.h.in --- old/nfs-utils-2.9.1/support/include/config.h.in 2026-04-04 11:28:41.000000000 +0200 +++ new/nfs-utils-2.9.2/support/include/config.h.in 2026-07-28 16:06:23.000000000 +0200 @@ -255,6 +255,9 @@ /* Bundled lib always has the `nfs4_set_debug' function. */ #undef HAVE_NFS4_SET_DEBUG +/* Define to 1 if nfsd generic netlink support is available */ +#undef HAVE_NFSD_NETLINK + /* Define this if you want NFSv4 server only support compiled in */ #undef HAVE_NFSV4SERVER_SUPPORT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/include/nfsd_netlink.h new/nfs-utils-2.9.2/support/include/nfsd_netlink.h --- old/nfs-utils-2.9.1/support/include/nfsd_netlink.h 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/include/nfsd_netlink.h 2026-07-23 17:47:22.000000000 +0200 @@ -152,15 +152,6 @@ }; 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, NFSD_A_SVC_EXPORT_PATH, @@ -214,6 +205,62 @@ }; 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_PROC4CB_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, NFSD_CMD_THREADS_GET, @@ -229,6 +276,10 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/include/nfsdnl.h new/nfs-utils-2.9.2/support/include/nfsdnl.h --- old/nfs-utils-2.9.1/support/include/nfsdnl.h 1970-01-01 01:00:00.000000000 +0100 +++ new/nfs-utils-2.9.2/support/include/nfsdnl.h 2026-07-23 17:47:22.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/nfs/Makefile.am new/nfs-utils-2.9.2/support/nfs/Makefile.am --- old/nfs-utils-2.9.1/support/nfs/Makefile.am 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/nfs/Makefile.am 2026-07-23 17:47:22.000000000 +0200 @@ -11,6 +11,13 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/nfs/fh_key_file.c new/nfs-utils-2.9.2/support/nfs/fh_key_file.c --- old/nfs-utils-2.9.1/support/nfs/fh_key_file.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/nfs/fh_key_file.c 2026-07-23 17:47:22.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/nfs/getport.c new/nfs-utils-2.9.2/support/nfs/getport.c --- old/nfs-utils-2.9.1/support/nfs/getport.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/nfs/getport.c 2026-07-23 17:47:22.000000000 +0200 @@ -452,11 +452,12 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/nfs/nfsdnl.c new/nfs-utils-2.9.2/support/nfs/nfsdnl.c --- old/nfs-utils-2.9.1/support/nfs/nfsdnl.c 1970-01-01 01:00:00.000000000 +0100 +++ new/nfs-utils-2.9.2/support/nfs/nfsdnl.c 2026-07-23 17:47:22.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/nfsidmap/libnfsidmap.c new/nfs-utils-2.9.2/support/nfsidmap/libnfsidmap.c --- old/nfs-utils-2.9.1/support/nfsidmap/libnfsidmap.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/nfsidmap/libnfsidmap.c 2026-07-23 17:47:22.000000000 +0200 @@ -409,15 +409,14 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/support/reexport/backend_sqlite.c new/nfs-utils-2.9.2/support/reexport/backend_sqlite.c --- old/nfs-utils-2.9.1/support/reexport/backend_sqlite.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/support/reexport/backend_sqlite.c 2026-07-23 17:47:22.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/tools/nfs-iostat/nfs-iostat.py new/nfs-utils-2.9.2/tools/nfs-iostat/nfs-iostat.py --- old/nfs-utils-2.9.1/tools/nfs-iostat/nfs-iostat.py 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/tools/nfs-iostat/nfs-iostat.py 2026-07-23 17:47:22.000000000 +0200 @@ -327,7 +327,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/tools/nfs-iostat/nfsiostat.man new/nfs-utils-2.9.2/tools/nfs-iostat/nfsiostat.man --- old/nfs-utils-2.9.1/tools/nfs-iostat/nfsiostat.man 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/tools/nfs-iostat/nfsiostat.man 2026-07-23 17:47:22.000000000 +0200 @@ -56,16 +56,16 @@ .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 @@ .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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/exportfs/exportfs.c new/nfs-utils-2.9.2/utils/exportfs/exportfs.c --- old/nfs-utils-2.9.1/utils/exportfs/exportfs.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/exportfs/exportfs.c 2026-07-23 17:47:22.000000000 +0200 @@ -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 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 @@ * 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 @@ 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/exportfs/exportfs.man new/nfs-utils-2.9.2/utils/exportfs/exportfs.man --- old/nfs-utils-2.9.1/utils/exportfs/exportfs.man 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/exportfs/exportfs.man 2026-07-23 17:47:22.000000000 +0200 @@ -256,6 +256,23 @@ .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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/gssd/gssd.c new/nfs-utils-2.9.2/utils/gssd/gssd.c --- old/nfs-utils-2.9.1/utils/gssd/gssd.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/gssd/gssd.c 2026-07-23 17:47:22.000000000 +0200 @@ -360,6 +360,10 @@ clp->prog = program; clp->vers = version; clp->protocol = protoname; + clp->upcall_address = NULL; + clp->upcall_port = NULL; + clp->upcall_protoname = NULL; + clp->upcall_service = NULL; goto out; @@ -414,16 +418,16 @@ free(clp->servicename); free(clp->servername); free(clp->protocol); - if (!clp->servername) { - if (clp->upcall_address) - free(clp->upcall_address); - if (clp->upcall_port) - free(clp->upcall_port); - if (clp->upcall_protoname) - free(clp->upcall_protoname); - if (clp->upcall_service) - free(clp->upcall_service); - } + + if (clp->upcall_address) + free(clp->upcall_address); + if (clp->upcall_port) + free(clp->upcall_port); + if (clp->upcall_protoname) + free(clp->upcall_protoname); + if (clp->upcall_service) + free(clp->upcall_service); + free(clp); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/gssd/gssd_proc.c new/nfs-utils-2.9.2/utils/gssd/gssd_proc.c --- old/nfs-utils-2.9.1/utils/gssd/gssd_proc.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/gssd/gssd_proc.c 2026-07-23 17:47:22.000000000 +0200 @@ -897,6 +897,9 @@ if (info->srchost) free(info->srchost); out_info: + pthread_mutex_lock(&clp_lock); + clp->refcount--; + pthread_mutex_unlock(&clp_lock); free(info); info = NULL; goto out; @@ -981,6 +984,7 @@ tinfo->timeout.tv_sec += upcall_timeout; TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list); pthread_mutex_unlock(&active_thread_list_lock); + pthread_attr_destroy(&attr); return ret; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/nfsd/nfsd.c new/nfs-utils-2.9.2/utils/nfsd/nfsd.c --- old/nfs-utils-2.9.1/utils/nfsd/nfsd.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/nfsd/nfsd.c 2026-07-23 17:47:22.000000000 +0200 @@ -185,7 +185,7 @@ hcounter = 0; } if (hcounter) { - haddr = realloc(haddr, sizeof(char*) * hcounter+1); + haddr = realloc(haddr, sizeof(char*) * (hcounter+1)); if(!haddr) { fprintf(stderr, "%s: unable to allocate " "memory.\n", progname); @@ -311,12 +311,16 @@ argv[0], count); count = 1; } else if (count == 0) { - /* - * don't bother setting anything else if the threads - * are coming down anyway. - */ - socket_up = 1; - goto set_threads; + if (nfssvc_inuse()) { + /* + * don't bother setting anything else if the threads + * are coming down anyway. + */ + socket_up = 1; + goto set_threads; + } else { + goto out; + } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/nfsdctl/nfsdctl.c new/nfs-utils-2.9.2/utils/nfsdctl/nfsdctl.c --- old/nfs-utils-2.9.1/utils/nfsdctl/nfsdctl.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/nfsdctl/nfsdctl.c 2026-07-23 17:47:22.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/nfsstat/Makefile.am new/nfs-utils-2.9.2/utils/nfsstat/Makefile.am --- old/nfs-utils-2.9.1/utils/nfsstat/Makefile.am 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/nfsstat/Makefile.am 2026-07-23 17:47:22.000000000 +0200 @@ -5,8 +5,10 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/nfs-utils-2.9.1/utils/nfsstat/nfsstat.c new/nfs-utils-2.9.2/utils/nfsstat/nfsstat.c --- old/nfs-utils-2.9.1/utils/nfsstat/nfsstat.c 2026-04-04 11:27:07.000000000 +0200 +++ new/nfs-utils-2.9.2/utils/nfsstat/nfsstat.c 2026-07-23 17:47:22.000000000 +0200 @@ -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 { @@ -33,6 +44,8 @@ SRVPROC4_SZ = 2, CLTPROC4_SZ = 59, SRVPROC4OPS_SZ = 71, + SRVPROC4CB_SZ = 16, /* indexed by callback opcode; OP_CB_OFFLOAD == 15 */ + SRVPROC4CB_FIRST = 3, /* first assigned callback opcode, OP_CB_GETATTR */ }; static unsigned int srvproc2info[SRVPROC2_SZ+2], @@ -49,6 +62,8 @@ cltproc4info_old[CLTPROC4_SZ+2]; /* NFSv4 call counts ([0] == 49) */ static unsigned int srvproc4opsinfo[SRVPROC4OPS_SZ+2], srvproc4opsinfo_old[SRVPROC4OPS_SZ+2]; /* NFSv4 call counts ([0] == 59) */ +static unsigned int srvproc4cbinfo[SRVPROC4CB_SZ+2], + srvproc4cbinfo_old[SRVPROC4CB_SZ+2]; /* NFSv4 callback op counts (netlink only, [0] == 16) */ static unsigned int srvnetinfo[5], srvnetinfo_old[5]; /* 0 # of received packets * 1 UDP packets * 2 TCP packets @@ -196,6 +211,19 @@ "write_same", }; +/* + * NFSv4 callback (backchannel) operations, indexed directly by RFC 8881 + * callback opcode. Opcodes 0-2 are not assigned, so the first three slots + * are placeholders to keep the array index aligned with the opcode value; + * display starts at SRVPROC4CB_FIRST and never prints them. + */ +static const char * nfssrvproc4cbname[SRVPROC4CB_SZ] = { + "op0-unused", "op1-unused", "op2-unused", "cb_getattr", + "cb_recall", "cb_layoutrecall", "cb_notify", "cb_push_deleg", + "cb_recall_any","cb_recall_obj","cb_recall_slot", "cb_sequence", + "cb_wants_cancel", "cb_notify_lock", "cb_notify_devid", "cb_offload", +}; + #define LABEL_srvnet "Server packet stats:\n" #define LABEL_srvrpc "Server rpc stats:\n" #define LABEL_srvrc "Server reply cache:\n" @@ -206,6 +234,7 @@ #define LABEL_srvproc3 "Server nfs v3:\n" #define LABEL_srvproc4 "Server nfs v4:\n" #define LABEL_srvproc4ops "Server nfs v4 operations:\n" +#define LABEL_srvproc4cb "Server nfs v4 callback operations:\n" #define LABEL_cltnet "Client packet stats:\n" #define LABEL_cltrpc "Client rpc stats:\n" #define LABEL_cltproc2 "Client nfs v2:\n" @@ -239,6 +268,7 @@ SRV(proc3,s),\ SRV(proc4,s), \ SRV(proc4ops,s),\ + SRV(proc4cb,s),\ { NULL, NULL, 0, NULL }\ } #define DECLARE_CLT(n, s...) static statinfo n##s[] = { \ @@ -271,6 +301,7 @@ 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); @@ -671,14 +702,24 @@ nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)); print_callstats(LABEL_srvproc4ops, - nfssrvproc4opname, srvproc4opsinfo + 1, + nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)); + /* + * Callback op counts are only available via + * netlink; srvproc4cbinfo[0] is left zero when + * stats come from /proc. + */ + if (srvproc4cbinfo[0]) + print_callstats(LABEL_srvproc4cb, + nfssrvproc4cbname + SRVPROC4CB_FIRST, + srvproc4cbinfo + 1 + SRVPROC4CB_FIRST, + SRVPROC4CB_SZ - SRVPROC4CB_FIRST); } } } } static void -print_client_stats(int opt_prt) +print_client_stats(int opt_prt) { if (opt_prt & PRNT_NET) { if (opt_sleep && !has_rpcstats(cltnetinfo, 4)) { @@ -794,8 +835,13 @@ nfssrvproc4name, srvproc4info + 1, sizeof(nfssrvproc4name)/sizeof(char *)); print_callstats_list("nfs v4 servop", - nfssrvproc4opname, srvproc4opsinfo + 1, + nfssrvproc4opname, srvproc4opsinfo + 1, sizeof(nfssrvproc4opname)/sizeof(char *)); + if (srvproc4cbinfo[0]) + print_callstats_list("nfs v4 cback", + nfssrvproc4cbname + SRVPROC4CB_FIRST, + srvproc4cbinfo + 1 + SRVPROC4CB_FIRST, + SRVPROC4CB_SZ - SRVPROC4CB_FIRST); } } } @@ -1051,6 +1097,278 @@ 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; + case NFSD_A_SERVER_STATS_PROC4CB_OPS: + si = get_stat_info("proc4cb", info); + if (si) + parse_one_proc_entry(attr, si->valptr, + SRVPROC4CB_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 +1378,10 @@ 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;
