diff --git a/SOURCES/nfs-utils-2.5.4-conffile-argument.patch b/SOURCES/nfs-utils-2.5.4-conffile-argument.patch new file mode 100644 index 0000000..5a770d6 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-conffile-argument.patch @@ -0,0 +1,38 @@ +diff -up nfs-utils-2.5.4/support/nfs/conffile.c.orig nfs-utils-2.5.4/support/nfs/conffile.c +--- nfs-utils-2.5.4/support/nfs/conffile.c.orig 2021-06-10 14:07:47.000000000 -0400 ++++ nfs-utils-2.5.4/support/nfs/conffile.c 2025-01-08 10:36:51.838168127 -0500 +@@ -169,13 +169,15 @@ static void free_conftrans(struct conf_t + * Insert a tag-value combination from LINE (the equal sign is at POS) + */ + static int +-conf_remove_now(const char *section, const char *tag) ++conf_remove_now(const char *section, const char *arg, const char *tag) + { + struct conf_binding *cb, *next; + + cb = LIST_FIRST(&conf_bindings[conf_hash (section)]); + for (; cb; cb = next) { + next = LIST_NEXT(cb, link); ++ if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0)) ++ continue; + if (strcasecmp(cb->section, section) == 0 + && strcasecmp(cb->tag, tag) == 0) { + LIST_REMOVE(cb, link); +@@ -217,7 +219,7 @@ conf_set_now(const char *section, const + struct conf_binding *node = 0; + + if (override) +- conf_remove_now(section, tag); ++ conf_remove_now(section, arg, tag); + else if (conf_get_section(section, arg, tag)) { + if (!is_default) { + xlog(LOG_INFO, "conf_set: duplicate tag [%s]:%s, ignoring...", +@@ -1252,7 +1254,7 @@ conf_end(int transaction, int commit) + node->is_default); + break; + case CONF_REMOVE: +- conf_remove_now(node->section, node->tag); ++ conf_remove_now(node->section, node->arg, node->tag); + break; + case CONF_REMOVE_SECTION: + conf_remove_section_now(node->section); diff --git a/SOURCES/nfs-utils-2.5.4-fix-nfsdcld-starting-too-early.patch b/SOURCES/nfs-utils-2.5.4-fix-nfsdcld-starting-too-early.patch new file mode 100644 index 0000000..0cef310 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-fix-nfsdcld-starting-too-early.patch @@ -0,0 +1,39 @@ +From 092971c4741814d831e8172eb3332e53aeaef890 Mon Sep 17 00:00:00 2001 +From: Seiichi Ikarashi +Date: Fri, 18 Oct 2024 08:29:57 -0400 +Subject: [nfs-utils PATCH] nfsdcld: prevent from accessing + /var/lib/nfs/nfsdcld in read-only file system during boot + +I saw a VMWare guest that hit a rare condition during boot; +nfsdcld started too early to check access on /var/lib/nfs/nfsdcld which were +still in read-only file system as follows: + + nfsdcld[...]: Unexpected error when checking access on /var/lib/nfs/nfsdcld: Read-only file system + systemd[1]: nfsdcld.service: Main process exited, code=exited, status=226/NAMESPACE + systemd[1]: nfsdcld.service: Failed with result 'exit-code'. + +nfsdcld.service needs to wait the root file system to be remounted at least. + +Reviewed-by: Jeff Layton +Signed-off-by: Seiichi Ikarashi +Signed-off-by: Steve Dickson +--- + systemd/nfsdcld.service | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/systemd/nfsdcld.service b/systemd/nfsdcld.service +index 3ced5658..188123df 100644 +--- a/systemd/nfsdcld.service ++++ b/systemd/nfsdcld.service +@@ -4,7 +4,7 @@ Documentation=man:nfsdcld(8) + DefaultDependencies=no + Conflicts=umount.target + Requires=rpc_pipefs.target proc-fs-nfsd.mount +-After=rpc_pipefs.target proc-fs-nfsd.mount ++After=rpc_pipefs.target proc-fs-nfsd.mount systemd-remount-fs.service + + [Service] + Type=forking +-- +2.48.1 + diff --git a/SOURCES/nfs-utils-2.5.4-mount-v3-retry.patch b/SOURCES/nfs-utils-2.5.4-mount-v3-retry.patch new file mode 100644 index 0000000..aff6f3d --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-mount-v3-retry.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-2.5.4/utils/mount/stropts.c.orig nfs-utils-2.5.4/utils/mount/stropts.c +--- nfs-utils-2.5.4/utils/mount/stropts.c.orig 2024-11-24 08:36:03.734689817 -0500 ++++ nfs-utils-2.5.4/utils/mount/stropts.c 2024-11-24 08:37:52.541239493 -0500 +@@ -973,7 +973,7 @@ fall_back: + if ((result = nfs_try_mount_v3v2(mi, FALSE))) + return result; + +- if (errno != EBUSY && errno != EACCES) ++ if (errno != EBUSY && errno != EACCES && errno != ETIMEDOUT) + errno = olderrno; + + return result; diff --git a/SOURCES/nfs-utils-2.5.4-mount-writable.patch b/SOURCES/nfs-utils-2.5.4-mount-writable.patch new file mode 100644 index 0000000..c8b6765 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-mount-writable.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-2.5.4/utils/mount/Makefile.am.orig nfs-utils-2.5.4/utils/mount/Makefile.am +--- nfs-utils-2.5.4/utils/mount/Makefile.am.orig 2021-06-10 14:07:47.000000000 -0400 ++++ nfs-utils-2.5.4/utils/mount/Makefile.am 2024-11-24 09:31:37.817565778 -0500 +@@ -47,7 +47,7 @@ install-exec-hook: + ln -sf mount.nfs mount.nfs4 && \ + ln -sf mount.nfs umount.nfs && \ + ln -sf mount.nfs umount.nfs4 && \ +- chmod 4511 mount.nfs ) ++ chmod 4711 mount.nfs ) + uninstall-hook: + (cd $(DESTDIR)$(sbindir) && \ + rm -f mount.nfs4 umount.nfs umount.nfs4) diff --git a/SOURCES/nfs-utils-2.5.4-nfsiostat-fixes.patch b/SOURCES/nfs-utils-2.5.4-nfsiostat-fixes.patch new file mode 100644 index 0000000..7533ba4 --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-nfsiostat-fixes.patch @@ -0,0 +1,310 @@ +diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 8e129c83..d488f9e1 100755 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -333,6 +333,11 @@ class DeviceData: + found = True + self.__parse_rpc_line(words) + ++ def fstype(self): ++ """Return the fstype for the mountpoint ++ """ ++ return self.__nfs_data['fstype'] ++ + def is_nfs_mountpoint(self): + """Return True if this is an NFS or NFSv4 mountpoint, + otherwise return False +@@ -953,73 +958,78 @@ def nfsstat_command(args): + return 0 + + def print_iostat_summary(old, new, devices, time): ++ if len(devices) == 0: ++ print('No NFS mount points were found') ++ return ++ + for device in devices: + stats = DeviceData() + stats.parse_stats(new[device]) +- if not old or device not in old: ++ if old and device in old: ++ old_stats = DeviceData() ++ old_stats.parse_stats(old[device]) ++ if stats.fstype() == old_stats.fstype(): ++ stats.compare_iostats(old_stats).display_iostats(time) ++ else: # device is in old, but fstypes are different ++ stats.display_iostats(time) ++ else: # device is only in new + stats.display_iostats(time) +- else: +- if ("fstype autofs" not in str(old[device])) and ("fstype autofs" not in str(new[device])): +- old_stats = DeviceData() +- old_stats.parse_stats(old[device]) +- diff_stats = stats.compare_iostats(old_stats) +- diff_stats.display_iostats(time) ++ ++def list_nfs_mounts(givenlist, mountstats): ++ """return a list of NFS mounts given a list to validate or ++ return a full list if the given list is empty - ++ may return an empty list if none found ++ """ ++ devicelist = [] ++ if len(givenlist) > 0: ++ for device in givenlist: ++ if device in mountstats: ++ stats = DeviceData() ++ stats.parse_stats(mountstats[device]) ++ if stats.is_nfs_mountpoint(): ++ devicelist += [device] ++ else: ++ for device, descr in mountstats.items(): ++ stats = DeviceData() ++ stats.parse_stats(descr) ++ if stats.is_nfs_mountpoint(): ++ devicelist += [device] ++ return devicelist + + def iostat_command(args): + """iostat-like command for NFS mount points + """ + mountstats = parse_stats_file(args.infile) +- devices = [os.path.normpath(mp) for mp in args.mountpoints] ++ origdevices = [os.path.normpath(mp) for mp in args.mountpoints] + + if args.since: + old_mountstats = parse_stats_file(args.since) + else: + old_mountstats = None + +- # make certain devices contains only NFS mount points +- if len(devices) > 0: +- check = [] +- for device in devices: +- stats = DeviceData() +- try: +- stats.parse_stats(mountstats[device]) +- if stats.is_nfs_mountpoint(): +- check += [device] +- except KeyError: +- continue +- devices = check +- else: +- for device, descr in mountstats.items(): +- stats = DeviceData() +- stats.parse_stats(descr) +- if stats.is_nfs_mountpoint(): +- devices += [device] +- if len(devices) == 0: +- print('No NFS mount points were found') +- return 1 +- + sample_time = 0 + ++ # make certain devices contains only NFS mount points ++ devices = list_nfs_mounts(origdevices, mountstats) ++ print_iostat_summary(old_mountstats, mountstats, devices, sample_time) ++ + if args.interval is None: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time) + return + +- if args.count is not None: +- count = args.count +- while count != 0: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time) +- old_mountstats = mountstats +- time.sleep(args.interval) +- sample_time = args.interval +- mountstats = parse_stats_file(args.infile) ++ count = args.count ++ while True: ++ if count is not None: + count -= 1 +- else: +- while True: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time) +- old_mountstats = mountstats +- time.sleep(args.interval) +- sample_time = args.interval +- mountstats = parse_stats_file(args.infile) ++ if count == 0: ++ break ++ time.sleep(args.interval) ++ old_mountstats = mountstats ++ sample_time = args.interval ++ mountstats = parse_stats_file(args.infile) ++ # nfs mountpoints may appear or disappear, so we need to ++ # recheck the devices list each time we parse mountstats ++ devices = list_nfs_mounts(origdevices, mountstats) ++ print_iostat_summary(old_mountstats, mountstats, devices, sample_time) + + args.infile.close() + if args.since: +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index 85294fb9..e46b1a83 100755 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -187,6 +187,11 @@ class DeviceData: + found = True + self.__parse_rpc_line(words) + ++ def fstype(self): ++ """Return the fstype for the mountpoint ++ """ ++ return self.__nfs_data['fstype'] ++ + def is_nfs_mountpoint(self): + """Return True if this is an NFS or NFSv4 mountpoint, + otherwise return False +@@ -471,41 +476,31 @@ def parse_stats_file(filename): + return ms_dict + + def print_iostat_summary(old, new, devices, time, options): +- stats = {} +- diff_stats = {} +- devicelist = [] +- if old: +- # Trim device list to only include intersection of old and new data, +- # this addresses umounts due to autofs mountpoints +- for device in devices: +- if "fstype autofs" not in str(old[device]): +- devicelist.append(device) +- else: +- devicelist = devices ++ display_stats = {} ++ ++ if len(devices) == 0: ++ print('No NFS mount points were found') ++ return + +- for device in devicelist: +- stats[device] = DeviceData() +- stats[device].parse_stats(new[device]) +- if old: ++ for device in devices: ++ stats = DeviceData() ++ stats.parse_stats(new[device]) ++ if old and device in old: + old_stats = DeviceData() + old_stats.parse_stats(old[device]) +- diff_stats[device] = stats[device].compare_iostats(old_stats) ++ if stats.fstype() == old_stats.fstype(): ++ display_stats[device] = stats.compare_iostats(old_stats) ++ else: # device is in old, but fstypes are different ++ display_stats[device] = stats ++ else: # device is only in new ++ display_stats[device] = stats + + if options.sort: +- if old: +- # We now have compared data and can print a comparison +- # ordered by mountpoint ops per second +- devicelist.sort(key=lambda x: diff_stats[x].ops(time), reverse=True) +- else: +- # First iteration, just sort by newly parsed ops/s +- devicelist.sort(key=lambda x: stats[x].ops(time), reverse=True) ++ devices.sort(key=lambda x: display_stats[x].ops(time), reverse=True) + + count = 1 +- for device in devicelist: +- if old: +- diff_stats[device].display_iostats(time, options.which) +- else: +- stats[device].display_iostats(time, options.which) ++ for device in devices: ++ display_stats[device].display_iostats(time, options.which) + + count += 1 + if (count > options.list): +@@ -520,10 +515,11 @@ def list_nfs_mounts(givenlist, mountstats): + devicelist = [] + if len(givenlist) > 0: + for device in givenlist: +- stats = DeviceData() +- stats.parse_stats(mountstats[device]) +- if stats.is_nfs_mountpoint(): +- devicelist += [device] ++ if device in mountstats: ++ stats = DeviceData() ++ stats.parse_stats(mountstats[device]) ++ if stats.is_nfs_mountpoint(): ++ devicelist += [device] + else: + for device, descr in mountstats.items(): + stats = DeviceData() +@@ -592,11 +588,7 @@ client are listed. + parser.add_option_group(displaygroup) + + (options, args) = parser.parse_args(sys.argv) +- for arg in args: +- +- if arg == sys.argv[0]: +- continue +- ++ for arg in args[1:]: + if arg in mountstats: + origdevices += [arg] + elif not interval_seen: +@@ -622,47 +614,29 @@ client are listed. + print('Illegal value %s' % arg) + return + +- # make certain devices contains only NFS mount points +- devices = list_nfs_mounts(origdevices, mountstats) +- if len(devices) == 0: +- print('No NFS mount points were found') +- return +- +- + old_mountstats = None + sample_time = 0.0 + ++ # make certain devices contains only NFS mount points ++ devices = list_nfs_mounts(origdevices, mountstats) ++ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) ++ + if not interval_seen: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) + return + +- if count_seen: +- while count != 0: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) +- old_mountstats = mountstats +- time.sleep(interval) +- sample_time = interval +- mountstats = parse_stats_file('/proc/self/mountstats') +- # automount mountpoints add and drop, if automount is involved +- # we need to recheck the devices list when reparsing +- devices = list_nfs_mounts(origdevices,mountstats) +- if len(devices) == 0: +- print('No NFS mount points were found') +- return ++ while True: ++ if count_seen: + count -= 1 +- else: +- while True: +- print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) +- old_mountstats = mountstats +- time.sleep(interval) +- sample_time = interval +- mountstats = parse_stats_file('/proc/self/mountstats') +- # automount mountpoints add and drop, if automount is involved +- # we need to recheck the devices list when reparsing +- devices = list_nfs_mounts(origdevices,mountstats) +- if len(devices) == 0: +- print('No NFS mount points were found') +- return ++ if count == 0: ++ break ++ time.sleep(interval) ++ old_mountstats = mountstats ++ sample_time = interval ++ mountstats = parse_stats_file('/proc/self/mountstats') ++ # nfs mountpoints may appear or disappear, so we need to ++ # recheck the devices list each time we parse mountstats ++ devices = list_nfs_mounts(origdevices, mountstats) ++ print_iostat_summary(old_mountstats, mountstats, devices, sample_time, options) + + # + # Main diff --git a/SOURCES/nfs-utils-2.5.4-rpcidmapd-nfsopen-failure.patch b/SOURCES/nfs-utils-2.5.4-rpcidmapd-nfsopen-failure.patch new file mode 100644 index 0000000..3f86e5e --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-rpcidmapd-nfsopen-failure.patch @@ -0,0 +1,31 @@ +commit fdc5081a8f48dd161ea94fff11bf0afd8d901747 +Author: Salvatore Bonaccorso +Date: Fri Sep 20 12:30:07 2024 -0400 + + rpc.idmapd: nfsopen() failures should not be fatal + + dirscancb() loops over all clnt* subdirectories of /run/rpc_pipefs/nfs/. + Some of these directories contain /idmap files, others don't. nfsopen() + returns -1 for the latter; we then want to skip the directory, not abort + the entire scan. + + Reported-by: Sergio Gelato + Closes: https://lore.kernel.org/linux-nfs/ZmCB_zqdu2cynJ1M@astro.su.se/ + Link: https://bugs.debian.org/1072573 + Patch-originally-by: Sergio Gelato + Signed-off-by: Salvatore Bonaccorso + Signed-off-by: Steve Dickson + +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index cd9a965f..5231f56d 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -556,7 +556,7 @@ dirscancb(int fd, short UNUSED(which), void *data) + if (nfsopen(ic) == -1) { + close(ic->ic_dirfd); + free(ic); +- goto out; ++ continue; + } + + if (verbose > 2) diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index 3bb183e..8e2c878 100644 --- a/SPECS/nfs-utils.spec +++ b/SPECS/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.5.4 -Release: 27%{?dist} +Release: 34%{?dist} Epoch: 1 # group all 32bit related archs @@ -65,6 +65,16 @@ Patch025: nfs-utils-2.5.4-rpcdebug-check-read-return.patch Patch026: nfs-utils-2.5.4-gssd-allowed-enctypes.patch Patch027: nfs-utils-2.5.4-gssd-segfault.patch +# +# RHEL9.6 +# +Patch028: nfs-utils-2.5.4-rpcidmapd-nfsopen-failure.patch +Patch029: nfs-utils-2.5.4-mount-writable.patch +Patch030: nfs-utils-2.5.4-mount-v3-retry.patch +Patch031: nfs-utils-2.5.4-conffile-argument.patch +Patch032: nfs-utils-2.5.4-fix-nfsdcld-starting-too-early.patch +Patch033: nfs-utils-2.5.4-nfsiostat-fixes.patch + Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch Patch102: nfs-utils-1.2.5-idmap-errmsg.patch @@ -327,7 +337,6 @@ if [ $1 -eq 0 ]; then %systemd_preun nfs-client.target rm -rf /etc/nfsmount.conf.d - rm -rf /var/lib/nfs/v4recovery fi %postun @@ -360,9 +369,9 @@ fi %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd/sm %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd/sm.bak -%ghost %attr(644,rpcuser,rpcuser) %{_statdpath}/state -%config(noreplace) %{_sharedstatedir}/nfs/etab -%config(noreplace) %{_sharedstatedir}/nfs/rmtab +%ghost %attr(644,root,root) %{_statdpath}/state +%ghost %attr(644,root,root) %{_sharedstatedir}/nfs/etab +%ghost %attr(644,root,root) %{_sharedstatedir}/nfs/rmtab %config(noreplace) %{_sysconfdir}/request-key.d/id_resolver.conf %config(noreplace) %{_sysconfdir}/modprobe.d/lockd.conf %config(noreplace) %{_sysconfdir}/nfs.conf @@ -420,7 +429,7 @@ fi %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd/sm %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd/sm.bak -%ghost %attr(644,rpcuser,rpcuser) %{_statdpath}/state +%ghost %attr(644,root,root) %{_statdpath}/state %config(noreplace) %{_sysconfdir}/nfsmount.conf %config(noreplace) %{_sysconfdir}/nfs.conf %config(noreplace) %{_sysconfdir}/request-key.d/id_resolver.conf @@ -461,6 +470,7 @@ fi %files -n nfsv4-client-utils %config(noreplace) /etc/nfsmount.conf +%config(noreplace) %{_sysconfdir}/nfs.conf %dir %{_sharedstatedir}/nfs/v4recovery %dir %attr(555, root, root) %{_sharedstatedir}/nfs/rpc_pipefs %dir %{_libexecdir}/nfs-utils @@ -505,6 +515,38 @@ fi %{_mandir}/*/nfsiostat.8.gz %changelog +* Sun Feb 16 2025 Steve Dickson 2.5.4-34 +- mountstats/nfsiostat: bugfixes for iostat (RHEL-72243) + +* Sat Feb 15 2025 Steve Dickson 2.5.4-33 +- Add nfs.conf to nfsv4-client-utils package (RHEL-72013) + +* Fri Feb 7 2025 Scott Mayhew 2.5.4-32 +- Undo 'Add explicit version requirement for libnfsidmap' from previous build (RHEL-78107) +- Undo 'Add --disable-sbin-override for when /sbin is a symlink' from previous build (RHEL-69771) +- Undo 'Move remaining binaries from /sbin to /usr/sbin' from previous build (RHEL-69771) + +* Thu Feb 6 2025 Scott Mayhew 2.5.4-31 +- nfsdcld: prevent from accessing /var/lib/nfs/nfsdcld in read-only file system during boot (RHEL-78177) +- Replace functional gating tests with the ones from RHEL10 (RHEL-78110) +- Add explicit version requirement for libnfsidmap (RHEL-78107) +- Add --disable-sbin-override for when /sbin is a symlink (RHEL-69771) +- Move remaining binaries from /sbin to /usr/sbin (RHEL-69771) + +* Mon Feb 3 2025 Scott Mayhew 2.5.4-30 +- fix ownership of /var/lib/nfs/statd/state (RHEL-72823) +- /var/lib/nfs/{etab,rmtab} should not be marked as config files (RHEL-64340) + +* Wed Jan 8 2025 Steve Dickson 2.5.4-29 +- conffile: add 'arg' argument to conf_remove_now() (RHEL-70923) + +* Sun Nov 24 2024 Steve Dickson 2.5.4-28 +- Makefile.am: allow mount.nfs to be writeable by owner (RHEL-68701) +- mount.nfs: retry NFSv3 mount after NFSv4 failure in auto negotiation (RHEL-68574) + +* Thu Nov 14 2024 Steve Dickson 2.5.4-28 +- rpc.idmapd: nfsopen() failures should not be fatal (RHEL-65727) + * Fri Aug 9 2024 Steve Dickson 2.5.4-27 - rpc-gssd.service has status failed (due to rpc.gssd segfault) (RHEL-43286)