diff --git a/dist b/dist index 9c0e36ec42a2d9bfefacb21ac6354c9ddd910533..1fe92cf0fdf9c2625d878a2ace258f64c1e8ca44 100644 --- a/dist +++ b/dist @@ -1 +1 @@ -an8 +an8_10 diff --git a/download b/download index 529c0bc5ff44f1cf113135555d9613d6d4a27a6a..a0ac25eb22b8e2ecc27647c3dbfe54e0b35bd044 100644 --- a/download +++ b/download @@ -1 +1 @@ -11490e3f85a8676c647fe1bddbf32f99 nfs-utils-2.3.3.tar.xz +b6c9c032995af1c08fea9fbcc1ce33e9 nfs-utils-2.3.3.tar.xz diff --git a/nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch b/nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch new file mode 100644 index 0000000000000000000000000000000000000000..2a7d21708f0ee6bcd479cfdcd5e669536424b2e2 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch @@ -0,0 +1,208 @@ +From ad4eafccd244e87af315c432d076b4f988dde52a Mon Sep 17 00:00:00 2001 +From: Olga Kornievskaia +Date: Mon, 24 Mar 2025 08:43:43 -0400 +Subject: [PATCH 2/2] gssd: do not use krb5_cc_initialize + +Note: This patch differs from the upstream version in several places +because RHEL 8 does not have c8659457 ("gssd: We never use the nocache +param of gssd_check_if_cc_exists()") or f066f87b ("gssd: enable forcing +cred renewal using the keytab"). + +Original commit message: + +When gssd refreshes machine credentials, it uses the +krb5_get_init_creds_keytab() and then to save the received credentials +in a ticket cache, it proceeds to initialize the credential cache via +a krb5_cc_initialize() before storing the received credentials into it. + +krb5_cc_initialize() is not concurrency safe. two gssd upcalls by +uid=0, one for krb5i auth flavor and another for krb5p, would enter +into krb5_cc_initialize() and one of them would fail, leading to +an upcall failure and NFS operation error. + +Instead it was proposed that gssd changes its design to do what +kinit does and forgo the use of krb5_cc_initialize and instead setup +the output cache via krb5_get_init_creds_opt_set_out_cache() prior +to calling krb5_get_init_creds_keytab() which would then store +credentials automatically. + +https://mailman.mit.edu/pipermail/krbdev/2025-February/013708.html + +Signed-off-by: Olga Kornievskaia +Signed-off-by: Steve Dickson +(cherry picked from commit 1cd9e3c0d290646e80750249914396566dd6b800) +Signed-off-by: Scott Mayhew +--- + utils/gssd/krb5_util.c | 103 ++++++++++++++++++++--------------------- + 1 file changed, 50 insertions(+), 53 deletions(-) + +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 871add74..43bc8744 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -161,7 +161,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 nocache); ++ krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache, ++ krb5_ccache ccache); + static int query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm); + +@@ -368,16 +369,14 @@ static int + gssd_get_single_krb5_cred(krb5_context context, + krb5_keytab kt, + struct gssd_k5_kt_princ *ple, +- int nocache) ++ int nocache, ++ krb5_ccache ccache) + { + 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; + pthread_t tid = pthread_self(); +@@ -427,6 +426,14 @@ gssd_get_single_krb5_cred(krb5_context context, + 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); +@@ -436,61 +443,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: + 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); + krb5_free_string(context, k5err); + return (code); +@@ -1108,10 +1072,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. +@@ -1170,7 +1136,38 @@ gssd_refresh_krb5_machine_credential_internal(char *hostname, + goto out_free_kt; + } + } +- retval = gssd_get_single_krb5_cred(context, kt, ple, 0); ++ ++ 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, 0, ccache); ++ krb5_cc_close(context, ccache); + out_free_kt: + krb5_kt_close(context, kt); + out_free_context: +-- +2.43.0 + diff --git a/nfs-utils-2.3.3-gssd-man-document-use-gss-proxy.patch b/nfs-utils-2.3.3-gssd-man-document-use-gss-proxy.patch new file mode 100644 index 0000000000000000000000000000000000000000..b6f95367f6bff4078da2dc4d77095fea71174458 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-man-document-use-gss-proxy.patch @@ -0,0 +1,47 @@ +From 7511a77fc7eb7bd3ae38fcf54d49a47c25c3ed50 Mon Sep 17 00:00:00 2001 +From: Scott Mayhew +Date: Mon, 24 Mar 2025 08:59:24 -0400 +Subject: [nfs-utils PATCH] gssd.man: add documentation for use-gss-proxy + nfs.conf option + +Signed-off-by: Scott Mayhew +Signed-off-by: Steve Dickson +--- + utils/gssd/gssd.man | 14 +++++++++++++- + 1 file changed, 13 insertions(+), 1 deletion(-) + +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 +-- +2.48.1 + diff --git a/nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch b/nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch new file mode 100644 index 0000000000000000000000000000000000000000..eabeda944ac0e7a9f9734533bf2c5f9424d55362 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch @@ -0,0 +1,98 @@ +From 55d9bf151b100db9bf52e8f968e33f3ae1d234f5 Mon Sep 17 00:00:00 2001 +From: Olga Kornievskaia +Date: Mon, 24 Mar 2025 08:40:32 -0400 +Subject: [PATCH 1/2] gssd: unconditionally use krb5_get_init_creds_opt_alloc + +Note: This patch has a context difference from the upstream version +because RHEL 8 does not have c8659457 ("gssd: We never use the nocache +param of gssd_check_if_cc_exists()") or f066f87b ("gssd: enable forcing +cred renewal using the keytab"). + +Original commit message: + +Modern kerberos API uses krb5_get_init_creds_opt_alloc() for managing +its options for credential data structure. + +Signed-off-by: Olga Kornievskaia +Signed-off-by: Steve Dickson +(cherry picked from commit 9b3f949331c6541a358fc28bac323533f94d7e0b) +Signed-off-by: Scott Mayhew +--- + utils/gssd/krb5_util.c | 37 ++++++++++--------------------------- + 1 file changed, 10 insertions(+), 27 deletions(-) + +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index a1a77a2f..871add74 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -370,12 +370,7 @@ gssd_get_single_krb5_cred(krb5_context context, + struct gssd_k5_kt_princ *ple, + int nocache) + { +-#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]; +@@ -413,33 +408,23 @@ 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_keytab(context, &my_creds, ple->princ, +@@ -500,10 +485,8 @@ gssd_get_single_krb5_cred(krb5_context context, + printerr(2, "%s(0x%lx): principal '%s' ccache:'%s'\n", + __func__, tid, pname, cc_name); + 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) +-- +2.43.0 + diff --git a/nfs-utils-2.3.3-mountstats-fixes.patch b/nfs-utils-2.3.3-mountstats-fixes.patch new file mode 100644 index 0000000000000000000000000000000000000000..875dd693937afe99f4abe74b60c8ff13d82424ca --- /dev/null +++ b/nfs-utils-2.3.3-mountstats-fixes.patch @@ -0,0 +1,141 @@ +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/nfs-utils-2.3.3-nfs-man-rdirplus.patch b/nfs-utils-2.3.3-nfs-man-rdirplus.patch new file mode 100644 index 0000000000000000000000000000000000000000..e2178900e4a3516118a28dbe2478de2d3fbd2568 --- /dev/null +++ b/nfs-utils-2.3.3-nfs-man-rdirplus.patch @@ -0,0 +1,47 @@ +From 3cca9dbeab3b159c27c1dc48e791139744baf6d0 Mon Sep 17 00:00:00 2001 +From: Benjamin Coddington +Date: Mon, 24 Mar 2025 15:47:43 -0400 +Subject: [nfs-utils PATCH] nfs(5): Add new rdirplus functionality, clarify + +The proposed kernel [patch][1] will modify the rdirplus mount option to +accept optional string values of "none" and "force". Update the man page +to reflect these changes and clarify the current client's behavior for the +default. + +[1]: https://lore.kernel.org/linux-nfs/8c33cd92be52255b0dd0a7489c9e5cc35434ec95.1741876784.git.bcodding@redhat.com/T/#u + +Signed-off-by: Benjamin Coddington +Signed-off-by: Steve Dickson +--- + utils/mount/nfs.man | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man +index b5c5913b..94dc29d4 100644 +--- a/utils/mount/nfs.man ++++ b/utils/mount/nfs.man +@@ -434,11 +434,16 @@ option may also be used by some pNFS drivers to decide how many + connections to set up to the data servers. + .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 +-- +2.48.1 + diff --git a/nfs-utils-2.3.3-nfsiostat-fixes.patch b/nfs-utils-2.3.3-nfsiostat-fixes.patch new file mode 100644 index 0000000000000000000000000000000000000000..afbebf805fcde234d9e602d7f984ac4ffc7332f8 --- /dev/null +++ b/nfs-utils-2.3.3-nfsiostat-fixes.patch @@ -0,0 +1,242 @@ +26f8410d mountstats/nfsiostat: merge and rework the infinite and counted loops +21238e5c mountstats/nfsiostat: Move the checks for empty mountpoint list into the print function +1bd30a9d nfsiostat: make comment explain mount/unmount more broadly +2b9251ed nfsiostat: fix crash when filtering mountstats after unmount +9836adbc nfsiostat: mirror how mountstats iostat prints the stats +936a19cc mountstats/nfsiostat: add a function to return the fstype +155c5343 nfsiostat: skip argv[0] when parsing command-line +39f57998 nfsiostat/mountstats: handle KeyError in compare_iostats() +c4c14011 nfsiostat: replace 'list' reserved word + +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index 7cbe543c..dec0e861 100644 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -493,20 +493,20 @@ def list_nfs_mounts(givenlist, mountstats): + return a full list if the given list is empty - + may return an empty list if none found + """ +- list = [] ++ devicelist = [] + if len(givenlist) > 0: + for device in givenlist: + stats = DeviceData() + stats.parse_stats(mountstats[device]) + if stats.is_nfs_mountpoint(): +- list += [device] ++ devicelist += [device] + else: + for device, descr in mountstats.items(): + stats = DeviceData() + stats.parse_stats(descr) + if stats.is_nfs_mountpoint(): +- list += [device] +- return list ++ devicelist += [device] ++ return devicelist + + def iostat_command(name): + """iostat-like command for NFS mount points +diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 014f38a3..1054f698 100755 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -560,7 +560,10 @@ class DeviceData: + # the reference to them. so we build new lists here + # for the result object. + for op in result.__rpc_data['ops']: +- result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ try: ++ result.__rpc_data[op] = list(map(difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ except KeyError: ++ continue + + # update the remaining keys + if protocol == 'udp': +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +index b7e98a2a..5556f692 100755 +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -213,8 +213,11 @@ class DeviceData: + # the reference to them. so we build new lists here + # for the result object. + for op in result.__rpc_data['ops']: +- result.__rpc_data[op] = list(map( +- difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ try: ++ result.__rpc_data[op] = list(map( ++ difference, self.__rpc_data[op], old_stats.__rpc_data[op])) ++ except KeyError: ++ continue + + # update the remaining keys we care about + result.__rpc_data['rpcsends'] -= old_stats.__rpc_data['rpcsends'] +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/nfs-utils.spec b/nfs-utils.spec index 748251c1456e48c7af963a179e555a3f63cfce29..669cc15adb9d1b9e31b09c731ac8ebd495bb9b15 100644 --- a/nfs-utils.spec +++ b/nfs-utils.spec @@ -1,10 +1,10 @@ -%define anolis_release .0.4 +%define anolis_release .0.1 Summary: NFS utilities and supporting clients and daemons for the kernel NFS server Name: nfs-utils URL: http://linux-nfs.org/ Version: 2.3.3 -Release: 59%{anolis_release}%{?dist}%{?lifsea_dist} +Release: 64%{anolis_release}%{?dist}%{?lifsea_dist} Epoch: 1 # group all 32bit related archs @@ -113,6 +113,16 @@ Patch055: nfs-utils-2.3.3-systemd-rpcstatd.patch Patch056: nfs-utils-2.3.3-mountd-v4clnts.patch Patch057: nfs-utils-2.3.3-covscan-return-value.patch +# +# RHEL 8.10.z +# +Patch058: nfs-utils-2.3.3-gssd-man-document-use-gss-proxy.patch +Patch059: nfs-utils-2.3.3-gssd-unconditionally-use-krb5_get_init_creds_opt_all.patch +Patch060: nfs-utils-2.3.3-gssd-do-not-use-krb5_cc_initialize.patch +Patch061: nfs-utils-2.3.3-nfsiostat-fixes.patch +Patch062: nfs-utils-2.3.3-mountstats-fixes.patch +Patch063: nfs-utils-2.3.3-nfs-man-rdirplus.patch + Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch Patch102: nfs-utils-2.3.3-idmap-errmsg.patch @@ -431,21 +441,31 @@ fi %{_libdir}/libnfsidmap.so %changelog -* Tue Jun 25 2024 Weisson 2.3.3-59.0.4 -- more carefully detect availability of res_querydomain(3). - -* Mon Jan 22 2024 yuanhui 2.3.3-59.0.3 -- LifseaOS: Remove Python dependencies and gssproxy package - -* Thu Jun 29 2023 Ferry Meng 2.3.3-59.0.2 -- Fix NFSv4 export of tmpfs filesystems -- move estab/rmtab into libraries for compiling - -* Thu May 25 2023 Weitao Zhou 2.3.3-59.0.1 +* Mon Sep 15 2025 Weitao Zhou 2.3.3-64.0.1 - use extern in header files when declaring global variables for compatible gcc10 build - allow compilation to succeed with -fno-common - Add libuuid-devel buildrequires (wb-zh951434@alibaba-inc.com) - Disable v4client on loongarch platform (liwei.glw@alibaba-inc.com) +- Fix NFSv4 export of tmpfs filesystems +- move estab/rmtab into libraries for compiling +- LifseaOS: Remove Python dependencies and gssproxy package +- more carefully detect availability of res_querydomain(3). + +* Tue May 20 2025 Scott Mayhew 2.3.3-64 +- update rdirplus documentation on nfs(5) man page (RHEL-91253) + +* Fri May 9 2025 Scott Mayhew 2.3.3-63 +- mountstats fixes (RHEL-90242) + +* Thu May 8 2025 Scott Mayhew 2.3.3-62 +- nfsiostat fixes (RHEL-90242) + +* Mon Apr 28 2025 Scott Mayhew 2.3.3-61 +- gssd: unconditionally use krb5_get_init_creds_opt_alloc (RHEL-62422) +- gssd: do not use krb5_cc_initialize (RHEL-62422) + +* Tue Apr 15 2025 Scott Mayhew 2.3.3-60 +- gssd.man: add documentation for use-gss-proxy (RHEL-13085) * Thu Jan 12 2023 Steve Dickson 2.3.3-59 - Covscan Scan: Wrong Check of Return Value (bz 2151966)