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-gssd-dup-cachecreds.patch b/SOURCES/nfs-utils-2.5.4-gssd-dup-cachecreds.patch new file mode 100644 index 0000000..7174dcc --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-gssd-dup-cachecreds.patch @@ -0,0 +1,213 @@ +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 57b3cf8a..4f3a196a 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -168,7 +168,8 @@ static int select_krb5_ccache(const struct dirent *d); + static int gssd_find_existing_krb5_ccache(uid_t uid, char *dirname, + const char **cctype, struct dirent **d); + static int gssd_get_single_krb5_cred(krb5_context context, +- krb5_keytab kt, struct gssd_k5_kt_princ *ple, int force_renew); ++ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int force_renew, ++ krb5_ccache ccache); + static int query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm); + +@@ -395,21 +396,14 @@ static int + gssd_get_single_krb5_cred(krb5_context context, + krb5_keytab kt, + struct gssd_k5_kt_princ *ple, +- int force_renew) ++ int force_renew, ++ krb5_ccache ccache) + { +-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS +- krb5_get_init_creds_opt *init_opts = NULL; +-#else +- krb5_get_init_creds_opt options; +-#endif +- krb5_get_init_creds_opt *opts; ++ krb5_get_init_creds_opt *opts = NULL; + krb5_creds my_creds; +- krb5_ccache ccache = NULL; + char kt_name[BUFSIZ]; +- char cc_name[BUFSIZ]; + int code; + time_t now = time(0); +- char *cache_type; + char *pname = NULL; + char *k5err = NULL; + int nocache = 0; +@@ -443,35 +437,33 @@ gssd_get_single_krb5_cred(krb5_context context, + if ((krb5_unparse_name(context, ple->princ, &pname))) + pname = NULL; + +-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS +- code = krb5_get_init_creds_opt_alloc(context, &init_opts); ++ code = krb5_get_init_creds_opt_alloc(context, &opts); + if (code) { + k5err = gssd_k5_err_msg(context, code); + printerr(0, "ERROR: %s allocating gic options\n", k5err); + goto out; + } +- if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1)) ++#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS ++ if (krb5_get_init_creds_opt_set_addressless(context, opts, 1)) + printerr(1, "WARNING: Unable to set option for addressless " + "tickets. May have problems behind a NAT.\n"); +-#ifdef TEST_SHORT_LIFETIME +- /* set a short lifetime (for debugging only!) */ +- printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n"); +- krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60); ++#else ++ krb5_get_init_creds_opt_set_address_list(opts, NULL); + #endif +- opts = init_opts; +- +-#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */ +- +- krb5_get_init_creds_opt_init(&options); +- krb5_get_init_creds_opt_set_address_list(&options, NULL); + #ifdef TEST_SHORT_LIFETIME + /* set a short lifetime (for debugging only!) */ +- printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); +- krb5_get_init_creds_opt_set_tkt_life(&options, 5*60); +-#endif +- opts = &options; ++ printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n"); ++ krb5_get_init_creds_opt_set_tkt_life(opts, 5*60); + #endif + ++ if ((code = krb5_get_init_creds_opt_set_out_ccache(context, opts, ++ ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(1, "WARNING: %s while initializing ccache for " ++ "principal '%s' using keytab '%s'\n", k5err, ++ pname ? pname : "", kt_name); ++ goto out; ++ } + if ((code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ, + kt, 0, NULL, opts))) { + k5err = gssd_k5_err_msg(context, code); +@@ -481,63 +473,18 @@ gssd_get_single_krb5_cred(krb5_context context, + goto out; + } + +- /* +- * Initialize cache file which we're going to be using +- */ +- + pthread_mutex_lock(&ple_lock); +- if (use_memcache) +- cache_type = "MEMORY"; +- else +- cache_type = "FILE"; +- snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s", +- cache_type, +- ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, +- GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); + ple->endtime = my_creds.times.endtime; +- if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) { +- free(ple->ccname); +- ple->ccname = strdup(cc_name); +- if (ple->ccname == NULL) { +- printerr(0, "ERROR: no storage to duplicate credentials " +- "cache name '%s'\n", cc_name); +- code = ENOMEM; +- pthread_mutex_unlock(&ple_lock); +- goto out; +- } +- } + pthread_mutex_unlock(&ple_lock); +- if ((code = krb5_cc_resolve(context, cc_name, &ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "ERROR: %s while opening credential cache '%s'\n", +- k5err, cc_name); +- goto out; +- } +- if ((code = krb5_cc_initialize(context, ccache, ple->princ))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "ERROR: %s while initializing credential " +- "cache '%s'\n", k5err, cc_name); +- goto out; +- } +- if ((code = krb5_cc_store_cred(context, ccache, &my_creds))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "ERROR: %s while storing credentials in '%s'\n", +- k5err, cc_name); +- goto out; +- } + + code = 0; +- printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n", +- __func__, tid, pname, cc_name); ++ printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n", ++ __func__, tid, pname, ple->ccname); + out: +-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS +- if (init_opts) +- krb5_get_init_creds_opt_free(context, init_opts); +-#endif ++ if (opts) ++ krb5_get_init_creds_opt_free(context, opts); + if (pname) + k5_free_unparsed_name(context, pname); +- if (ccache) +- krb5_cc_close(context, ccache); + krb5_free_cred_contents(context, &my_creds); + free(k5err); + return (code); +@@ -1164,10 +1111,12 @@ gssd_refresh_krb5_machine_credential_internal(char *hostname, + { + krb5_error_code code = 0; + krb5_context context; +- krb5_keytab kt = NULL;; ++ krb5_keytab kt = NULL; ++ krb5_ccache ccache = NULL; + int retval = 0; +- char *k5err = NULL; ++ char *k5err = NULL, *cache_type; + const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; ++ char cc_name[BUFSIZ]; + + /* + * If a specific service name was specified, use it. +@@ -1226,7 +1175,38 @@ gssd_refresh_krb5_machine_credential_internal(char *hostname, + goto out_free_kt; + } + } +- retval = gssd_get_single_krb5_cred(context, kt, ple, force_renew); ++ ++ if (use_memcache) ++ cache_type = "MEMORY"; ++ else ++ cache_type = "FILE"; ++ snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s", ++ cache_type, ++ ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, ++ GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); ++ ++ pthread_mutex_lock(&ple_lock); ++ if (ple->ccname == NULL || strcmp(ple->ccname, cc_name) != 0) { ++ free(ple->ccname); ++ ple->ccname = strdup(cc_name); ++ if (ple->ccname == NULL) { ++ printerr(0, "ERROR: no storage to duplicate credentials " ++ "cache name '%s'\n", cc_name); ++ code = ENOMEM; ++ pthread_mutex_unlock(&ple_lock); ++ goto out_free_kt; ++ } ++ } ++ pthread_mutex_unlock(&ple_lock); ++ if ((code = krb5_cc_resolve(context, cc_name, &ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "ERROR: %s while opening credential cache '%s'\n", ++ k5err, cc_name); ++ goto out_free_kt; ++ } ++ ++ retval = gssd_get_single_krb5_cred(context, kt, ple, force_renew, ccache); ++ krb5_cc_close(context, ccache); + out_free_kt: + krb5_kt_close(context, kt); + out_free_context: diff --git a/SOURCES/nfs-utils-2.5.4-gssd-man-gssproxy.patch b/SOURCES/nfs-utils-2.5.4-gssd-man-gssproxy.patch new file mode 100644 index 0000000..98b10dd --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-gssd-man-gssproxy.patch @@ -0,0 +1,41 @@ +commit 7511a77fc7eb7bd3ae38fcf54d49a47c25c3ed50 +Author: Scott Mayhew +Date: Mon Mar 24 08:59:24 2025 -0400 + + gssd.man: add documentation for use-gss-proxy nfs.conf option + + Signed-off-by: Scott Mayhew + Signed-off-by: Steve Dickson + +diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man +index c735eff6..4a75b056 100644 +--- a/utils/gssd/gssd.man ++++ b/utils/gssd/gssd.man +@@ -392,6 +392,17 @@ Setting to + is equivalent to providing the + .B -H + flag. ++.TP ++.B use-gss-proxy ++Setting this to 1 allows ++.BR gssproxy (8) ++to intercept GSSAPI calls and service them on behalf of ++.BR rpc.gssd , ++enabling certain features such as keytab-based client initiation. ++Note that this is unrelated to the functionality that ++.BR gssproxy (8) ++provides on behalf of the NFS server. For more information, see ++.BR https://github.com/gssapi/gssproxy/blob/main/docs/NFS.md#nfs-client . + .P + In addtion, the following value is recognized from the + .B [general] +@@ -405,7 +416,8 @@ Equivalent to + .BR rpc.svcgssd (8), + .BR kerberos (1), + .BR kinit (1), +-.BR krb5.conf (5) ++.BR krb5.conf (5), ++.BR gssproxy (8) + .SH AUTHORS + .br + Dug Song 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-nfsd-64conns.patch b/SOURCES/nfs-utils-2.5.4-nfsd-64conns.patch new file mode 100644 index 0000000..fd1979a --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-nfsd-64conns.patch @@ -0,0 +1,34 @@ +commit 0470fd526cb15ace1241f8c60fb98fedef74307b +Author: Trond Myklebust +Date: Fri May 10 09:09:28 2024 -0400 + + nfsd: allow more than 64 backlogged connections + + When creating a listener socket to be handed to /proc/fs/nfsd/portlist, + we currently limit the number of backlogged connections to 64. Since + that value was chosen in 2006, the scale at which data centres operate + has changed significantly. Given a modern server with many thousands of + clients, a limit of 64 connections can create bottlenecks, particularly + at at boot time. + Let's use the POSIX-sanctioned maximum value of SOMAXCONN. + + Reviewed-by: Jeff Layton + Acked-by: Chuck Lever + Tested-by: Chen Hanxiao + Signed-off-by: Trond Myklebust + Signed-off-by: Steve Dickson + +diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c +index 46452d97..9650cece 100644 +--- a/utils/nfsd/nfssvc.c ++++ b/utils/nfsd/nfssvc.c +@@ -205,7 +205,8 @@ nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port) + rc = errno; + goto error; + } +- if (addr->ai_protocol == IPPROTO_TCP && listen(sockfd, 64)) { ++ if (addr->ai_protocol == IPPROTO_TCP && ++ listen(sockfd, SOMAXCONN)) { + xlog(L_ERROR, "unable to create listening socket: " + "errno %d (%m)", errno); + rc = errno; 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-rdirplus-man.patch b/SOURCES/nfs-utils-2.5.4-rdirplus-man.patch new file mode 100644 index 0000000..792f63e --- /dev/null +++ b/SOURCES/nfs-utils-2.5.4-rdirplus-man.patch @@ -0,0 +1,25 @@ +diff -up nfs-utils-2.5.4/utils/mount/nfs.man.orig nfs-utils-2.5.4/utils/mount/nfs.man +--- nfs-utils-2.5.4/utils/mount/nfs.man.orig 2025-04-14 11:05:21.218916553 -0400 ++++ nfs-utils-2.5.4/utils/mount/nfs.man 2025-04-14 11:06:50.822741625 -0400 +@@ -430,11 +430,16 @@ created network transport, the client wi + list of available transports for that RPC client. + .TP 1.5i + .BR rdirplus " / " nordirplus +-Selects whether to use NFS v3 or v4 READDIRPLUS requests. +-If this option is not specified, the NFS client uses READDIRPLUS requests +-on NFS v3 or v4 mounts to read small directories. +-Some applications perform better if the client uses only READDIR requests +-for all directories. ++Selects whether to use NFS v3 or v4 READDIRPLUS requests. If this option is ++not specified, the NFS client uses a heuristic to optimize performance by ++choosing READDIR vs READDIRPLUS based on how often the calling process uses ++the additional attributes returned from READDIRPLUS. Some applications ++perform better if the client uses only READDIR requests for all directories. ++.TP 1.5i ++.BR rdirplus={none|force} ++If set to "force", the NFS client always attempts to use READDIRPLUS ++requests. If set to "none", the behavior is the same as ++.B nordirplus. + .TP 1.5i + .BI retry= n + The number of minutes that the 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 50cd685..8ca02ea 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}.1 +Release: 38%{?dist} Epoch: 1 # group all 32bit related archs @@ -66,9 +66,22 @@ Patch026: nfs-utils-2.5.4-gssd-allowed-enctypes.patch Patch027: nfs-utils-2.5.4-gssd-segfault.patch # -# RHEL9.5-z +# RHEL9.6 # -Patch028: nfs-utils-2.5.4-conffile-argument.patch +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 + +# +# RHEL9.7 +# +Patch033: nfs-utils-2.5.4-rdirplus-man.patch +Patch034: nfs-utils-2.5.4-nfsd-64conns.patch +Patch035: nfs-utils-2.5.4-nfsiostat-fixes.patch +Patch036: nfs-utils-2.5.4-gssd-man-gssproxy.patch +Patch037: nfs-utils-2.5.4-gssd-dup-cachecreds.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -325,6 +338,11 @@ fi if [ $1 -eq 0 ]; then %systemd_preun nfs-client.target %systemd_preun nfs-server.service + %systemd_preun auth-rpcgss-module.service + %systemd_preun nfs-blkmap.service + %systemd_preun rpc-gssd.service + %systemd_preun rpc-statd-notify.service + %systemd_preun var-lib-nfs-rpc_pipefs.mount fi %preun -n nfsv4-client-utils @@ -332,7 +350,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 @@ -365,9 +382,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 @@ -425,7 +442,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 @@ -466,6 +483,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 @@ -510,8 +528,51 @@ fi %{_mandir}/*/nfsiostat.8.gz %changelog -* Mon Mar 10 2025 Scott Mayhew - 2.5.4-27.1 -- conffile: add 'arg' argument to conf_remove_now() (RHEL-82881) +* Thu Jun 12 2025 Scott Mayhew 2.5.4-38 +- ensure services are stopped when nfs-utils is uninstalled (RHEL-88422) + +* Sat Apr 26 2025 Steve Dickson 2.5.4-37 +- gssd.man: add documentation for use-gss-proxy nfs.conf option (RHEL-85408) +- gssd: do not use krb5_cc_initialize (RHEL-85412) + +* Fri Apr 25 2025 Steve Dickson 2.5.4-36 +- mountstats: verify that old and new types are the same (RHEL-88553) + +* Tue Apr 22 2025 Steve Dickson 2.5.4-35 +- nfs(5): Add new rdirplus functionality, clarify (RHEL-87143) +- nfsd: allow more than 64 backlogged connections (RHEL-87752) + +* 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)