From ef8a36e2f0eca21b29ad43b69b8b5de16e5f8a61 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Sat, 2 Jul 2022 00:13:01 +0000 Subject: [PATCH] Auto sync2gitlab import of nfs-utils-2.3.3-56.el8.src.rpm --- .gitignore | 1 + 24-nfs-server.conf | 7 + EMPTY | 1 - id_resolver.conf | 9 + lockd.conf | 23 + nfs-convert.service | 24 + nfs-utils-1.2.1-exp-subtree-warn-off.patch | 12 + nfs-utils-1.2.1-statdpath-man.patch | 58 + ...utils-2.3.1-systemd-gssproxy-restart.patch | 11 + nfs-utils-2.3.1-systemd-svcgssd-removed.patch | 65 + nfs-utils-2.3.3-covscan-resource-leaks.patch | 597 +++ ...tils-2.3.3-covscan-rm-deadcode-leaks.patch | 27 + nfs-utils-2.3.3-exportfs-man-labels.patch | 50 + nfs-utils-2.3.3-exportfs-root.patch | 43 + ...utils-2.3.3-exports-manpage-outdated.patch | 30 + nfs-utils-2.3.3-gssd-debug-cleanup.patch | 290 ++ nfs-utils-2.3.3-gssd-early-daemon.patch | 17 + nfs-utils-2.3.3-gssd-failed-thread.patch | 402 ++ nfs-utils-2.3.3-gssd-k5identity.patch | 141 + nfs-utils-2.3.3-gssd-man-tflag.patch | 17 + nfs-utils-2.3.3-gssd-man-verbose.patch | 29 + nfs-utils-2.3.3-gssd-memoryleak.patch | 118 + ...utils-2.3.3-gssd-multithread-updates.patch | 1009 ++++ nfs-utils-2.3.3-gssd-mutex-refcnt.patch | 43 + nfs-utils-2.3.3-gssd-printerr.patch | 14 + nfs-utils-2.3.3-gssd-timeout-thread.patch | 625 +++ nfs-utils-2.3.3-gssd-usegssproxy.patch | 70 + nfs-utils-2.3.3-gssd-verbose.patch | 52 + nfs-utils-2.3.3-idmap-errmsg.patch | 12 + nfs-utils-2.3.3-junction-err-fix.patch | 57 + nfs-utils-2.3.3-junction-update.patch | 162 + nfs-utils-2.3.3-lseek-error-handling.patch | 49 + nfs-utils-2.3.3-man-tcpwrappers.patch | 89 + nfs-utils-2.3.3-memleak-on-error.patch | 37 + nfs-utils-2.3.3-mount-ebusy.patch | 14 + nfs-utils-2.3.3-mount-fallback.patch | 48 + nfs-utils-2.3.3-mount-sharecache.patch | 47 + nfs-utils-2.3.3-mount-sloppy.patch | 116 + nfs-utils-2.3.3-mountd-memleak.patch | 77 + nfs-utils-2.3.3-mountd-pseudofs.patch | 61 + nfs-utils-2.3.3-mountd-v4-logging.patch | 976 ++++ nfs-utils-2.3.3-nconnect-manpage.patch | 40 + nfs-utils-2.3.3-nfsclnts-cmd.patch | 481 ++ nfs-utils-2.3.3-nfsconf-inplace.patch | 276 ++ nfs-utils-2.3.3-nfsconf-manage-gids.patch | 23 + nfs-utils-2.3.3-nfsconf-rdmaport.patch | 73 + nfs-utils-2.3.3-nfsconf-usegssproxy.patch | 82 + nfs-utils-2.3.3-nfsd-disable-v4.patch | 104 + nfs-utils-2.3.3-nfsdcld-upstream-update.patch | 4048 +++++++++++++++++ ...utils-2.3.3-nfsdclddb-manpage-rename.patch | 29 + nfs-utils-2.3.3-nfsdclddb-rename.patch | 130 + nfs-utils-2.3.3-nfsidmap-debug.patch | 27 + nfs-utils-2.3.3-nfsiostat-div-zero.patch | 12 + nfs-utils-2.3.3-nfsiostat-err-cnts.patch | 159 + nfs-utils-2.3.3-nfsiostat-key-error.patch | 37 + nfs-utils-2.3.3-nfsman-softreval.patch | 53 + nfs-utils-2.3.3-nfsman-typo.patch | 22 + nfs-utils-2.3.3-nfsrahead.patch | 399 ++ nfs-utils-2.3.3-remove-osd_login.patch | 188 + nfs-utils-2.3.3-rpcctl-posixpath.patch | 12 + nfs-utils-2.3.3-rpcctl-subparser.patch | 34 + nfs-utils-2.3.3-rpcctl.patch | 384 ++ nfs-utils-2.3.3-statd-force.patch | 73 + nfs-utils-2.3.3-statd-no-notify.patch | 105 + nfs-utils-2.3.3-systemd-exportfs-nofail.patch | 37 + nfs-utils.spec | 2187 +++++++++ nfsconvert.py | 324 ++ nfsconvert.sh | 38 + sources | 1 + 69 files changed, 14907 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 24-nfs-server.conf delete mode 100644 EMPTY create mode 100644 id_resolver.conf create mode 100644 lockd.conf create mode 100644 nfs-convert.service create mode 100644 nfs-utils-1.2.1-exp-subtree-warn-off.patch create mode 100644 nfs-utils-1.2.1-statdpath-man.patch create mode 100644 nfs-utils-2.3.1-systemd-gssproxy-restart.patch create mode 100644 nfs-utils-2.3.1-systemd-svcgssd-removed.patch create mode 100644 nfs-utils-2.3.3-covscan-resource-leaks.patch create mode 100644 nfs-utils-2.3.3-covscan-rm-deadcode-leaks.patch create mode 100644 nfs-utils-2.3.3-exportfs-man-labels.patch create mode 100644 nfs-utils-2.3.3-exportfs-root.patch create mode 100644 nfs-utils-2.3.3-exports-manpage-outdated.patch create mode 100644 nfs-utils-2.3.3-gssd-debug-cleanup.patch create mode 100644 nfs-utils-2.3.3-gssd-early-daemon.patch create mode 100644 nfs-utils-2.3.3-gssd-failed-thread.patch create mode 100644 nfs-utils-2.3.3-gssd-k5identity.patch create mode 100644 nfs-utils-2.3.3-gssd-man-tflag.patch create mode 100644 nfs-utils-2.3.3-gssd-man-verbose.patch create mode 100644 nfs-utils-2.3.3-gssd-memoryleak.patch create mode 100644 nfs-utils-2.3.3-gssd-multithread-updates.patch create mode 100644 nfs-utils-2.3.3-gssd-mutex-refcnt.patch create mode 100644 nfs-utils-2.3.3-gssd-printerr.patch create mode 100644 nfs-utils-2.3.3-gssd-timeout-thread.patch create mode 100644 nfs-utils-2.3.3-gssd-usegssproxy.patch create mode 100644 nfs-utils-2.3.3-gssd-verbose.patch create mode 100644 nfs-utils-2.3.3-idmap-errmsg.patch create mode 100644 nfs-utils-2.3.3-junction-err-fix.patch create mode 100644 nfs-utils-2.3.3-junction-update.patch create mode 100644 nfs-utils-2.3.3-lseek-error-handling.patch create mode 100644 nfs-utils-2.3.3-man-tcpwrappers.patch create mode 100644 nfs-utils-2.3.3-memleak-on-error.patch create mode 100644 nfs-utils-2.3.3-mount-ebusy.patch create mode 100644 nfs-utils-2.3.3-mount-fallback.patch create mode 100644 nfs-utils-2.3.3-mount-sharecache.patch create mode 100644 nfs-utils-2.3.3-mount-sloppy.patch create mode 100644 nfs-utils-2.3.3-mountd-memleak.patch create mode 100644 nfs-utils-2.3.3-mountd-pseudofs.patch create mode 100644 nfs-utils-2.3.3-mountd-v4-logging.patch create mode 100644 nfs-utils-2.3.3-nconnect-manpage.patch create mode 100644 nfs-utils-2.3.3-nfsclnts-cmd.patch create mode 100644 nfs-utils-2.3.3-nfsconf-inplace.patch create mode 100644 nfs-utils-2.3.3-nfsconf-manage-gids.patch create mode 100644 nfs-utils-2.3.3-nfsconf-rdmaport.patch create mode 100644 nfs-utils-2.3.3-nfsconf-usegssproxy.patch create mode 100644 nfs-utils-2.3.3-nfsd-disable-v4.patch create mode 100644 nfs-utils-2.3.3-nfsdcld-upstream-update.patch create mode 100644 nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch create mode 100644 nfs-utils-2.3.3-nfsdclddb-rename.patch create mode 100644 nfs-utils-2.3.3-nfsidmap-debug.patch create mode 100644 nfs-utils-2.3.3-nfsiostat-div-zero.patch create mode 100644 nfs-utils-2.3.3-nfsiostat-err-cnts.patch create mode 100644 nfs-utils-2.3.3-nfsiostat-key-error.patch create mode 100644 nfs-utils-2.3.3-nfsman-softreval.patch create mode 100644 nfs-utils-2.3.3-nfsman-typo.patch create mode 100644 nfs-utils-2.3.3-nfsrahead.patch create mode 100644 nfs-utils-2.3.3-remove-osd_login.patch create mode 100644 nfs-utils-2.3.3-rpcctl-posixpath.patch create mode 100644 nfs-utils-2.3.3-rpcctl-subparser.patch create mode 100644 nfs-utils-2.3.3-rpcctl.patch create mode 100644 nfs-utils-2.3.3-statd-force.patch create mode 100644 nfs-utils-2.3.3-statd-no-notify.patch create mode 100644 nfs-utils-2.3.3-systemd-exportfs-nofail.patch create mode 100644 nfs-utils.spec create mode 100644 nfsconvert.py create mode 100644 nfsconvert.sh create mode 100644 sources diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b79139 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/nfs-utils-2.3.3.tar.xz diff --git a/24-nfs-server.conf b/24-nfs-server.conf new file mode 100644 index 0000000..5011e92 --- /dev/null +++ b/24-nfs-server.conf @@ -0,0 +1,7 @@ +[service/nfs-server] + mechs = krb5 + socket = /run/gssproxy.sock + cred_store = keytab:/etc/krb5.keytab + trusted = yes + kernel_nfsd = yes + euid = 0 diff --git a/EMPTY b/EMPTY deleted file mode 100644 index 0519ecb..0000000 --- a/EMPTY +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/id_resolver.conf b/id_resolver.conf new file mode 100644 index 0000000..67ccb18 --- /dev/null +++ b/id_resolver.conf @@ -0,0 +1,9 @@ +# +# nfsidmap(5) - The NFS idmapper upcall program +# Summary: Used by NFSv4 to map user/group ids into +# user/group names and names into in ids +# Options: +# -v Increases the verbosity of the output to syslog +# -t timeout Set the expiration timer, in seconds, on the key +# +create id_resolver * * /usr/sbin/nfsidmap %k %d diff --git a/lockd.conf b/lockd.conf new file mode 100644 index 0000000..f672d12 --- /dev/null +++ b/lockd.conf @@ -0,0 +1,23 @@ +# +# Set the NFS lock manager grace period. n is measured in seconds. +#options lockd nlm_grace_period=90 +# +# Set the TCP port that the NFS lock manager should use. +# port must be a valid TCP port value (1-65535). +#options lockd nlm_tcpport +# +# Set the UDP port that the NFS lock manager should use. +# port must be a valid UDP port value (1-65535). +#options lockd nlm_udpport +# +# Set the maximum number of outstanding connections +#options lockd nlm_max_connections=1024 +# +# Set the default time value for the NFS lock manager +# in seconds. Default is 10 secs (min 3 max 20) +#options lockd nlm_timeout=10 +# +# Choose whether to record the caller_name or IP address +# this peer in the local rpc.statd's database. +#options lockd nsm_use_hostnames=0 + diff --git a/nfs-convert.service b/nfs-convert.service new file mode 100644 index 0000000..6720192 --- /dev/null +++ b/nfs-convert.service @@ -0,0 +1,24 @@ +[Unit] +Description=Preprocess NFS configuration convertion +DefaultDependencies=no + +Before=nfs-server.service nfs-mountd.service nfs-idmapd.service +Before=nfs-blkmap.service rpc-statd.service rpc-gssd.service +Before=rpc-statd-notify.service + +After=initrd-root-fs.target + +ConditionPathExists=/etc/sysconfig/nfs + +[Service] +Type=oneshot +ExecStart=/usr/libexec/nfs-utils/nfsconvert.sh + +[Install] +RequiredBy=nfs-server.service +RequiredBy=nfs-mountd.service +RequiredBy=nfs-idmapd.service +RequiredBy=nfs-blkmap.service +RequiredBy=rpc-statd.service +RequiredBy=rpc-gssd.service +RequiredBy=rpc-statd-notify.service diff --git a/nfs-utils-1.2.1-exp-subtree-warn-off.patch b/nfs-utils-1.2.1-exp-subtree-warn-off.patch new file mode 100644 index 0000000..7708750 --- /dev/null +++ b/nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-2.1.1/support/nfs/exports.c.orig nfs-utils-2.1.1/support/nfs/exports.c +--- nfs-utils-2.1.1/support/nfs/exports.c.orig 2017-01-12 10:21:39.000000000 -0500 ++++ nfs-utils-2.1.1/support/nfs/exports.c 2017-04-26 12:46:24.186480312 -0400 +@@ -507,7 +507,7 @@ void fix_pseudoflavor_flags(struct expor + static int + parseopts(char *cp, struct exportent *ep, int warn, int *had_subtree_opt_ptr) + { +- int had_subtree_opt = 0; ++ int had_subtree_opt = 1; + char *flname = efname?efname:"command line"; + int flline = efp?efp->x_line:0; + unsigned int active = 0; diff --git a/nfs-utils-1.2.1-statdpath-man.patch b/nfs-utils-1.2.1-statdpath-man.patch new file mode 100644 index 0000000..986a4b8 --- /dev/null +++ b/nfs-utils-1.2.1-statdpath-man.patch @@ -0,0 +1,58 @@ +diff -up nfs-utils-2.1.1/utils/statd/sm-notify.man.orig nfs-utils-2.1.1/utils/statd/sm-notify.man +--- nfs-utils-2.1.1/utils/statd/sm-notify.man.orig 2017-04-26 12:45:14.205742654 -0400 ++++ nfs-utils-2.1.1/utils/statd/sm-notify.man 2017-04-26 12:45:44.042630801 -0400 +@@ -184,7 +184,7 @@ where NSM state information resides. + If this option is not specified, + .B sm-notify + uses +-.I /var/lib/nfs ++.I /var/lib/nfs/statd + by default. + .IP + After starting, +@@ -330,13 +330,13 @@ Currently, the + command supports sending notification only via datagram transport protocols. + .SH FILES + .TP 2.5i +-.I /var/lib/nfs/sm ++.I /var/lib/nfs/statd/sm + directory containing monitor list + .TP 2.5i +-.I /var/lib/nfs/sm.bak ++.I /var/lib/nfs/statd/sm.bak + directory containing notify list + .TP 2.5i +-.I /var/lib/nfs/state ++.I /var/lib/nfs/statd/state + NSM state number for this host + .TP 2.5i + .I /proc/sys/fs/nfs/nsm_local_state +diff -up nfs-utils-2.1.1/utils/statd/statd.man.orig nfs-utils-2.1.1/utils/statd/statd.man +--- nfs-utils-2.1.1/utils/statd/statd.man.orig 2017-01-12 10:21:39.000000000 -0500 ++++ nfs-utils-2.1.1/utils/statd/statd.man 2017-04-26 12:45:44.043630798 -0400 +@@ -253,7 +253,7 @@ where NSM state information resides. + If this option is not specified, + .B rpc.statd + uses +-.I /var/lib/nfs ++.I /var/lib/nfs/statd + by default. + .IP + After starting, +@@ -425,13 +425,13 @@ If set to a positive integer, has the sa + .IR \-\-no\-notify . + .SH FILES + .TP 2.5i +-.I /var/lib/nfs/sm ++.I /var/lib/nfs/statd/sm + directory containing monitor list + .TP 2.5i +-.I /var/lib/nfs/sm.bak ++.I /var/lib/nfs/statd/sm.bak + directory containing notify list + .TP 2.5i +-.I /var/lib/nfs/state ++.I /var/lib/nfs/statd/state + NSM state number for this host + .TP 2.5i + .I /var/run/run.statd.pid diff --git a/nfs-utils-2.3.1-systemd-gssproxy-restart.patch b/nfs-utils-2.3.1-systemd-gssproxy-restart.patch new file mode 100644 index 0000000..f298b89 --- /dev/null +++ b/nfs-utils-2.3.1-systemd-gssproxy-restart.patch @@ -0,0 +1,11 @@ +diff -up nfs-utils-2.3.3/systemd/nfs-server.service.orig nfs-utils-2.3.3/systemd/nfs-server.service +--- nfs-utils-2.3.3/systemd/nfs-server.service.orig 2020-12-11 09:05:23.499222371 -0500 ++++ nfs-utils-2.3.3/systemd/nfs-server.service 2020-12-11 09:06:38.970186395 -0500 +@@ -23,6 +23,7 @@ Type=oneshot + RemainAfterExit=yes + ExecStartPre=-/usr/sbin/exportfs -r + ExecStart=/usr/sbin/rpc.nfsd ++ExecStart=-/bin/sh -c 'if systemctl -q is-active gssproxy; then systemctl reload gssproxy ; fi' + ExecStop=/usr/sbin/rpc.nfsd 0 + ExecStopPost=/usr/sbin/exportfs -au + ExecStopPost=/usr/sbin/exportfs -f diff --git a/nfs-utils-2.3.1-systemd-svcgssd-removed.patch b/nfs-utils-2.3.1-systemd-svcgssd-removed.patch new file mode 100644 index 0000000..e3cc92f --- /dev/null +++ b/nfs-utils-2.3.1-systemd-svcgssd-removed.patch @@ -0,0 +1,65 @@ +diff -up nfs-utils-2.3.3/systemd/auth-rpcgss-module.service.orig nfs-utils-2.3.3/systemd/auth-rpcgss-module.service +--- nfs-utils-2.3.3/systemd/auth-rpcgss-module.service.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/systemd/auth-rpcgss-module.service 2018-10-17 10:42:17.682830880 -0400 +@@ -1,5 +1,5 @@ +-# We want to start gss-proxy on kernels that support it and rpc.svcgssd +-# on those that don't. Those services check for support by checking ++# We want to start gss-proxy on kernels that support it ++# Those services check for support by checking + # for existence of the path /proc/net/rpc/use-gss-proxy. Before they + # can perform that check, they need this module loaded. (Unless + # rpcsec_gss support is built directly into the kernel, in which case this +@@ -7,8 +7,8 @@ + [Unit] + Description=Kernel Module supporting RPCSEC_GSS + DefaultDependencies=no +-Before=gssproxy.service rpc-svcgssd.service rpc-gssd.service +-Wants=gssproxy.service rpc-svcgssd.service rpc-gssd.service ++Before=gssproxy.service rpc-gssd.service ++Wants=gssproxy.service rpc-gssd.service + ConditionPathExists=/etc/krb5.keytab + + [Service] +diff -up nfs-utils-2.3.3/systemd/nfs-client.target.orig nfs-utils-2.3.3/systemd/nfs-client.target +--- nfs-utils-2.3.3/systemd/nfs-client.target.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/systemd/nfs-client.target 2018-10-17 10:42:17.682830880 -0400 +@@ -9,7 +9,7 @@ Wants=rpc-statd-notify.service + + # GSS services dependencies and ordering + Wants=auth-rpcgss-module.service +-After=rpc-gssd.service rpc-svcgssd.service gssproxy.service ++After=rpc-gssd.service gssproxy.service + + [Install] + WantedBy=multi-user.target +diff -up nfs-utils-2.3.3/systemd/nfs.conf.man.orig nfs-utils-2.3.3/systemd/nfs.conf.man +--- nfs-utils-2.3.3/systemd/nfs.conf.man.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/systemd/nfs.conf.man 2018-10-17 10:42:17.682830880 -0400 +@@ -226,15 +226,6 @@ See + for details. + + .TP +-.B svcgssd +-Recognized values: +-.BR principal . +- +-See +-.BR rpc.svcgssd (8) +-for details. +- +-.TP + .B exportfs + Only + .B debug= +diff -up nfs-utils-2.3.3/systemd/nfs-server.service.orig nfs-utils-2.3.3/systemd/nfs-server.service +--- nfs-utils-2.3.3/systemd/nfs-server.service.orig 2018-10-17 10:41:24.347121069 -0400 ++++ nfs-utils-2.3.3/systemd/nfs-server.service 2018-10-17 10:42:17.683830874 -0400 +@@ -14,7 +14,7 @@ Before= rpc-statd-notify.service + + # GSS services dependencies and ordering + Wants=auth-rpcgss-module.service +-After=rpc-gssd.service gssproxy.service rpc-svcgssd.service ++After=rpc-gssd.service gssproxy.service + + [Service] + Type=oneshot diff --git a/nfs-utils-2.3.3-covscan-resource-leaks.patch b/nfs-utils-2.3.3-covscan-resource-leaks.patch new file mode 100644 index 0000000..51fdb6f --- /dev/null +++ b/nfs-utils-2.3.3-covscan-resource-leaks.patch @@ -0,0 +1,597 @@ +diff -up nfs-utils-2.3.3/configure.ac.orig nfs-utils-2.3.3/configure.ac +--- nfs-utils-2.3.3/configure.ac.orig 2019-09-18 10:57:14.190810677 -0400 ++++ nfs-utils-2.3.3/configure.ac 2019-09-18 10:57:56.715567641 -0400 +@@ -561,6 +561,7 @@ my_am_cflags="\ + -Werror=parentheses \ + -Werror=aggregate-return \ + -Werror=unused-result \ ++ -Wno-cast-function-type \ + -fno-strict-aliasing \ + " + +diff -up nfs-utils-2.3.3/support/junction/path.c.orig nfs-utils-2.3.3/support/junction/path.c +--- nfs-utils-2.3.3/support/junction/path.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/junction/path.c 2019-09-18 10:57:56.715567641 -0400 +@@ -163,8 +163,10 @@ nsdb_count_components(const char *pathna + break; + next = strchrnul(component, '/'); + tmp = (size_t)(next - component); +- if (tmp > 255) ++ if (tmp > 255) { ++ free(start); + return false; ++ } + length += XDR_UINT_BYTES + (nsdb_quadlen(tmp) << 2); + count++; + +@@ -328,11 +330,13 @@ nsdb_posix_to_path_array(const char *pat + length = (size_t)(next - component); + if (length > 255) { + nsdb_free_string_array(result); ++ free(normalized); + return FEDFS_ERR_SVRFAULT; + } + + result[i] = strndup(component, length); + if (result[i] == NULL) { ++ free(normalized); + nsdb_free_string_array(result); + return FEDFS_ERR_SVRFAULT; + } +diff -up nfs-utils-2.3.3/support/nfs/exports.c.orig nfs-utils-2.3.3/support/nfs/exports.c +--- nfs-utils-2.3.3/support/nfs/exports.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/exports.c 2019-09-18 10:57:56.715567641 -0400 +@@ -714,6 +714,7 @@ parsesquash(char *list, int **idp, int * + } + if (id0 == -1 || id1 == -1) { + syntaxerr("uid/gid -1 not permitted"); ++ xfree(id); + return -1; + } + if ((len % 8) == 0) +@@ -724,6 +725,7 @@ parsesquash(char *list, int **idp, int * + break; + if (*cp != ',') { + syntaxerr("bad uid/gid list"); ++ xfree(id); + return -1; + } + cp++; +diff -up nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c.orig nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c +--- nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c 2019-09-18 10:57:56.716567635 -0400 +@@ -406,8 +406,10 @@ int nfs4_init_name_mapping(char *conffil + nfs4_methods = conf_get_list("Translation", "Method"); + if (nfs4_methods) { + IDMAP_LOG(1, ("libnfsidmap: processing 'Method' list")); +- if (load_plugins(nfs4_methods, &nfs4_plugins) == -1) ++ if (load_plugins(nfs4_methods, &nfs4_plugins) == -1) { ++ conf_free_list(nfs4_methods); + return -ENOENT; ++ } + } else { + struct conf_list list; + struct conf_list_node node; +@@ -475,11 +477,15 @@ out: + if (ret) { + if (nfs4_plugins) + unload_plugins(nfs4_plugins); +- if (gss_plugins) ++ if (gss_plugins) { + unload_plugins(gss_plugins); ++ } + nfs4_plugins = gss_plugins = NULL; + } + ++ if (gss_methods) ++ conf_free_list(gss_methods); ++ + return ret ? -ENOENT: 0; + } + +diff -up nfs-utils-2.3.3/support/nfsidmap/static.c.orig nfs-utils-2.3.3/support/nfsidmap/static.c +--- nfs-utils-2.3.3/support/nfsidmap/static.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfsidmap/static.c 2019-09-18 10:57:56.716567635 -0400 +@@ -347,6 +347,7 @@ static int static_init(void) { + warnx("static_init: calloc (1, %lu) failed", + (unsigned long)sizeof *unode); + free(pw); ++ conf_free_list(princ_list); + return -ENOMEM; + } + unode->uid = pw->pw_uid; +@@ -355,6 +356,9 @@ static int static_init(void) { + unode->localname = conf_get_str("Static", cln->field); + if (!unode->localname) { + free(pw); ++ free(unode->principal); ++ free(unode); ++ conf_free_list(princ_list); + return -ENOENT; + } + +@@ -379,6 +383,7 @@ static int static_init(void) { + warnx("static_init: calloc (1, %lu) failed", + (unsigned long)sizeof *gnode); + free(gr); ++ conf_free_list(princ_list); + return -ENOMEM; + } + gnode->gid = gr->gr_gid; +@@ -387,6 +392,9 @@ static int static_init(void) { + gnode->localgroup = conf_get_str("Static", cln->field); + if (!gnode->localgroup) { + free(gr); ++ free(gnode->principal); ++ free(gnode); ++ conf_free_list(princ_list); + return -ENOENT; + } + +@@ -394,6 +402,8 @@ static int static_init(void) { + + LIST_INSERT_HEAD (&gid_mappings[gid_hash(gnode->gid)], gnode, link); + } ++ ++ conf_free_list(princ_list); + return 0; + } + +diff -up nfs-utils-2.3.3/support/nfs/mydaemon.c.orig nfs-utils-2.3.3/support/nfs/mydaemon.c +--- nfs-utils-2.3.3/support/nfs/mydaemon.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/mydaemon.c 2019-09-18 10:57:56.716567635 -0400 +@@ -123,6 +123,7 @@ daemon_init(bool fg) + dup2(tempfd, 0); + dup2(tempfd, 1); + dup2(tempfd, 2); ++ close(tempfd); + closelog(); + dup2(pipefds[1], 3); + pipefds[1] = 3; +diff -up nfs-utils-2.3.3/support/nfs/rpcmisc.c.orig nfs-utils-2.3.3/support/nfs/rpcmisc.c +--- nfs-utils-2.3.3/support/nfs/rpcmisc.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/rpcmisc.c 2019-09-18 10:57:56.716567635 -0400 +@@ -102,6 +102,7 @@ makesock(int port, int proto) + if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) { + xlog(L_FATAL, "Could not bind name to socket: %s", + strerror(errno)); ++ close(sock); + return -1; + } + +diff -up nfs-utils-2.3.3/support/nfs/svc_socket.c.orig nfs-utils-2.3.3/support/nfs/svc_socket.c +--- nfs-utils-2.3.3/support/nfs/svc_socket.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/svc_socket.c 2019-09-18 10:57:56.717567629 -0400 +@@ -134,6 +134,7 @@ svc_socket (u_long number, int type, int + if (ret < 0) + { + xlog(L_ERROR, "svc_socket: socket reuse problem: %m"); ++ (void) __close(sock); + return ret; + } + } +diff -up nfs-utils-2.3.3/support/nfs/xcommon.c.orig nfs-utils-2.3.3/support/nfs/xcommon.c +--- nfs-utils-2.3.3/support/nfs/xcommon.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/xcommon.c 2019-09-18 10:57:56.717567629 -0400 +@@ -53,14 +53,17 @@ char * + xstrconcat3 (const char *s, const char *t, const char *u) { + char *res; + +- if (!s) s = ""; ++ int dofree = 1; ++ ++ if (!s) s = "", dofree=0; + if (!t) t = ""; + if (!u) u = ""; + res = xmalloc(strlen(s) + strlen(t) + strlen(u) + 1); + strcpy(res, s); + strcat(res, t); + strcat(res, u); +- free((void *) s); ++ if (dofree) ++ free((void *) s); + return res; + } + +@@ -69,7 +72,9 @@ char * + xstrconcat4 (const char *s, const char *t, const char *u, const char *v) { + char *res; + +- if (!s) s = ""; ++ int dofree = 1; ++ ++ if (!s) s = "", dofree=0; + if (!t) t = ""; + if (!u) u = ""; + if (!v) v = ""; +@@ -78,7 +83,8 @@ xstrconcat4 (const char *s, const char * + strcat(res, t); + strcat(res, u); + strcat(res, v); +- free((void *) s); ++ if (dofree) ++ free((void *) s); + return res; + } + +diff -up nfs-utils-2.3.3/support/nfs/xlog.c.orig nfs-utils-2.3.3/support/nfs/xlog.c +--- nfs-utils-2.3.3/support/nfs/xlog.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/xlog.c 2019-09-18 10:57:56.717567629 -0400 +@@ -135,10 +135,14 @@ xlog_from_conffile(char *service) + struct conf_list_node *n; + + kinds = conf_get_list(service, "debug"); +- if (!kinds || !kinds->cnt) ++ if (!kinds || !kinds->cnt) { ++ free(kinds); + return; ++ } + TAILQ_FOREACH(n, &(kinds->fields), link) + xlog_sconfig(n->field, 1); ++ ++ conf_free_list(kinds); + } + + int +diff -up nfs-utils-2.3.3/support/nsm/file.c.orig nfs-utils-2.3.3/support/nsm/file.c +--- nfs-utils-2.3.3/support/nsm/file.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nsm/file.c 2019-09-18 10:57:56.717567629 -0400 +@@ -533,6 +533,7 @@ nsm_update_kernel_state(const int state) + len = snprintf(buf, sizeof(buf), "%d", state); + if (error_check(len, sizeof(buf))) { + xlog_warn("Failed to form NSM state number string"); ++ close(fd); + return; + } + +diff -up nfs-utils-2.3.3/systemd/rpc-pipefs-generator.c.orig nfs-utils-2.3.3/systemd/rpc-pipefs-generator.c +--- nfs-utils-2.3.3/systemd/rpc-pipefs-generator.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/systemd/rpc-pipefs-generator.c 2019-09-18 10:57:56.717567629 -0400 +@@ -69,12 +69,16 @@ int generate_target(char *pipefs_path, c + return 1; + + ret = generate_mount_unit(pipefs_path, pipefs_unit, dirname); +- if (ret) ++ if (ret) { ++ free(pipefs_unit); + return ret; ++ } + + path = malloc(strlen(dirname) + 1 + sizeof(filebase)); +- if (!path) ++ if (!path) { ++ free(pipefs_unit); + return 2; ++ } + sprintf(path, "%s", dirname); + mkdir(path, 0755); + strcat(path, filebase); +@@ -82,6 +86,7 @@ int generate_target(char *pipefs_path, c + if (!f) + { + free(path); ++ free(pipefs_unit); + return 1; + } + +@@ -90,6 +95,7 @@ int generate_target(char *pipefs_path, c + fprintf(f, "After=%s\n", pipefs_unit); + fclose(f); + free(path); ++ free(pipefs_unit); + + return 0; + } +diff -up nfs-utils-2.3.3/utils/blkmapd/device-discovery.c.orig nfs-utils-2.3.3/utils/blkmapd/device-discovery.c +--- nfs-utils-2.3.3/utils/blkmapd/device-discovery.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/blkmapd/device-discovery.c 2019-09-18 10:58:54.444237714 -0400 +@@ -186,8 +186,11 @@ static void bl_add_disk(char *filepath) + } + } + +- if (disk && diskpath) ++ if (disk && diskpath) { ++ if (serial) ++ free(serial); + return; ++ } + + /* add path */ + path = malloc(sizeof(struct bl_disk_path)); +@@ -223,6 +226,8 @@ static void bl_add_disk(char *filepath) + disk->size = size; + disk->valid_path = path; + } ++ if (serial) ++ free(serial); + } + return; + +@@ -232,6 +237,9 @@ static void bl_add_disk(char *filepath) + free(path->full_path); + free(path); + } ++ if (serial) ++ free(serial); ++ + return; + } + +@@ -375,7 +383,12 @@ static void bl_rpcpipe_cb(void) + if (event->mask & IN_CREATE) { + BL_LOG_WARNING("nfs pipe dir created\n"); + bl_watch_dir(nfspipe_dir, &nfs_pipedir_wfd); ++ if (bl_pipe_fd >= 0) ++ close(bl_pipe_fd); + bl_pipe_fd = open(bl_pipe_file, O_RDWR); ++ if (bl_pipe_fd < 0) ++ BL_LOG_ERR("open %s failed: %s\n", ++ event->name, strerror(errno)); + } else if (event->mask & IN_DELETE) { + BL_LOG_WARNING("nfs pipe dir deleted\n"); + inotify_rm_watch(bl_watch_fd, nfs_pipedir_wfd); +@@ -388,6 +401,8 @@ static void bl_rpcpipe_cb(void) + continue; + if (event->mask & IN_CREATE) { + BL_LOG_WARNING("blocklayout pipe file created\n"); ++ if (bl_pipe_fd >= 0) ++ close(bl_pipe_fd); + bl_pipe_fd = open(bl_pipe_file, O_RDWR); + if (bl_pipe_fd < 0) + BL_LOG_ERR("open %s failed: %s\n", +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2019-09-18 10:57:56.718567624 -0400 +@@ -698,6 +698,8 @@ gssd_search_krb5_keytab(krb5_context con + "we failed to unparse principal name: %s\n", + k5err); + k5_free_kt_entry(context, kte); ++ free(k5err); ++ k5err = NULL; + continue; + } + printerr(4, "Processing keytab entry for principal '%s'\n", +@@ -899,6 +901,8 @@ find_keytab_entry(krb5_context context, + k5err = gssd_k5_err_msg(context, code); + printerr(1, "%s while building principal for '%s'\n", + k5err, spn); ++ free(k5err); ++ k5err = NULL; + continue; + } + code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte); +@@ -1168,7 +1172,8 @@ gssd_get_krb5_machine_cred_list(char *** + *list = l; + retval = 0; + goto out; +- } ++ } else ++ free((void *)l); + out: + return retval; + } +@@ -1216,6 +1221,8 @@ gssd_destroy_krb5_machine_creds(void) + printerr(0, "WARNING: %s while resolving credential " + "cache '%s' for destruction\n", k5err, + ple->ccname); ++ free(k5err); ++ k5err = NULL; + continue; + } + +diff -up nfs-utils-2.3.3/utils/mount/configfile.c.orig nfs-utils-2.3.3/utils/mount/configfile.c +--- nfs-utils-2.3.3/utils/mount/configfile.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/mount/configfile.c 2019-09-18 10:57:56.718567624 -0400 +@@ -404,7 +404,7 @@ char *conf_get_mntopts(char *spec, char + + /* list_size + optlen + ',' + '\0' */ + config_opts = calloc(1, (list_size+optlen+2)); +- if (server == NULL) { ++ if (config_opts == NULL) { + xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); + free_all(); + return mount_opts; +diff -up nfs-utils-2.3.3/utils/mountd/cache.c.orig nfs-utils-2.3.3/utils/mountd/cache.c +--- nfs-utils-2.3.3/utils/mountd/cache.c.orig 2019-09-18 10:57:14.190810677 -0400 ++++ nfs-utils-2.3.3/utils/mountd/cache.c 2019-09-18 10:57:56.718567624 -0400 +@@ -1240,7 +1240,7 @@ static struct exportent *lookup_junction + goto out; + } + status = nfs_get_basic_junction(pathname, &locations); +- switch (status) { ++ if (status) { + xlog(L_WARNING, "Dangling junction %s: %s", + pathname, strerror(status)); + goto out; +@@ -1248,10 +1248,11 @@ static struct exportent *lookup_junction + + parent = lookup_parent_export(dom, pathname, ai); + if (parent == NULL) +- goto out; ++ goto free_locations; + + exp = locations_to_export(locations, pathname, parent); + ++free_locations: + nfs_free_locations(locations->ns_list); + free(locations); + +diff -up nfs-utils-2.3.3/utils/mountd/fsloc.c.orig nfs-utils-2.3.3/utils/mountd/fsloc.c +--- nfs-utils-2.3.3/utils/mountd/fsloc.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/mountd/fsloc.c 2019-09-18 10:57:56.719567618 -0400 +@@ -102,6 +102,7 @@ static struct servers *parse_list(char * + cp = strchr(list[i], '@'); + if ((!cp) || list[i][0] != '/') { + xlog(L_WARNING, "invalid entry '%s'", list[i]); ++ free(mp); + continue; /* XXX Need better error handling */ + } + res->h_mp[i] = mp; +diff -up nfs-utils-2.3.3/utils/mount/nfsmount.c.orig nfs-utils-2.3.3/utils/mount/nfsmount.c +--- nfs-utils-2.3.3/utils/mount/nfsmount.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/mount/nfsmount.c 2019-09-18 10:57:56.730567555 -0400 +@@ -452,6 +452,7 @@ parse_options(char *old_opts, struct nfs + nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt); + out_bad: + free(tmp_opts); ++ free(mounthost); + return 0; + } + +diff -up nfs-utils-2.3.3/utils/mount/stropts.c.orig nfs-utils-2.3.3/utils/mount/stropts.c +--- nfs-utils-2.3.3/utils/mount/stropts.c.orig 2019-09-18 10:57:14.183810717 -0400 ++++ nfs-utils-2.3.3/utils/mount/stropts.c 2019-09-18 10:57:56.730567555 -0400 +@@ -982,8 +982,11 @@ static int nfs_try_mount(struct nfsmount + } + + if (!nfs_append_addr_option(address->ai_addr, +- address->ai_addrlen, mi->options)) ++ address->ai_addrlen, mi->options)) { ++ freeaddrinfo(address); ++ errno = ENOMEM; + return 0; ++ } + mi->address = address; + } + +diff -up nfs-utils-2.3.3/utils/nfsdcltrack/sqlite.c.orig nfs-utils-2.3.3/utils/nfsdcltrack/sqlite.c +--- nfs-utils-2.3.3/utils/nfsdcltrack/sqlite.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/nfsdcltrack/sqlite.c 2019-09-18 10:57:56.731567549 -0400 +@@ -215,6 +215,8 @@ sqlite_maindb_init_v2(void) + &err); + if (ret != SQLITE_OK) { + xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ if (err) ++ sqlite3_free(err); + return ret; + } + +diff -up nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c.orig nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c +--- nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c.orig 2019-11-11 08:49:06.044870974 -0500 ++++ nfs-utils-2.3.3/support/nfsidmap/libnfsidmap.c 2019-11-11 09:19:10.391896845 -0500 +@@ -486,6 +486,9 @@ out: + if (gss_methods) + conf_free_list(gss_methods); + ++ if (nfs4_methods) ++ conf_free_list(nfs4_methods); ++ + return ret ? -ENOENT: 0; + } + +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2019-11-11 08:49:06.045870979 -0500 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2019-11-11 09:19:10.393896856 -0500 +@@ -911,6 +911,8 @@ find_keytab_entry(krb5_context context, + k5err = gssd_k5_err_msg(context, code); + printerr(3, "%s while getting keytab entry for '%s'\n", + k5err, spn); ++ free(k5err); ++ k5err = NULL; + /* + * We tried the active directory machine account + * with the hostname part as-is and failed... +@@ -1013,6 +1015,8 @@ query_krb5_ccache(const char* cred_cache + char *str = NULL; + char *princstring; + ++ *ret_princname = *ret_realm = NULL; ++ + ret = krb5_init_context(&context); + if (ret) + return 0; +@@ -1047,7 +1051,7 @@ err_princ: + krb5_cc_close(context, ccache); + err_cache: + krb5_free_context(context); +- return found; ++ return (*ret_princname && *ret_realm); + } + + /*==========================*/ +@@ -1230,6 +1234,8 @@ gssd_destroy_krb5_machine_creds(void) + k5err = gssd_k5_err_msg(context, code); + printerr(0, "WARNING: %s while destroying credential " + "cache '%s'\n", k5err, ple->ccname); ++ free(k5err); ++ k5err = NULL; + } + } + krb5_free_context(context); +diff -up nfs-utils-2.3.3/utils/idmapd/idmapd.c.orig nfs-utils-2.3.3/utils/idmapd/idmapd.c +--- nfs-utils-2.3.3/utils/idmapd/idmapd.c.orig 2019-11-11 08:49:06.029870889 -0500 ++++ nfs-utils-2.3.3/utils/idmapd/idmapd.c 2019-11-11 09:19:10.393896856 -0500 +@@ -517,14 +517,16 @@ static void + clntscancb(int UNUSED(fd), short UNUSED(which), void *data) + { + struct idmap_clientq *icq = data; +- struct idmap_client *ic; ++ struct idmap_client *ic, *ic_next; + +- TAILQ_FOREACH(ic, icq, ic_next) ++ for (ic = TAILQ_FIRST(icq); ic != NULL; ic = ic_next) { ++ ic_next = TAILQ_NEXT(ic, ic_next); + if (ic->ic_fd == -1 && nfsopen(ic) == -1) { + close(ic->ic_dirfd); + TAILQ_REMOVE(icq, ic, ic_next); + free(ic); + } ++ } + } + + static void +diff -up nfs-utils-2.3.3/utils/statd/monitor.c.orig nfs-utils-2.3.3/utils/statd/monitor.c +--- nfs-utils-2.3.3/utils/statd/monitor.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/statd/monitor.c 2019-11-11 09:19:10.393896856 -0500 +@@ -66,7 +66,7 @@ sm_mon_1_svc(struct mon *argp, struct sv + *my_name = argp->mon_id.my_id.my_name; + struct my_id *id = &argp->mon_id.my_id; + char *cp; +- notify_list *clnt; ++ notify_list *clnt = NULL; + struct sockaddr_in my_addr = { + .sin_family = AF_INET, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), +@@ -223,6 +224,7 @@ sm_mon_1_svc(struct mon *argp, struct sv + + failure: + xlog_warn("STAT_FAIL to %s for SM_MON of %s", my_name, mon_name); ++ free(clnt); + return (&result); + } + +@@ -242,6 +244,7 @@ load_one_host(const char *hostname, + clnt->dns_name = strdup(hostname); + if (clnt->dns_name == NULL) { + nlist_free(NULL, clnt); ++ free(clnt); + return 0; + } + +diff -up nfs-utils-2.3.3/utils/statd/notlist.c.orig nfs-utils-2.3.3/utils/statd/notlist.c +--- nfs-utils-2.3.3/utils/statd/notlist.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/statd/notlist.c 2019-11-11 09:19:10.394896861 -0500 +@@ -210,7 +210,6 @@ nlist_free(notify_list **head, notify_li + if (NL_MON_NAME(entry)) + free(NL_MON_NAME(entry)); + free(entry->dns_name); +- free(entry); + } + + /* +@@ -219,8 +218,14 @@ nlist_free(notify_list **head, notify_li + void + nlist_kill(notify_list **head) + { +- while (*head) ++ notify_list *next; ++ ++ while (*head) { ++ next = (*head)->next; + nlist_free(head, *head); ++ free(*head); ++ *head = next; ++ } + } + + /* diff --git a/nfs-utils-2.3.3-covscan-rm-deadcode-leaks.patch b/nfs-utils-2.3.3-covscan-rm-deadcode-leaks.patch new file mode 100644 index 0000000..b109f97 --- /dev/null +++ b/nfs-utils-2.3.3-covscan-rm-deadcode-leaks.patch @@ -0,0 +1,27 @@ +From c9305f75070abe76155d6db29889bf5dead218c2 Mon Sep 17 00:00:00 2001 +From: Steve Dickson +Date: Fri, 7 Feb 2020 10:18:21 -0500 +Subject: [PATCH] query_krb5_ccache: Removed dead code that was flagged by a + covscan + +Signed-off-by: Steve Dickson +--- + utils/gssd/krb5_util.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index bff759f..a1c43d2 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -1066,8 +1066,6 @@ query_krb5_ccache(const char* cred_cache, char **ret_princname, + *ret_realm = strdup(str+1); + } + k5_free_unparsed_name(context, princstring); +- } else { +- found = 0; + } + } + krb5_free_principal(context, principal); +-- +2.24.1 + diff --git a/nfs-utils-2.3.3-exportfs-man-labels.patch b/nfs-utils-2.3.3-exportfs-man-labels.patch new file mode 100644 index 0000000..738fb85 --- /dev/null +++ b/nfs-utils-2.3.3-exportfs-man-labels.patch @@ -0,0 +1,50 @@ +commit 7d5dcd2358df55353eed94a0e84b77bb3597634e +Author: J. Bruce Fields +Date: Fri Mar 27 13:11:28 2020 -0400 + + exports man page: warn about subdirectory exports + + Subdirectory exports have a number of problems which have been poorly + documented. + + Signed-off-by: J. Bruce Fields + Signed-off-by: Steve Dickson + +diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man +index e3a16f6b..1d171849 100644 +--- a/utils/exportfs/exports.man ++++ b/utils/exportfs/exports.man +@@ -494,6 +494,33 @@ export entry for + .B /home/joe + in the example section below, which maps all requests to uid 150 (which + is supposedly that of user joe). ++ ++.SS Subdirectory Exports ++ ++Normally you should only export only the root of a filesystem. The NFS ++server will also allow you to export a subdirectory of a filesystem, ++however, this has drawbacks: ++ ++First, it may be possible for a malicious user to access files on the ++filesystem outside of the exported subdirectory, by guessing filehandles ++for those other files. The only way to prevent this is by using the ++.IR no_subtree_check ++option, which can cause other problems. ++ ++Second, export options may not be enforced in the way that you would ++expect. For example, the ++.IR security_label ++option will not work on subdirectory exports, and if nested subdirectory ++exports change the ++.IR security_label ++or ++.IR sec= ++options, NFSv4 clients will normally see only the options on the parent ++export. Also, where security options differ, a malicious client may use ++filehandle-guessing attacks to access the files from one subdirectory ++using the options from another. ++ ++ + .SS Extra Export Tables + After reading + .I /etc/exports diff --git a/nfs-utils-2.3.3-exportfs-root.patch b/nfs-utils-2.3.3-exportfs-root.patch new file mode 100644 index 0000000..4aa4bc1 --- /dev/null +++ b/nfs-utils-2.3.3-exportfs-root.patch @@ -0,0 +1,43 @@ +commit ac266e2edc4f40eef810d52c72657b645e4010db +Author: Ondrej Mosnacek +Date: Tue Apr 6 15:57:37 2021 -0400 + + exportfs: fix unexporting of '/' + + The code that has been added to strip trailing slashes from path in + unexportfs_parsed() forgot to account for the case of the root + directory, which is simply '/'. In that case it accesses path[-1] and + reduces the path to an empty string, which then fails to match any + export. + + Fix it by stopping the stripping when the path is just a single + character - it doesn't matter if it's a '/' or not, we want to keep it + either way in that case. + + Reproducer: + + exportfs localhost:/ + exportfs -u localhost:/ + + Without this patch, the unexport step fails with "exportfs: Could not + find 'localhost:/' to unexport." + + Fixes: a9a7728d8743 ("exportfs: Deal with path's trailing "/" in unexportfs_parsed()") + Link: https://bugzilla.redhat.com/show_bug.cgi?id=1941171 + + Signed-off-by: Ondrej Mosnacek + Signed-off-by: Steve Dickson + +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 262dd19a..25d757d8 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -383,7 +383,7 @@ unexportfs_parsed(char *hname, char *path, int verbose) + * so need to deal with it. + */ + size_t nlen = strlen(path); +- while (path[nlen - 1] == '/') ++ while ((nlen > 1) && (path[nlen - 1] == '/')) + nlen--; + + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { diff --git a/nfs-utils-2.3.3-exports-manpage-outdated.patch b/nfs-utils-2.3.3-exports-manpage-outdated.patch new file mode 100644 index 0000000..1660781 --- /dev/null +++ b/nfs-utils-2.3.3-exports-manpage-outdated.patch @@ -0,0 +1,30 @@ +commit ba90d61be3abca5a699765ce08759ca6b986781d +Author: Steve Dickson +Date: Thu Dec 10 14:05:23 2020 -0500 + + exports.man: Remove some outdated verbiage + + Years ago, commit 6a7d90cea765 removed the warning + this verbiage was talking about, but was never + removed from the man page. + + Signed-off-by: Steve Dickson + +diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man +index 1d171849..54b3f877 100644 +--- a/utils/exportfs/exports.man ++++ b/utils/exportfs/exports.man +@@ -169,13 +169,6 @@ default. In all releases after 1.0.0, + is the default, and + .I async + must be explicitly requested if needed. +-To help make system administrators aware of this change, +-.B exportfs +-will issue a warning if neither +-.I sync +-nor +-.I async +-is specified. + .TP + .IR no_wdelay + This option has no effect if diff --git a/nfs-utils-2.3.3-gssd-debug-cleanup.patch b/nfs-utils-2.3.3-gssd-debug-cleanup.patch new file mode 100644 index 0000000..cac0c28 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-debug-cleanup.patch @@ -0,0 +1,290 @@ +diff -up nfs-utils-2.3.3/utils/gssd/err_util.c.orig nfs-utils-2.3.3/utils/gssd/err_util.c +--- nfs-utils-2.3.3/utils/gssd/err_util.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/gssd/err_util.c 2021-07-19 12:29:21.366829573 -0400 +@@ -70,3 +70,17 @@ int get_verbosity(void) + { + return verbosity; + } ++ ++char * ++sec2time(int value) ++{ ++ static char buf[BUFSIZ]; ++ int hr, min, sec; ++ ++ hr = (value / 3600); ++ min = (value - (3600*hr))/60; ++ sec = (value - (3600*hr) - (min*60)); ++ sprintf(buf, "%dh:%dm:%ds", hr, min, sec); ++ return(buf); ++} ++ +diff -up nfs-utils-2.3.3/utils/gssd/err_util.h.orig nfs-utils-2.3.3/utils/gssd/err_util.h +--- nfs-utils-2.3.3/utils/gssd/err_util.h.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/gssd/err_util.h 2021-07-19 12:29:21.367829599 -0400 +@@ -34,5 +34,6 @@ + void initerr(char *progname, int verbosity, int fg); + void printerr(int priority, char *format, ...); + int get_verbosity(void); ++char * sec2time(int); + + #endif /* _ERR_UTIL_H_ */ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 12:24:13.963644016 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 12:29:21.368829626 -0400 +@@ -396,7 +396,7 @@ gssd_free_client(struct clnt_info *clp) + if (refcnt > 0) + return; + +- printerr(3, "freeing client %s\n", clp->relpath); ++ printerr(4, "freeing client %s\n", clp->relpath); + + if (clp->krb5_fd >= 0) + close(clp->krb5_fd); +@@ -417,7 +417,7 @@ gssd_free_client(struct clnt_info *clp) + static void + gssd_destroy_client(struct clnt_info *clp) + { +- printerr(3, "destroying client %s\n", clp->relpath); ++ printerr(4, "destroying client %s\n", clp->relpath); + + if (clp->krb5_ev) { + event_del(clp->krb5_ev); +@@ -494,7 +494,7 @@ scan_active_thread_list(void) + * upcall_thread_info from the list and free it. + */ + if (tret == PTHREAD_CANCELED) +- printerr(3, "watchdog: thread id 0x%lx cancelled successfully\n", ++ printerr(2, "watchdog: thread id 0x%lx cancelled successfully\n", + info->tid); + saveprev = info->list.tqe_prev; + TAILQ_REMOVE(&active_thread_list, info, list); +@@ -783,7 +783,7 @@ gssd_scan(void) + { + struct dirent *d; + +- printerr(3, "doing a full rescan\n"); ++ printerr(4, "doing a full rescan\n"); + rewinddir(pipefs_dir); + + while ((d = readdir(pipefs_dir))) { +diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c +--- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2021-07-19 12:24:13.964644043 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 12:29:21.368829626 -0400 +@@ -166,8 +166,9 @@ do_downcall(int k5_fd, uid_t uid, struct + unsigned int buf_size = 0; + pthread_t tid = pthread_self(); + +- printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n", +- tid, lifetime_rec, acceptor->length, acceptor->value); ++ if (get_verbosity() > 1) ++ printerr(2, "do_downcall(0x%lx): lifetime_rec=%s acceptor=%.*s\n", ++ tid, sec2time(lifetime_rec), acceptor->length, acceptor->value); + buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) + + sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length + + sizeof(context_token->length) + context_token->length + +@@ -193,7 +194,7 @@ do_downcall(int k5_fd, uid_t uid, struct + return; + out_err: + free(buf); +- printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid); ++ printerr(1, "do_downcall(0x%lx): Failed to write downcall!\n", tid); + return; + } + +@@ -204,8 +205,9 @@ do_error_downcall(int k5_fd, uid_t uid, + char *p = buf, *end = buf + 1024; + unsigned int timeout = 0; + int zero = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "doing error downcall\n"); ++ printerr(2, "do_error_downcall(0x%lx): uid %d err %d\n", tid, uid, err); + + if (WRITE_BYTES(&p, end, uid)) goto out_err; + if (WRITE_BYTES(&p, end, timeout)) goto out_err; +@@ -328,6 +330,7 @@ create_auth_rpc_client(struct clnt_info + struct timeval timeout; + struct sockaddr *addr = (struct sockaddr *) &clp->addr; + socklen_t salen; ++ pthread_t tid = pthread_self(); + + sec.qop = GSS_C_QOP_DEFAULT; + sec.svc = RPCSEC_GSS_SVC_NONE; +@@ -361,8 +364,8 @@ create_auth_rpc_client(struct clnt_info + + /* create an rpc connection to the nfs server */ + +- printerr(2, "creating %s client for server %s\n", clp->protocol, +- clp->servername); ++ printerr(3, "create_auth_rpc_client(0x%lx): creating %s client for server %s\n", ++ tid, clp->protocol, clp->servername); + + protocol = IPPROTO_TCP; + if ((strcmp(clp->protocol, "udp")) == 0) +@@ -405,7 +408,8 @@ create_auth_rpc_client(struct clnt_info + if (!tgtname) + tgtname = clp->servicename; + +- printerr(2, "creating context with server %s\n", tgtname); ++ printerr(3, "create_auth_rpc_client(0x%lx): creating context with server %s\n", ++ tid, tgtname); + auth = authgss_create_default(rpc_clnt, tgtname, &sec); + if (!auth) { + /* Our caller should print appropriate message */ +@@ -507,9 +511,10 @@ krb5_not_machine_creds(struct clnt_info + gss_cred_id_t gss_cred; + char **dname; + int err, resp = -1; ++ pthread_t tid = pthread_self(); + +- printerr(2, "krb5_not_machine_creds: uid %d tgtname %s\n", +- uid, tgtname); ++ printerr(2, "krb5_not_machine_creds(0x%lx): uid %d tgtname %s\n", ++ tid, uid, tgtname); + + *chg_err = change_identity(uid); + if (*chg_err) { +@@ -555,9 +560,10 @@ krb5_use_machine_creds(struct clnt_info + char **ccname; + int nocache = 0; + int success = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "krb5_use_machine_creds: uid %d tgtname %s\n", +- uid, tgtname); ++ printerr(2, "krb5_use_machine_creds(0x%lx): uid %d tgtname %s\n", ++ tid, uid, tgtname); + + do { + gssd_refresh_krb5_machine_credential(clp->servername, +@@ -874,6 +880,7 @@ start_upcall_thread(void (*func)(struct + pthread_t th; + struct upcall_thread_info *tinfo; + int ret; ++ pthread_t tid = pthread_self(); + + tinfo = alloc_upcall_thread_info(); + if (!tinfo) +@@ -896,6 +903,9 @@ start_upcall_thread(void (*func)(struct + free(tinfo); + return ret; + } ++ printerr(2, "start_upcall_thread(0x%lx): created thread id 0x%lx\n", ++ tid, th); ++ + tinfo->tid = th; + pthread_mutex_lock(&active_thread_list_lock); + clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout); +@@ -958,7 +968,7 @@ handle_gssd_upcall(struct clnt_info *clp + } + lbuf[lbuflen-1] = 0; + +- printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ printerr(2, "\n%s(0x%lx): '%s' (%s)\n", __func__, tid, + lbuf, clp->relpath); + + for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) { +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-07-19 12:24:13.951643697 -0400 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-07-19 12:36:27.746223992 -0400 +@@ -375,6 +375,7 @@ gssd_get_single_krb5_cred(krb5_context c + char *cache_type; + char *pname = NULL; + char *k5err = NULL; ++ pthread_t tid = pthread_self(); + + memset(&my_creds, 0, sizeof(my_creds)); + +@@ -385,8 +386,8 @@ gssd_get_single_krb5_cred(krb5_context c + now += 300; + pthread_mutex_lock(&ple_lock); + if (ple->ccname && ple->endtime > now && !nocache) { +- printerr(3, "INFO: Credentials in CC '%s' are good until %d\n", +- ple->ccname, ple->endtime); ++ printerr(3, "%s(0x%lx): Credentials in CC '%s' are good until %s", ++ __func__, tid, ple->ccname, ctime((time_t *)&ple->endtime)); + code = 0; + pthread_mutex_unlock(&ple_lock); + goto out; +@@ -486,7 +487,8 @@ gssd_get_single_krb5_cred(krb5_context c + } + + code = 0; +- printerr(2, "%s: principal '%s' ccache:'%s'\n", __func__, pname, cc_name); ++ 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) +@@ -615,6 +617,7 @@ get_full_hostname(const char *inhost, ch + struct addrinfo hints; + int retval; + char *c; ++ pthread_t tid = pthread_self(); + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; +@@ -624,8 +627,8 @@ get_full_hostname(const char *inhost, ch + /* Get full target hostname */ + retval = getaddrinfo(inhost, NULL, &hints, &addrs); + if (retval) { +- printerr(1, "%s while getting full hostname for '%s'\n", +- gai_strerror(retval), inhost); ++ printerr(1, "%s(0x%lx): getaddrinfo(%s) failed: %s\n", ++ __func__, tid, inhost, gai_strerror(retval)); + goto out; + } + strncpy(outhost, addrs->ai_canonname, outhostlen); +@@ -633,7 +636,10 @@ get_full_hostname(const char *inhost, ch + for (c = outhost; *c != '\0'; c++) + *c = tolower(*c); + +- printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost); ++ if (get_verbosity() && strcmp(inhost, outhost)) ++ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost'%s'\n", ++ inhost, outhost); ++ + retval = 0; + out: + return retval; +@@ -819,6 +825,7 @@ find_keytab_entry(krb5_context context, + krb5_principal princ; + const char *notsetstr = "not set"; + char *adhostoverride = NULL; ++ pthread_t tid = pthread_self(); + + + /* Get full target hostname */ +@@ -972,7 +979,7 @@ find_keytab_entry(krb5_context context, + tried_upper = 1; + } + } else { +- printerr(2, "Success getting keytab entry for '%s'\n",spn); ++ printerr(2, "find_keytab_entry(0x%lx): Success getting keytab entry for '%s'\n",tid, spn); + retval = 0; + goto out; + } +@@ -1113,9 +1120,6 @@ gssd_refresh_krb5_machine_credential_int + char *k5err = NULL; + const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; + +- printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", +- __func__, hostname, ple, service, srchost); +- + /* + * If a specific service name was specified, use it. + * Otherwise, use the default list. +@@ -1124,9 +1128,10 @@ gssd_refresh_krb5_machine_credential_int + svcnames[0] = service; + svcnames[1] = NULL; + } +- if (hostname == NULL && ple == NULL) ++ if (hostname == NULL && ple == NULL) { ++ printerr(0, "ERROR: %s: Invalid args\n", __func__); + return EINVAL; +- ++ } + code = krb5_init_context(&context); + if (code) { + k5err = gssd_k5_err_msg(NULL, code); diff --git a/nfs-utils-2.3.3-gssd-early-daemon.patch b/nfs-utils-2.3.3-gssd-early-daemon.patch new file mode 100644 index 0000000..1935daf --- /dev/null +++ b/nfs-utils-2.3.3-gssd-early-daemon.patch @@ -0,0 +1,17 @@ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2019-11-11 09:23:28.920435729 -0500 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2019-11-26 13:59:52.812685585 -0500 +@@ -1020,11 +1020,11 @@ main(int argc, char *argv[]) + "support setting debug levels\n"); + #endif + ++ daemon_init(fg); ++ + if (gssd_check_mechs() != 0) + errx(1, "Problem with gssapi library"); + +- daemon_init(fg); +- + event_init(); + + pipefs_dir = opendir(pipefs_path); diff --git a/nfs-utils-2.3.3-gssd-failed-thread.patch b/nfs-utils-2.3.3-gssd-failed-thread.patch new file mode 100644 index 0000000..53c68e5 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-failed-thread.patch @@ -0,0 +1,402 @@ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 09:39:04.273895536 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 09:40:13.942751214 -0400 +@@ -364,7 +364,7 @@ out: + /* Actually frees clp and fields that might be used from other + * threads if was last reference. + */ +-static void ++void + gssd_free_client(struct clnt_info *clp) + { + int refcnt; +@@ -416,55 +416,6 @@ gssd_destroy_client(struct clnt_info *cl + + static void gssd_scan(void); + +-static int +-start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info) +-{ +- pthread_attr_t attr; +- pthread_t th; +- int ret; +- +- ret = pthread_attr_init(&attr); +- if (ret != 0) { +- printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", +- ret, strerror(errno)); +- return ret; +- } +- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); +- if (ret != 0) { +- printerr(0, "ERROR: failed to create pthread attr: ret %d: " +- "%s\n", ret, strerror(errno)); +- return ret; +- } +- +- ret = pthread_create(&th, &attr, (void *)func, (void *)info); +- if (ret != 0) +- printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", +- ret, strerror(errno)); +- return ret; +-} +- +-static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp) +-{ +- struct clnt_upcall_info *info; +- +- info = malloc(sizeof(struct clnt_upcall_info)); +- if (info == NULL) +- return NULL; +- +- pthread_mutex_lock(&clp_lock); +- clp->refcount++; +- pthread_mutex_unlock(&clp_lock); +- info->clp = clp; +- +- return info; +-} +- +-void free_upcall_info(struct clnt_upcall_info *info) +-{ +- gssd_free_client(info->clp); +- free(info); +-} +- + /* For each upcall read the upcall info into the buffer, then create a + * thread in a detached state so that resources are released back into + * the system without the need for a join. +@@ -473,44 +424,16 @@ static void + gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) + { + struct clnt_info *clp = data; +- struct clnt_upcall_info *info; +- +- info = alloc_upcall_info(clp); +- if (info == NULL) +- return; +- +- info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf)); +- if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') { +- printerr(0, "WARNING: %s: failed reading request\n", __func__); +- free_upcall_info(info); +- return; +- } +- info->lbuf[info->lbuflen-1] = 0; + +- if (start_upcall_thread(handle_gssd_upcall, info)) +- free_upcall_info(info); ++ handle_gssd_upcall(clp); + } + + static void + gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) + { + struct clnt_info *clp = data; +- struct clnt_upcall_info *info; +- +- info = alloc_upcall_info(clp); +- if (info == NULL) +- return; +- +- if (read(clp->krb5_fd, &info->uid, +- sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) { +- printerr(0, "WARNING: %s: failed reading uid from krb5 " +- "upcall pipe: %s\n", __func__, strerror(errno)); +- free_upcall_info(info); +- return; +- } + +- if (start_upcall_thread(handle_krb5_upcall, info)) +- free_upcall_info(info); ++ handle_krb5_upcall(clp); + } + + static struct clnt_info * +diff -up nfs-utils-2.3.3/utils/gssd/gssd.h.orig nfs-utils-2.3.3/utils/gssd/gssd.h +--- nfs-utils-2.3.3/utils/gssd/gssd.h.orig 2021-07-19 09:39:04.269895430 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.h 2021-07-19 09:40:13.943751240 -0400 +@@ -84,14 +84,17 @@ struct clnt_info { + + struct clnt_upcall_info { + struct clnt_info *clp; +- char lbuf[RPC_CHAN_BUF_SIZE]; +- int lbuflen; + uid_t uid; ++ int fd; ++ char *srchost; ++ char *target; ++ char *service; + }; + +-void handle_krb5_upcall(struct clnt_upcall_info *clp); +-void handle_gssd_upcall(struct clnt_upcall_info *clp); ++void handle_krb5_upcall(struct clnt_info *clp); ++void handle_gssd_upcall(struct clnt_info *clp); + void free_upcall_info(struct clnt_upcall_info *info); ++void gssd_free_client(struct clnt_info *clp); + + + #endif /* _RPC_GSSD_H_ */ +diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c +--- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2021-07-19 09:39:04.269895430 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 09:40:13.944751267 -0400 +@@ -80,6 +80,8 @@ + #include "nfslib.h" + #include "gss_names.h" + ++extern pthread_mutex_t clp_lock; ++ + /* Encryption types supported by the kernel rpcsec_gss code */ + int num_krb5_enctypes = 0; + krb5_enctype *krb5_enctypes = NULL; +@@ -719,22 +721,133 @@ out_return_error: + goto out; + } + +-void +-handle_krb5_upcall(struct clnt_upcall_info *info) +-{ +- struct clnt_info *clp = info->clp; ++static struct clnt_upcall_info * ++alloc_upcall_info(struct clnt_info *clp, uid_t uid, int fd, char *srchost, ++ char *target, char *service) ++{ ++ struct clnt_upcall_info *info; ++ ++ info = malloc(sizeof(struct clnt_upcall_info)); ++ if (info == NULL) ++ return NULL; ++ ++ memset(info, 0, sizeof(*info)); ++ pthread_mutex_lock(&clp_lock); ++ clp->refcount++; ++ pthread_mutex_unlock(&clp_lock); ++ info->clp = clp; ++ info->uid = uid; ++ info->fd = fd; ++ if (srchost) { ++ info->srchost = strdup(srchost); ++ if (info->srchost == NULL) ++ goto out_info; ++ } ++ if (target) { ++ info->target = strdup(target); ++ if (info->target == NULL) ++ goto out_srchost; ++ } ++ if (service) { ++ info->service = strdup(service); ++ if (info->service == NULL) ++ goto out_target; ++ } ++ ++out: ++ return info; + +- printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); ++out_target: ++ if (info->target) ++ free(info->target); ++out_srchost: ++ if (info->srchost) ++ free(info->srchost); ++out_info: ++ free(info); ++ info = NULL; ++ goto out; ++} ++ ++void free_upcall_info(struct clnt_upcall_info *info) ++{ ++ gssd_free_client(info->clp); ++ if (info->service) ++ free(info->service); ++ if (info->target) ++ free(info->target); ++ if (info->srchost) ++ free(info->srchost); ++ free(info); ++} + +- process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL); ++static void ++gssd_work_thread_fn(struct clnt_upcall_info *info) ++{ ++ process_krb5_upcall(info->clp, info->uid, info->fd, info->srchost, info->target, info->service); + free_upcall_info(info); + } + ++static int ++start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info) ++{ ++ pthread_attr_t attr; ++ pthread_t th; ++ int ret; ++ ++ ret = pthread_attr_init(&attr); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++ } ++ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to create pthread attr: ret %d: " ++ "%s\n", ret, strerror(errno)); ++ return ret; ++ } ++ ++ ret = pthread_create(&th, &attr, (void *)func, (void *)info); ++ if (ret != 0) ++ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++} ++ + void +-handle_gssd_upcall(struct clnt_upcall_info *info) ++handle_krb5_upcall(struct clnt_info *clp) + { +- struct clnt_info *clp = info->clp; + uid_t uid; ++ struct clnt_upcall_info *info; ++ int err; ++ ++ if (read(clp->krb5_fd, &uid, sizeof(uid)) < (ssize_t)sizeof(uid)) { ++ printerr(0, "WARNING: failed reading uid from krb5 " ++ "upcall pipe: %s\n", strerror(errno)); ++ return; ++ } ++ printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath); ++ ++ info = alloc_upcall_info(clp, uid, clp->krb5_fd, NULL, NULL, NULL); ++ if (info == NULL) { ++ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__); ++ do_error_downcall(clp->krb5_fd, uid, -EACCES); ++ return; ++ } ++ err = start_upcall_thread(gssd_work_thread_fn, info); ++ if (err != 0) { ++ do_error_downcall(clp->krb5_fd, uid, -EACCES); ++ free_upcall_info(info); ++ } ++} ++ ++void ++handle_gssd_upcall(struct clnt_info *clp) ++{ ++ uid_t uid; ++ char lbuf[RPC_CHAN_BUF_SIZE]; ++ int lbuflen = 0; + char *p; + char *mech = NULL; + char *uidstr = NULL; +@@ -742,20 +855,22 @@ handle_gssd_upcall(struct clnt_upcall_in + char *service = NULL; + char *srchost = NULL; + char *enctypes = NULL; +- char *upcall_str; +- char *pbuf = info->lbuf; + pthread_t tid = pthread_self(); ++ struct clnt_upcall_info *info; ++ int err; + +- printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, +- info->lbuf, clp->relpath); +- +- upcall_str = strdup(info->lbuf); +- if (upcall_str == NULL) { +- printerr(0, "ERROR: malloc failure\n"); +- goto out_nomem; ++ lbuflen = read(clp->gssd_fd, lbuf, sizeof(lbuf)); ++ if (lbuflen <= 0 || lbuf[lbuflen-1] != '\n') { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed reading request\n"); ++ return; + } ++ lbuf[lbuflen-1] = 0; ++ ++ printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ lbuf, clp->relpath); + +- while ((p = strsep(&pbuf, " "))) { ++ for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) { + if (!strncmp(p, "mech=", strlen("mech="))) + mech = p + strlen("mech="); + else if (!strncmp(p, "uid=", strlen("uid="))) +@@ -773,8 +888,8 @@ handle_gssd_upcall(struct clnt_upcall_in + if (!mech || strlen(mech) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find gss mechanism name " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + if (uidstr) { +@@ -786,21 +901,21 @@ handle_gssd_upcall(struct clnt_upcall_in + if (!uidstr) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find uid " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + if (enctypes && parse_enctypes(enctypes) != 0) { + printerr(0, "WARNING: handle_gssd_upcall: " + "parsing encryption types failed: errno %d\n", errno); +- goto out; ++ return; + } + + if (target && strlen(target) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse target name " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + /* +@@ -814,21 +929,26 @@ handle_gssd_upcall(struct clnt_upcall_in + if (service && strlen(service) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse service type " +- "in upcall string '%s'\n", upcall_str); +- goto out; ++ "in upcall string '%s'\n", lbuf); ++ return; + } + +- if (strcmp(mech, "krb5") == 0 && clp->servername) +- process_krb5_upcall(clp, uid, clp->gssd_fd, srchost, target, service); +- else { ++ if (strcmp(mech, "krb5") == 0 && clp->servername) { ++ info = alloc_upcall_info(clp, uid, clp->gssd_fd, srchost, target, service); ++ if (info == NULL) { ++ printerr(0, "%s: failed to allocate clnt_upcall_info\n", __func__); ++ do_error_downcall(clp->gssd_fd, uid, -EACCES); ++ return; ++ } ++ err = start_upcall_thread(gssd_work_thread_fn, info); ++ if (err != 0) { ++ do_error_downcall(clp->gssd_fd, uid, -EACCES); ++ free_upcall_info(info); ++ } ++ } else { + if (clp->servername) + printerr(0, "WARNING: handle_gssd_upcall: " + "received unknown gss mech '%s'\n", mech); + do_error_downcall(clp->gssd_fd, uid, -EACCES); + } +-out: +- free(upcall_str); +-out_nomem: +- free_upcall_info(info); +- return; + } diff --git a/nfs-utils-2.3.3-gssd-k5identity.patch b/nfs-utils-2.3.3-gssd-k5identity.patch new file mode 100644 index 0000000..dd25da4 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-k5identity.patch @@ -0,0 +1,141 @@ +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2021-04-17 10:49:23.660184527 -0400 ++++ nfs-utils-2.3.3/nfs.conf 2021-04-17 11:14:41.482108562 -0400 +@@ -21,6 +21,7 @@ use-gss-proxy=1 + # keytab-file=/etc/krb5.keytab + # cred-cache-directory= + # preferred-realm= ++# set-home=1 + # + [lockd] + # port=0 +diff -up nfs-utils-2.3.3/systemd/nfs.conf.man.orig nfs-utils-2.3.3/systemd/nfs.conf.man +--- nfs-utils-2.3.3/systemd/nfs.conf.man.orig 2021-04-17 10:49:23.696185472 -0400 ++++ nfs-utils-2.3.3/systemd/nfs.conf.man 2021-04-17 11:14:41.483108588 -0400 +@@ -222,7 +222,8 @@ Recognized values: + .BR rpc-timeout , + .BR keytab-file , + .BR cred-cache-directory , +-.BR preferred-realm . ++.BR preferred-realm , ++.BR set-home . + + See + .BR rpc.gssd (8) +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-04-17 10:49:23.684185157 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-04-17 11:14:41.483108588 -0400 +@@ -87,6 +87,8 @@ unsigned int context_timeout = 0; + unsigned int rpc_timeout = 5; + char *preferred_realm = NULL; + char *ccachedir = NULL; ++/* set $HOME to "/" by default */ ++static bool set_home = true; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; + static bool use_gssproxy = false; +@@ -885,7 +887,7 @@ sig_die(int signal) + static void + usage(char *progname) + { +- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D]\n", ++ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H]\n", + progname); + exit(1); + } +@@ -926,6 +928,7 @@ read_gss_conf(void) + preferred_realm = s; + + use_gssproxy = conf_get_bool("gssd", "use-gss-proxy", use_gssproxy); ++ set_home = conf_get_bool("gssd", "set-home", set_home); + } + + int +@@ -946,7 +949,7 @@ main(int argc, char *argv[]) + verbosity = conf_get_num("gssd", "verbosity", verbosity); + rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity); + +- while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { ++ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:")) != -1) { + switch (opt) { + case 'f': + fg = 1; +@@ -994,6 +997,9 @@ main(int argc, char *argv[]) + case 'D': + avoid_dns = false; + break; ++ case 'H': ++ set_home = false; ++ break; + default: + usage(argv[0]); + break; +@@ -1003,13 +1009,19 @@ main(int argc, char *argv[]) + /* + * Some krb5 routines try to scrape info out of files in the user's + * home directory. This can easily deadlock when that homedir is on a +- * kerberized NFS mount. By setting $HOME unconditionally to "/", we +- * prevent this behavior in routines that use $HOME in preference to +- * the results of getpw*. ++ * kerberized NFS mount. By setting $HOME to "/" by default, we prevent ++ * this behavior in routines that use $HOME in preference to the results ++ * of getpw*. ++ * ++ * Some users do not use Kerberized home dirs and need $HOME to remain ++ * unchanged. Those users can leave $HOME unchanged by setting set_home ++ * to false. + */ +- if (setenv("HOME", "/", 1)) { +- printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno)); +- exit(1); ++ if (set_home) { ++ if (setenv("HOME", "/", 1)) { ++ printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno)); ++ exit(1); ++ } + } + + if (use_gssproxy) { +diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man +--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-04-17 10:49:23.650184264 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-04-17 11:14:41.484108615 -0400 +@@ -8,7 +8,7 @@ + rpc.gssd \- RPCSEC_GSS daemon + .SH SYNOPSIS + .B rpc.gssd +-.RB [ \-DfMnlvr ] ++.RB [ \-DfMnlvrH ] + .RB [ \-k + .IR keytab ] + .RB [ \-p +@@ -297,6 +297,16 @@ The default timeout is set to 5 seconds. + If you get messages like "WARNING: can't create tcp rpc_clnt to server + %servername% for user with uid %uid%: RPC: Remote system error - + Connection timed out", you should consider an increase of this timeout. ++.TP ++.B -H ++Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity ++files versus trying to read /.k5identity for each user. ++ ++If ++.B \-H ++is not set, rpc.gssd will use the first match found in ++/var/kerberos/krb5/user/$EUID/client.keytab and will not use a principal based on ++host and/or service parameters listed in $HOME/.k5identity. + .SH CONFIGURATION FILE + Many of the options that can be set on the command line can also be + controlled through values set in the +@@ -354,6 +364,13 @@ Equivalent to + .B preferred-realm + Equivalent to + .BR -R . ++.TP ++.B set-home ++Setting to ++.B false ++is equivalent to providing the ++.B -H ++flag. + .P + In addtion, the following value is recognized from the + .B [general] diff --git a/nfs-utils-2.3.3-gssd-man-tflag.patch b/nfs-utils-2.3.3-gssd-man-tflag.patch new file mode 100644 index 0000000..d710faa --- /dev/null +++ b/nfs-utils-2.3.3-gssd-man-tflag.patch @@ -0,0 +1,17 @@ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man +--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-04-17 11:21:18.326543446 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-04-17 12:35:59.867574517 -0400 +@@ -347,11 +347,11 @@ Equivalent to + .TP + .B context-timeout + Equivalent to +-.BR -T . ++.BR -t . + .TP + .B rpc-timeout + Equivalent to +-.BR -t . ++.BR -T . + .TP + .B keytab-file + Equivalent to diff --git a/nfs-utils-2.3.3-gssd-man-verbose.patch b/nfs-utils-2.3.3-gssd-man-verbose.patch new file mode 100644 index 0000000..7624169 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-man-verbose.patch @@ -0,0 +1,29 @@ +commit 52db5259fe78c2b948df279b697412f99e12f229 +Author: Yongcheng Yang +Date: Fri Aug 23 14:06:42 2019 -0400 + + gssd: add configure options verbosity to man page rpc.gssd(8) + + Signed-off-by: Pierguido Lambri + Signed-off-by: Yongcheng Yang + Signed-off-by: Steve Dickson + +diff --git a/utils/gssd/gssd.man b/utils/gssd/gssd.man +index e620f0d1..cc3a210a 100644 +--- a/utils/gssd/gssd.man ++++ b/utils/gssd/gssd.man +@@ -305,6 +305,14 @@ section of the + .I /etc/nfs.conf + configuration file. Values recognized include: + .TP ++.B verbosity ++Value which is equivalent to the number of ++.BR -v . ++.TP ++.B rpc-verbosity ++Value which is equivalent to the number of ++.BR -r . ++.TP + .B use-memcache + A Boolean flag equivalent to + .BR -M . diff --git a/nfs-utils-2.3.3-gssd-memoryleak.patch b/nfs-utils-2.3.3-gssd-memoryleak.patch new file mode 100644 index 0000000..89764fe --- /dev/null +++ b/nfs-utils-2.3.3-gssd-memoryleak.patch @@ -0,0 +1,118 @@ +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index eb993aab..26e51edf 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -459,7 +459,7 @@ gssd_get_single_krb5_cred(krb5_context context, + if (ccache) + krb5_cc_close(context, ccache); + krb5_free_cred_contents(context, &my_creds); +- free(k5err); ++ krb5_free_string(context, k5err); + return (code); + } + +@@ -698,7 +698,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + "we failed to unparse principal name: %s\n", + k5err); + k5_free_kt_entry(context, kte); +- free(k5err); ++ krb5_free_string(context, k5err); + k5err = NULL; + continue; + } +@@ -745,7 +745,7 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + if (retval < 0) + retval = 0; + out: +- free(k5err); ++ krb5_free_string(context, k5err); + return retval; + } + +@@ -774,7 +774,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + int tried_all = 0, tried_default = 0, tried_upper = 0; + krb5_principal princ; + const char *notsetstr = "not set"; +- char *adhostoverride; ++ char *adhostoverride = NULL; + + + /* Get full target hostname */ +@@ -802,7 +802,6 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + adhostoverride); + /* No overflow: Windows cannot handle strings longer than 19 chars */ + strcpy(myhostad, adhostoverride); +- free(adhostoverride); + } else { + strcpy(myhostad, myhostname); + for (i = 0; myhostad[i] != 0; ++i) { +@@ -811,6 +810,8 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + myhostad[i] = '$'; + myhostad[i+1] = 0; + } ++ if (adhostoverride) ++ krb5_free_string(context, adhostoverride); + + if (!srchost) { + retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); +@@ -901,7 +902,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + k5err = gssd_k5_err_msg(context, code); + printerr(1, "%s while building principal for '%s'\n", + k5err, spn); +- free(k5err); ++ krb5_free_string(context, k5err); + k5err = NULL; + continue; + } +@@ -911,7 +912,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + k5err = gssd_k5_err_msg(context, code); + printerr(3, "%s while getting keytab entry for '%s'\n", + k5err, spn); +- free(k5err); ++ krb5_free_string(context, k5err); + k5err = NULL; + /* + * We tried the active directory machine account +@@ -960,7 +961,7 @@ out: + k5_free_default_realm(context, default_realm); + if (realmnames) + krb5_free_host_realm(context, realmnames); +- free(k5err); ++ krb5_free_string(context, k5err); + return retval; + } + +@@ -1223,7 +1224,7 @@ gssd_destroy_krb5_machine_creds(void) + printerr(0, "WARNING: %s while resolving credential " + "cache '%s' for destruction\n", k5err, + ple->ccname); +- free(k5err); ++ krb5_free_string(context, k5err); + k5err = NULL; + continue; + } +@@ -1232,13 +1233,13 @@ gssd_destroy_krb5_machine_creds(void) + k5err = gssd_k5_err_msg(context, code); + printerr(0, "WARNING: %s while destroying credential " + "cache '%s'\n", k5err, ple->ccname); +- free(k5err); ++ krb5_free_string(context, k5err); + k5err = NULL; + } + } + krb5_free_context(context); + out: +- free(k5err); ++ krb5_free_string(context, k5err); + } + + /* +@@ -1321,7 +1322,7 @@ out_free_kt: + out_free_context: + krb5_free_context(context); + out: +- free(k5err); ++ krb5_free_string(context, k5err); + return retval; + } + diff --git a/nfs-utils-2.3.3-gssd-multithread-updates.patch b/nfs-utils-2.3.3-gssd-multithread-updates.patch new file mode 100644 index 0000000..2813d0c --- /dev/null +++ b/nfs-utils-2.3.3-gssd-multithread-updates.patch @@ -0,0 +1,1009 @@ +diff --git a/aclocal/libevent.m4 b/aclocal/libevent.m4 +index b5ac00ff..0ebcb524 100644 +--- a/aclocal/libevent.m4 ++++ b/aclocal/libevent.m4 +@@ -4,9 +4,13 @@ AC_DEFUN([AC_LIBEVENT], [ + dnl Check for libevent, but do not add -levent to LIBS + AC_CHECK_LIB([event], [event_dispatch], [LIBEVENT=-levent], + [AC_MSG_ERROR([libevent not found.])]) ++ AC_CHECK_LIB([event_core], [event_base_dispatch], [LIBEVENT=-levent_core], ++ [AC_MSG_ERROR([libevent2 not found.])]) + AC_SUBST(LIBEVENT) + + AC_CHECK_HEADERS([event.h], , + [AC_MSG_ERROR([libevent headers not found.])]) ++ AC_CHECK_HEADERS([event2/event.h], , ++ [AC_MSG_ERROR([libevent headers not found.])]) + + ])dnl +diff --git a/utils/gssd/gss_names.c b/utils/gssd/gss_names.c +index 2a7f3a13..982b96f4 100644 +--- a/utils/gssd/gss_names.c ++++ b/utils/gssd/gss_names.c +@@ -110,10 +110,12 @@ get_hostbased_client_name(gss_name_t client_name, gss_OID mech, + /* For Kerberos, transform the NT_KRB5_PRINCIPAL name to + * an NT_HOSTBASED_SERVICE name */ + if (g_OID_equal(&krb5oid, mech)) { +- if (get_krb5_hostbased_name(&name, &cname) == 0) +- *hostbased_name = cname; ++ if (get_krb5_hostbased_name(&name, &cname) != 0) ++ goto out_rel_buf; ++ *hostbased_name = cname; + } else { + printerr(1, "WARNING: unknown/unsupport mech OID\n"); ++ goto out_rel_buf; + } + + res = 0; +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index af66ed62..6f461fdf 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -64,7 +64,7 @@ + #include + #include + #include +-#include ++#include + + #include "gssd.h" + #include "err_util.h" +@@ -77,7 +77,7 @@ static char *pipefs_path = GSSD_PIPEFS_DIR; + static DIR *pipefs_dir; + static int pipefs_fd; + static int inotify_fd; +-struct event inotify_ev; ++struct event *inotify_ev; + + char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE; + char **ccachesearch; +@@ -90,9 +90,9 @@ char *ccachedir = NULL; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; + static bool use_gssproxy = false; +-int thread_started = false; +-pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER; +-pthread_cond_t pcond = PTHREAD_COND_INITIALIZER; ++pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER; ++static bool signal_received = false; ++static struct event_base *evbase = NULL; + + TAILQ_HEAD(topdir_list_head, topdir) topdir_list; + +@@ -359,20 +359,28 @@ out: + free(port); + } + ++/* Actually frees clp and fields that might be used from other ++ * threads if was last reference. ++ */ + static void +-gssd_destroy_client(struct clnt_info *clp) ++gssd_free_client(struct clnt_info *clp) + { +- if (clp->krb5_fd >= 0) { ++ int refcnt; ++ ++ pthread_mutex_lock(&clp_lock); ++ refcnt = --clp->refcount; ++ pthread_mutex_unlock(&clp_lock); ++ if (refcnt > 0) ++ return; ++ ++ printerr(3, "freeing client %s\n", clp->relpath); ++ ++ if (clp->krb5_fd >= 0) + close(clp->krb5_fd); +- event_del(&clp->krb5_ev); +- } + +- if (clp->gssd_fd >= 0) { ++ if (clp->gssd_fd >= 0) + close(clp->gssd_fd); +- event_del(&clp->gssd_ev); +- } + +- inotify_rm_watch(inotify_fd, clp->wd); + free(clp->relpath); + free(clp->servicename); + free(clp->servername); +@@ -380,6 +388,30 @@ gssd_destroy_client(struct clnt_info *clp) + free(clp); + } + ++/* Called when removing from clnt_list to tear down event handling. ++ * Will then free clp if was last reference. ++ */ ++static void ++gssd_destroy_client(struct clnt_info *clp) ++{ ++ printerr(3, "destroying client %s\n", clp->relpath); ++ ++ if (clp->krb5_ev) { ++ event_del(clp->krb5_ev); ++ event_free(clp->krb5_ev); ++ clp->krb5_ev = NULL; ++ } ++ ++ if (clp->gssd_ev) { ++ event_del(clp->gssd_ev); ++ event_free(clp->gssd_ev); ++ clp->gssd_ev = NULL; ++ } ++ ++ inotify_rm_watch(inotify_fd, clp->wd); ++ gssd_free_client(clp); ++} ++ + static void gssd_scan(void); + + static int +@@ -416,11 +448,21 @@ static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp) + info = malloc(sizeof(struct clnt_upcall_info)); + if (info == NULL) + return NULL; ++ ++ pthread_mutex_lock(&clp_lock); ++ clp->refcount++; ++ pthread_mutex_unlock(&clp_lock); + info->clp = clp; + + return info; + } + ++void free_upcall_info(struct clnt_upcall_info *info) ++{ ++ gssd_free_client(info->clp); ++ free(info); ++} ++ + /* For each upcall read the upcall info into the buffer, then create a + * thread in a detached state so that resources are released back into + * the system without the need for a join. +@@ -438,13 +480,13 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) + info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf)); + if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') { + printerr(0, "WARNING: %s: failed reading request\n", __func__); +- free(info); ++ free_upcall_info(info); + return; + } + info->lbuf[info->lbuflen-1] = 0; + + if (start_upcall_thread(handle_gssd_upcall, info)) +- free(info); ++ free_upcall_info(info); + } + + static void +@@ -461,12 +503,12 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) + sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) { + printerr(0, "WARNING: %s: failed reading uid from krb5 " + "upcall pipe: %s\n", __func__, strerror(errno)); +- free(info); ++ free_upcall_info(info); + return; + } + + if (start_upcall_thread(handle_krb5_upcall, info)) +- free(info); ++ free_upcall_info(info); + } + + static struct clnt_info * +@@ -501,6 +543,7 @@ gssd_get_clnt(struct topdir *tdi, const char *name) + clp->name = clp->relpath + strlen(tdi->name) + 1; + clp->krb5_fd = -1; + clp->gssd_fd = -1; ++ clp->refcount = 1; + + TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list); + return clp; +@@ -535,15 +578,15 @@ gssd_scan_clnt(struct clnt_info *clp) + clp->krb5_fd = openat(clntfd, "krb5", O_RDWR | O_NONBLOCK); + + if (gssd_was_closed && clp->gssd_fd >= 0) { +- event_set(&clp->gssd_ev, clp->gssd_fd, EV_READ | EV_PERSIST, +- gssd_clnt_gssd_cb, clp); +- event_add(&clp->gssd_ev, NULL); ++ clp->gssd_ev = event_new(evbase, clp->gssd_fd, EV_READ | EV_PERSIST, ++ gssd_clnt_gssd_cb, clp); ++ event_add(clp->gssd_ev, NULL); + } + + if (krb5_was_closed && clp->krb5_fd >= 0) { +- event_set(&clp->krb5_ev, clp->krb5_fd, EV_READ | EV_PERSIST, +- gssd_clnt_krb5_cb, clp); +- event_add(&clp->krb5_ev, NULL); ++ clp->krb5_ev = event_new(evbase, clp->krb5_fd, EV_READ | EV_PERSIST, ++ gssd_clnt_krb5_cb, clp); ++ event_add(clp->krb5_ev, NULL); + } + + if (clp->krb5_fd == -1 && clp->gssd_fd == -1) +@@ -649,7 +692,7 @@ gssd_scan_topdir(const char *name) + if (clp->scanned) + continue; + +- printerr(3, "destroying client %s\n", clp->relpath); ++ printerr(3, "orphaned client %s\n", clp->relpath); + saveprev = clp->list.tqe_prev; + TAILQ_REMOVE(&tdi->clnt_list, clp, list); + gssd_destroy_client(clp); +@@ -746,12 +789,16 @@ gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotif + } else if (ev->mask & IN_DELETE) { + if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) { + close(clp->gssd_fd); +- event_del(&clp->gssd_ev); ++ event_del(clp->gssd_ev); ++ event_free(clp->gssd_ev); ++ clp->gssd_ev = NULL; + clp->gssd_fd = -1; + + } else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) { + close(clp->krb5_fd); +- event_del(&clp->krb5_ev); ++ event_del(clp->krb5_ev); ++ event_free(clp->krb5_ev); ++ clp->krb5_ev = NULL; + clp->krb5_fd = -1; + } + +@@ -824,10 +871,15 @@ found: + static void + sig_die(int signal) + { +- if (root_uses_machine_creds) +- gssd_destroy_krb5_machine_creds(); ++ if (signal_received) { ++ gssd_destroy_krb5_principals(root_uses_machine_creds); ++ printerr(1, "forced exiting on signal %d\n", signal); ++ exit(0); ++ } ++ ++ signal_received = true; + printerr(1, "exiting on signal %d\n", signal); +- exit(0); ++ event_base_loopexit(evbase, NULL); + } + + static void +@@ -884,9 +936,10 @@ main(int argc, char *argv[]) + int rpc_verbosity = 0; + int opt; + int i; ++ int rc; + extern char *optarg; + char *progname; +- struct event sighup_ev; ++ struct event *sighup_ev; + + read_gss_conf(); + +@@ -1025,7 +1078,11 @@ main(int argc, char *argv[]) + if (gssd_check_mechs() != 0) + errx(1, "Problem with gssapi library"); + +- event_init(); ++ evbase = event_base_new(); ++ if (!evbase) { ++ printerr(0, "ERROR: failed to create event base\n"); ++ exit(EXIT_FAILURE); ++ } + + pipefs_dir = opendir(pipefs_path); + if (!pipefs_dir) { +@@ -1047,18 +1104,43 @@ main(int argc, char *argv[]) + + signal(SIGINT, sig_die); + signal(SIGTERM, sig_die); +- signal_set(&sighup_ev, SIGHUP, gssd_scan_cb, NULL); +- signal_add(&sighup_ev, NULL); +- event_set(&inotify_ev, inotify_fd, EV_READ | EV_PERSIST, gssd_inotify_cb, NULL); +- event_add(&inotify_ev, NULL); ++ sighup_ev = evsignal_new(evbase, SIGHUP, gssd_scan_cb, NULL); ++ evsignal_add(sighup_ev, NULL); ++ inotify_ev = event_new(evbase, inotify_fd, EV_READ | EV_PERSIST, ++ gssd_inotify_cb, NULL); ++ event_add(inotify_ev, NULL); + + TAILQ_INIT(&topdir_list); + gssd_scan(); + daemon_ready(); + +- event_dispatch(); ++ rc = event_base_dispatch(evbase); + +- printerr(0, "ERROR: event_dispatch() returned!\n"); +- return EXIT_FAILURE; +-} ++ printerr(0, "event_dispatch() returned %i!\n", rc); ++ ++ gssd_destroy_krb5_principals(root_uses_machine_creds); ++ ++ while (!TAILQ_EMPTY(&topdir_list)) { ++ struct topdir *tdi = TAILQ_FIRST(&topdir_list); ++ TAILQ_REMOVE(&topdir_list, tdi, list); ++ while (!TAILQ_EMPTY(&tdi->clnt_list)) { ++ struct clnt_info *clp = TAILQ_FIRST(&tdi->clnt_list); ++ TAILQ_REMOVE(&tdi->clnt_list, clp, list); ++ gssd_destroy_client(clp); ++ } ++ free(tdi); ++ } ++ ++ event_free(inotify_ev); ++ event_free(sighup_ev); ++ event_base_free(evbase); ++ ++ close(inotify_fd); ++ close(pipefs_fd); ++ closedir(pipefs_dir); + ++ free(preferred_realm); ++ free(ccachesearch); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} +diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h +index f4f59754..1e8c58d4 100644 +--- a/utils/gssd/gssd.h ++++ b/utils/gssd/gssd.h +@@ -62,13 +62,10 @@ extern int root_uses_machine_creds; + extern unsigned int context_timeout; + extern unsigned int rpc_timeout; + extern char *preferred_realm; +-extern pthread_mutex_t ple_lock; +-extern pthread_cond_t pcond; +-extern pthread_mutex_t pmutex; +-extern int thread_started; + + struct clnt_info { + TAILQ_ENTRY(clnt_info) list; ++ int refcount; + int wd; + bool scanned; + char *name; +@@ -79,9 +76,9 @@ struct clnt_info { + int vers; + char *protocol; + int krb5_fd; +- struct event krb5_ev; ++ struct event *krb5_ev; + int gssd_fd; +- struct event gssd_ev; ++ struct event *gssd_ev; + struct sockaddr_storage addr; + }; + +@@ -94,6 +91,7 @@ struct clnt_upcall_info { + + void handle_krb5_upcall(struct clnt_upcall_info *clp); + void handle_gssd_upcall(struct clnt_upcall_info *clp); ++void free_upcall_info(struct clnt_upcall_info *info); + + + #endif /* _RPC_GSSD_H_ */ +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index bfcf3f09..ae3ebe81 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -149,9 +149,10 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + char *buf = NULL, *p = NULL, *end = NULL; + unsigned int timeout = context_timeout; + unsigned int buf_size = 0; ++ pthread_t tid = pthread_self(); + +- printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n", +- lifetime_rec, acceptor->length, acceptor->value); ++ printerr(2, "do_downcall(0x%x): lifetime_rec=%u acceptor=%.*s\n", ++ tid, lifetime_rec, acceptor->length, acceptor->value); + buf_size = sizeof(uid) + sizeof(timeout) + sizeof(pd->pd_seq_win) + + sizeof(pd->pd_ctx_hndl.length) + pd->pd_ctx_hndl.length + + sizeof(context_token->length) + context_token->length + +@@ -177,7 +178,7 @@ do_downcall(int k5_fd, uid_t uid, struct authgss_private_data *pd, + return; + out_err: + free(buf); +- printerr(1, "Failed to write downcall!\n"); ++ printerr(1, "do_downcall(0x%x): Failed to write downcall!\n", tid); + return; + } + +@@ -231,7 +232,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen, + switch (sa->sa_family) { + case AF_INET: + if (s4->sin_port != 0) { +- printerr(2, "DEBUG: port already set to %d\n", ++ printerr(4, "DEBUG: port already set to %d\n", + ntohs(s4->sin_port)); + return 1; + } +@@ -239,7 +240,7 @@ populate_port(struct sockaddr *sa, const socklen_t salen, + #ifdef IPV6_SUPPORTED + case AF_INET6: + if (s6->sin6_port != 0) { +- printerr(2, "DEBUG: port already set to %d\n", ++ printerr(4, "DEBUG: port already set to %d\n", + ntohs(s6->sin6_port)); + return 1; + } +@@ -544,7 +545,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, + uid, tgtname); + + do { +- gssd_refresh_krb5_machine_credential(clp->servername, NULL, ++ gssd_refresh_krb5_machine_credential(clp->servername, + service, srchost); + /* + * Get a list of credential cache names and try each +@@ -726,7 +727,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info) + printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); + + process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL); +- free(info); ++ free_upcall_info(info); + } + + void +@@ -743,8 +744,10 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + char *enctypes = NULL; + char *upcall_str; + char *pbuf = info->lbuf; ++ pthread_t tid = pthread_self(); + +- printerr(2, "%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath); ++ printerr(2, "\n%s(0x%x): '%s' (%s)\n", __func__, tid, ++ info->lbuf, clp->relpath); + + upcall_str = strdup(info->lbuf); + if (upcall_str == NULL) { +@@ -826,6 +829,6 @@ handle_gssd_upcall(struct clnt_upcall_info *info) + out: + free(upcall_str); + out_nomem: +- free(info); ++ free_upcall_info(info); + return; + } +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 26e51edf..d675c3a4 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -126,9 +126,28 @@ + #include "gss_util.h" + #include "krb5_util.h" + ++/* ++ * List of principals from our keytab that we ++ * will try to use to obtain credentials ++ * (known as a principal list entry (ple)) ++ */ ++struct gssd_k5_kt_princ { ++ struct gssd_k5_kt_princ *next; ++ // Only protect against deletion, not modification ++ int refcount; ++ // Only set during creation in new_ple() ++ krb5_principal princ; ++ char *realm; ++ // Modified during usage by gssd_get_single_krb5_cred() ++ char *ccname; ++ krb5_timestamp endtime; ++}; ++ ++ + /* Global list of principals/cache file names for machine credentials */ +-struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; +-pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER; ++static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list = NULL; ++/* This mutex protects list modification & ple->ccname */ ++static pthread_mutex_t ple_lock = PTHREAD_MUTEX_INITIALIZER; + + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES + int limit_to_legacy_enctypes = 0; +@@ -146,6 +165,18 @@ static int gssd_get_single_krb5_cred(krb5_context context, + static int query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm); + ++static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple) ++{ ++ if (--ple->refcount) ++ return; ++ ++ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm); ++ krb5_free_principal(context, ple->princ); ++ free(ple->ccname); ++ free(ple->realm); ++ free(ple); ++} ++ + /* + * Called from the scandir function to weed out potential krb5 + * credentials cache files +@@ -352,12 +383,15 @@ gssd_get_single_krb5_cred(krb5_context context, + * 300 because clock skew must be within 300sec for kerberos + */ + now += 300; ++ pthread_mutex_lock(&ple_lock); + if (ple->ccname && ple->endtime > now && !nocache) { + printerr(3, "INFO: Credentials in CC '%s' are good until %d\n", + ple->ccname, ple->endtime); + code = 0; ++ pthread_mutex_unlock(&ple_lock); + goto out; + } ++ pthread_mutex_unlock(&ple_lock); + + if ((code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ))) { + printerr(0, "ERROR: Unable to get keytab name in " +@@ -410,6 +444,7 @@ gssd_get_single_krb5_cred(krb5_context context, + * Initialize cache file which we're going to be using + */ + ++ pthread_mutex_lock(&ple_lock); + if (use_memcache) + cache_type = "MEMORY"; + else +@@ -419,15 +454,18 @@ gssd_get_single_krb5_cred(krb5_context context, + ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, + GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); + ple->endtime = my_creds.times.endtime; +- if (ple->ccname != NULL) ++ 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; +- goto out; ++ 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", +@@ -465,6 +503,7 @@ gssd_get_single_krb5_cred(krb5_context context, + + /* + * Given a principal, find a matching ple structure ++ * Called with mutex held + */ + static struct gssd_k5_kt_princ * + find_ple_by_princ(krb5_context context, krb5_principal princ) +@@ -481,6 +520,7 @@ find_ple_by_princ(krb5_context context, krb5_principal princ) + + /* + * Create, initialize, and add a new ple structure to the global list ++ * Called with mutex held + */ + static struct gssd_k5_kt_princ * + new_ple(krb5_context context, krb5_principal princ) +@@ -532,6 +572,7 @@ new_ple(krb5_context context, krb5_principal princ) + p->next = ple; + } + ++ ple->refcount = 1; + return ple; + outerr: + if (ple) { +@@ -550,13 +591,14 @@ get_ple_by_princ(krb5_context context, krb5_principal princ) + { + struct gssd_k5_kt_princ *ple; + +- /* Need to serialize list if we ever become multi-threaded! */ +- + pthread_mutex_lock(&ple_lock); + ple = find_ple_by_princ(context, princ); + if (ple == NULL) { + ple = new_ple(context, princ); + } ++ if (ple != NULL) { ++ ple->refcount++; ++ } + pthread_mutex_unlock(&ple_lock); + + return ple; +@@ -721,6 +763,8 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, + retval = ENOMEM; + k5_free_kt_entry(context, kte); + } else { ++ release_ple(context, ple); ++ ple = NULL; + retval = 0; + *found = 1; + } +@@ -796,12 +840,12 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + /* Compute the active directory machine name HOST$ */ + krb5_appdefault_string(context, "nfs", NULL, "ad_principal_name", + notsetstr, &adhostoverride); +- if (strcmp(adhostoverride, notsetstr) != 0) { +- printerr (1, +- "AD host string overridden with \"%s\" from appdefaults\n", +- adhostoverride); +- /* No overflow: Windows cannot handle strings longer than 19 chars */ +- strcpy(myhostad, adhostoverride); ++ if (adhostoverride && strcmp(adhostoverride, notsetstr) != 0) { ++ printerr(1, ++ "AD host string overridden with \"%s\" from appdefaults\n", ++ adhostoverride); ++ /* No overflow: Windows cannot handle strings longer than 19 chars */ ++ strcpy(myhostad, adhostoverride); + } else { + strcpy(myhostad, myhostname); + for (i = 0; myhostad[i] != 0; ++i) { +@@ -928,7 +972,7 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, + tried_upper = 1; + } + } else { +- printerr(3, "Success getting keytab entry for '%s'\n",spn); ++ printerr(2, "Success getting keytab entry for '%s'\n",spn); + retval = 0; + goto out; + } +@@ -1053,6 +1097,93 @@ err_cache: + return (*ret_princname && *ret_realm); + } + ++/* ++ * Obtain (or refresh if necessary) Kerberos machine credentials ++ * If a ple is passed in, it's reference will be released ++ */ ++static int ++gssd_refresh_krb5_machine_credential_internal(char *hostname, ++ struct gssd_k5_kt_princ *ple, ++ char *service, char *srchost) ++{ ++ krb5_error_code code = 0; ++ krb5_context context; ++ krb5_keytab kt = NULL;; ++ int retval = 0; ++ char *k5err = NULL; ++ const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; ++ ++ printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", ++ __func__, hostname, ple, service, srchost); ++ ++ /* ++ * If a specific service name was specified, use it. ++ * Otherwise, use the default list. ++ */ ++ if (service != NULL && strcmp(service, "*") != 0) { ++ svcnames[0] = service; ++ svcnames[1] = NULL; ++ } ++ if (hostname == NULL && ple == NULL) ++ return EINVAL; ++ ++ code = krb5_init_context(&context); ++ if (code) { ++ k5err = gssd_k5_err_msg(NULL, code); ++ printerr(0, "ERROR: %s: %s while initializing krb5 context\n", ++ __func__, k5err); ++ retval = code; ++ goto out; ++ } ++ ++ if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", ++ __func__, k5err, keytabfile); ++ goto out_free_context; ++ } ++ ++ if (ple == NULL) { ++ krb5_keytab_entry kte; ++ ++ code = find_keytab_entry(context, kt, srchost, hostname, ++ &kte, svcnames); ++ if (code) { ++ printerr(0, "ERROR: %s: no usable keytab entry found " ++ "in keytab %s for connection with host %s\n", ++ __FUNCTION__, keytabfile, hostname); ++ retval = code; ++ goto out_free_kt; ++ } ++ ++ ple = get_ple_by_princ(context, kte.principal); ++ k5_free_kt_entry(context, &kte); ++ if (ple == NULL) { ++ char *pname; ++ if ((krb5_unparse_name(context, kte.principal, &pname))) { ++ pname = NULL; ++ } ++ printerr(0, "ERROR: %s: Could not locate or create " ++ "ple struct for principal %s for connection " ++ "with host %s\n", ++ __FUNCTION__, pname ? pname : "", ++ hostname); ++ if (pname) k5_free_unparsed_name(context, pname); ++ goto out_free_kt; ++ } ++ } ++ retval = gssd_get_single_krb5_cred(context, kt, ple, 0); ++out_free_kt: ++ krb5_kt_close(context, kt); ++out_free_context: ++ if (ple) ++ release_ple(context, ple); ++ krb5_free_context(context); ++out: ++ free(k5err); ++ return retval; ++} ++ + /*==========================*/ + /*=== External routines ===*/ + /*==========================*/ +@@ -1146,37 +1277,56 @@ gssd_get_krb5_machine_cred_list(char ***list) + goto out; + } + +- /* Need to serialize list if we ever become multi-threaded! */ +- ++ pthread_mutex_lock(&ple_lock); + for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { +- if (ple->ccname) { +- /* Make sure cred is up-to-date before returning it */ +- retval = gssd_refresh_krb5_machine_credential(NULL, ple, +- NULL, NULL); +- if (retval) +- continue; +- if (i + 1 > listsize) { +- listsize += listinc; +- l = (char **) +- realloc(l, listsize * sizeof(char *)); +- if (l == NULL) { +- retval = ENOMEM; +- goto out; +- } +- } +- if ((l[i++] = strdup(ple->ccname)) == NULL) { ++ if (!ple->ccname) ++ continue; ++ ++ /* Take advantage of the fact we only remove the ple ++ * from the list during shutdown. If it's modified ++ * concurrently at worst we'll just miss a new entry ++ * before the current ple ++ * ++ * gssd_refresh_krb5_machine_credential_internal() will ++ * release the ple refcount ++ */ ++ ple->refcount++; ++ pthread_mutex_unlock(&ple_lock); ++ /* Make sure cred is up-to-date before returning it */ ++ retval = gssd_refresh_krb5_machine_credential_internal(NULL, ple, ++ NULL, NULL); ++ pthread_mutex_lock(&ple_lock); ++ if (gssd_k5_kt_princ_list == NULL) { ++ /* Looks like we did shutdown... abort */ ++ l[i] = NULL; ++ gssd_free_krb5_machine_cred_list(l); ++ retval = ENOMEM; ++ goto out_lock; ++ } ++ if (retval) ++ continue; ++ if (i + 1 > listsize) { ++ listsize += listinc; ++ l = (char **) ++ realloc(l, listsize * sizeof(char *)); ++ if (l == NULL) { + retval = ENOMEM; +- goto out; ++ goto out_lock; + } + } ++ if ((l[i++] = strdup(ple->ccname)) == NULL) { ++ retval = ENOMEM; ++ goto out_lock; ++ } + } + if (i > 0) { + l[i] = NULL; + *list = l; + retval = 0; +- goto out; + } else + free((void *)l); ++out_lock: ++ pthread_mutex_unlock(&ple_lock); + out: + return retval; + } +@@ -1201,7 +1351,7 @@ gssd_free_krb5_machine_cred_list(char **list) + * Called upon exit. Destroys machine credentials. + */ + void +-gssd_destroy_krb5_machine_creds(void) ++gssd_destroy_krb5_principals(int destroy_machine_creds) + { + krb5_context context; + krb5_error_code code = 0; +@@ -1213,33 +1363,38 @@ gssd_destroy_krb5_machine_creds(void) + if (code) { + k5err = gssd_k5_err_msg(NULL, code); + printerr(0, "ERROR: %s while initializing krb5\n", k5err); +- goto out; ++ free(k5err); ++ return; + } + +- for (ple = gssd_k5_kt_princ_list; ple; ple = ple->next) { +- if (!ple->ccname) +- continue; +- if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "WARNING: %s while resolving credential " +- "cache '%s' for destruction\n", k5err, +- ple->ccname); +- krb5_free_string(context, k5err); +- k5err = NULL; +- continue; +- } ++ pthread_mutex_lock(&ple_lock); ++ while (gssd_k5_kt_princ_list) { ++ ple = gssd_k5_kt_princ_list; ++ gssd_k5_kt_princ_list = ple->next; + +- if ((code = krb5_cc_destroy(context, ccache))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "WARNING: %s while destroying credential " +- "cache '%s'\n", k5err, ple->ccname); +- krb5_free_string(context, k5err); +- k5err = NULL; ++ if (destroy_machine_creds && ple->ccname) { ++ if ((code = krb5_cc_resolve(context, ple->ccname, &ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "WARNING: %s while resolving credential " ++ "cache '%s' for destruction\n", k5err, ++ ple->ccname); ++ free(k5err); ++ k5err = NULL; ++ } ++ ++ if (!code && (code = krb5_cc_destroy(context, ccache))) { ++ k5err = gssd_k5_err_msg(context, code); ++ printerr(0, "WARNING: %s while destroying credential " ++ "cache '%s'\n", k5err, ple->ccname); ++ free(k5err); ++ k5err = NULL; ++ } + } ++ ++ release_ple(context, ple); + } ++ pthread_mutex_unlock(&ple_lock); + krb5_free_context(context); +- out: +- krb5_free_string(context, k5err); + } + + /* +@@ -1247,83 +1402,10 @@ gssd_destroy_krb5_machine_creds(void) + */ + int + gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, + char *service, char *srchost) + { +- krb5_error_code code = 0; +- krb5_context context; +- krb5_keytab kt = NULL;; +- int retval = 0; +- char *k5err = NULL; +- const char *svcnames[] = { "$", "root", "nfs", "host", NULL }; +- +- printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n", +- __func__, hostname, ple, service, srchost); +- +- /* +- * If a specific service name was specified, use it. +- * Otherwise, use the default list. +- */ +- if (service != NULL && strcmp(service, "*") != 0) { +- svcnames[0] = service; +- svcnames[1] = NULL; +- } +- if (hostname == NULL && ple == NULL) +- return EINVAL; +- +- code = krb5_init_context(&context); +- if (code) { +- k5err = gssd_k5_err_msg(NULL, code); +- printerr(0, "ERROR: %s: %s while initializing krb5 context\n", +- __func__, k5err); +- retval = code; +- goto out; +- } +- +- if ((code = krb5_kt_resolve(context, keytabfile, &kt))) { +- k5err = gssd_k5_err_msg(context, code); +- printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", +- __func__, k5err, keytabfile); +- goto out_free_context; +- } +- +- if (ple == NULL) { +- krb5_keytab_entry kte; +- +- code = find_keytab_entry(context, kt, srchost, hostname, +- &kte, svcnames); +- if (code) { +- printerr(0, "ERROR: %s: no usable keytab entry found " +- "in keytab %s for connection with host %s\n", +- __FUNCTION__, keytabfile, hostname); +- retval = code; +- goto out_free_kt; +- } +- +- ple = get_ple_by_princ(context, kte.principal); +- k5_free_kt_entry(context, &kte); +- if (ple == NULL) { +- char *pname; +- if ((krb5_unparse_name(context, kte.principal, &pname))) { +- pname = NULL; +- } +- printerr(0, "ERROR: %s: Could not locate or create " +- "ple struct for principal %s for connection " +- "with host %s\n", +- __FUNCTION__, pname ? pname : "", +- hostname); +- if (pname) k5_free_unparsed_name(context, pname); +- goto out_free_kt; +- } +- } +- retval = gssd_get_single_krb5_cred(context, kt, ple, 0); +-out_free_kt: +- krb5_kt_close(context, kt); +-out_free_context: +- krb5_free_context(context); +-out: +- krb5_free_string(context, k5err); +- return retval; ++ return gssd_refresh_krb5_machine_credential_internal(hostname, NULL, ++ service, srchost); + } + + /* +diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h +index b000b444..2415205a 100644 +--- a/utils/gssd/krb5_util.h ++++ b/utils/gssd/krb5_util.h +@@ -9,27 +9,13 @@ + #include "gss_oids.h" + #endif + +-/* +- * List of principals from our keytab that we +- * will try to use to obtain credentials +- * (known as a principal list entry (ple)) +- */ +-struct gssd_k5_kt_princ { +- struct gssd_k5_kt_princ *next; +- krb5_principal princ; +- char *ccname; +- char *realm; +- krb5_timestamp endtime; +-}; +- + + int gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, + char *dirname); + int gssd_get_krb5_machine_cred_list(char ***list); + void gssd_free_krb5_machine_cred_list(char **list); +-void gssd_destroy_krb5_machine_creds(void); ++void gssd_destroy_krb5_principals(int destroy_machine_creds); + int gssd_refresh_krb5_machine_credential(char *hostname, +- struct gssd_k5_kt_princ *ple, + char *service, char *srchost); + char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); + void gssd_k5_get_default_realm(char **def_realm); diff --git a/nfs-utils-2.3.3-gssd-mutex-refcnt.patch b/nfs-utils-2.3.3-gssd-mutex-refcnt.patch new file mode 100644 index 0000000..7764ff4 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-mutex-refcnt.patch @@ -0,0 +1,43 @@ +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-07-22 15:27:27.728680553 -0400 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-07-22 15:30:08.916979585 -0400 +@@ -165,18 +165,28 @@ static int gssd_get_single_krb5_cred(krb + static int query_krb5_ccache(const char* cred_cache, char **ret_princname, + char **ret_realm); + +-static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple) ++static void release_ple_locked(krb5_context context, ++ struct gssd_k5_kt_princ *ple) + { + if (--ple->refcount) + return; + +- printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ple->ccname, ple->realm); ++ printerr(3, "freeing cached principal (ccname=%s, realm=%s)\n", ++ ple->ccname, ple->realm); + krb5_free_principal(context, ple->princ); + free(ple->ccname); + free(ple->realm); + free(ple); + } + ++static void release_ple(krb5_context context, struct gssd_k5_kt_princ *ple) ++{ ++ pthread_mutex_lock(&ple_lock); ++ release_ple_locked(context, ple); ++ pthread_mutex_unlock(&ple_lock); ++} ++ ++ + /* + * Called from the scandir function to weed out potential krb5 + * credentials cache files +@@ -1396,7 +1406,7 @@ gssd_destroy_krb5_principals(int destroy + } + } + +- release_ple(context, ple); ++ release_ple_locked(context, ple); + } + pthread_mutex_unlock(&ple_lock); + krb5_free_context(context); diff --git a/nfs-utils-2.3.3-gssd-printerr.patch b/nfs-utils-2.3.3-gssd-printerr.patch new file mode 100644 index 0000000..0e564b1 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-printerr.patch @@ -0,0 +1,14 @@ +diff -up nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig nfs-utils-2.3.3/utils/gssd/krb5_util.c +--- nfs-utils-2.3.3/utils/gssd/krb5_util.c.orig 2021-11-04 10:13:07.788142847 -0400 ++++ nfs-utils-2.3.3/utils/gssd/krb5_util.c 2021-11-04 10:14:10.829841090 -0400 +@@ -647,8 +647,8 @@ get_full_hostname(const char *inhost, ch + *c = tolower(*c); + + if (get_verbosity() && strcmp(inhost, outhost)) +- printerr(1, "%s(0x%0lx): inhost '%s' different than outhost'%s'\n", +- inhost, outhost); ++ printerr(1, "%s(0x%0lx): inhost '%s' different than outhost '%s'\n", ++ __func__, tid, inhost, outhost); + + retval = 0; + out: diff --git a/nfs-utils-2.3.3-gssd-timeout-thread.patch b/nfs-utils-2.3.3-gssd-timeout-thread.patch new file mode 100644 index 0000000..6b57377 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-timeout-thread.patch @@ -0,0 +1,625 @@ +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2021-07-19 09:45:40.441448059 -0400 ++++ nfs-utils-2.3.3/nfs.conf 2021-07-19 12:08:55.314182838 -0400 +@@ -22,6 +22,8 @@ use-gss-proxy=1 + # cred-cache-directory= + # preferred-realm= + # set-home=1 ++# upcall-timeout=30 ++# cancel-timed-out-upcalls=0 + # + [lockd] + # port=0 +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2021-07-19 09:45:40.448448246 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2021-07-19 12:08:55.315182865 -0400 +@@ -96,8 +96,29 @@ pthread_mutex_t clp_lock = PTHREAD_MUTEX + static bool signal_received = false; + static struct event_base *evbase = NULL; + ++int upcall_timeout = DEF_UPCALL_TIMEOUT; ++static bool cancel_timed_out_upcalls = false; ++ + TAILQ_HEAD(topdir_list_head, topdir) topdir_list; + ++/* ++ * active_thread_list: ++ * ++ * used to track upcalls for timeout purposes. ++ * ++ * protected by the active_thread_list_lock mutex. ++ * ++ * upcall_thread_info structures are added to the tail of the list ++ * by start_upcall_thread(), so entries closer to the head of the list ++ * will be closer to hitting the upcall timeout. ++ * ++ * upcall_thread_info structures are removed from the list upon a ++ * sucessful join of the upcall thread by the watchdog thread (via ++ * scan_active_thread_list(). ++ */ ++TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list; ++pthread_mutex_t active_thread_list_lock = PTHREAD_MUTEX_INITIALIZER; ++ + struct topdir { + TAILQ_ENTRY(topdir) list; + TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list; +@@ -436,6 +457,138 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short + handle_krb5_upcall(clp); + } + ++/* ++ * scan_active_thread_list: ++ * ++ * Walks the active_thread_list, trying to join as many upcall threads as ++ * possible. For threads that have terminated, the corresponding ++ * upcall_thread_info will be removed from the list and freed. Threads that ++ * are still busy and have exceeded the upcall_timeout will cause an error to ++ * be logged and may be canceled (depending on the value of ++ * cancel_timed_out_upcalls). ++ * ++ * Returns the number of seconds that the watchdog thread should wait before ++ * calling scan_active_thread_list() again. ++ */ ++static int ++scan_active_thread_list(void) ++{ ++ struct upcall_thread_info *info; ++ struct timespec now; ++ unsigned int sleeptime; ++ bool sleeptime_set = false; ++ int err; ++ void *tret, *saveprev; ++ ++ sleeptime = upcall_timeout; ++ pthread_mutex_lock(&active_thread_list_lock); ++ clock_gettime(CLOCK_MONOTONIC, &now); ++ TAILQ_FOREACH(info, &active_thread_list, list) { ++ err = pthread_tryjoin_np(info->tid, &tret); ++ switch (err) { ++ case 0: ++ /* ++ * The upcall thread has either completed successfully, or ++ * has been canceled _and_ has acted on the cancellation request ++ * (i.e. has hit a cancellation point). We can now remove the ++ * upcall_thread_info from the list and free it. ++ */ ++ if (tret == PTHREAD_CANCELED) ++ printerr(3, "watchdog: thread id 0x%lx cancelled successfully\n", ++ info->tid); ++ saveprev = info->list.tqe_prev; ++ TAILQ_REMOVE(&active_thread_list, info, list); ++ free(info); ++ info = saveprev; ++ break; ++ case EBUSY: ++ /* ++ * The upcall thread is still running. If the timeout has expired ++ * then we either cancel the thread, log an error, and do an error ++ * downcall to the kernel (cancel_timed_out_upcalls=true) or simply ++ * log an error (cancel_timed_out_upcalls=false). In either case, ++ * the error is logged only once. ++ */ ++ if (now.tv_sec >= info->timeout.tv_sec) { ++ if (cancel_timed_out_upcalls && !(info->flags & UPCALL_THREAD_CANCELED)) { ++ printerr(0, "watchdog: thread id 0x%lx timed out\n", ++ info->tid); ++ pthread_cancel(info->tid); ++ info->flags |= (UPCALL_THREAD_CANCELED|UPCALL_THREAD_WARNED); ++ do_error_downcall(info->fd, info->uid, -ETIMEDOUT); ++ } else { ++ if (!(info->flags & UPCALL_THREAD_WARNED)) { ++ printerr(0, "watchdog: thread id 0x%lx running for %ld seconds\n", ++ info->tid, ++ now.tv_sec - info->timeout.tv_sec + upcall_timeout); ++ info->flags |= UPCALL_THREAD_WARNED; ++ } ++ } ++ } else if (!sleeptime_set) { ++ /* ++ * The upcall thread is still running, but the timeout has not yet ++ * expired. Calculate the time remaining until the timeout will ++ * expire. This is the amount of time the watchdog thread will ++ * wait before running again. We only need to do this for the busy ++ * thread closest to the head of the list - entries appearing later ++ * in the list will time out later. ++ */ ++ sleeptime = info->timeout.tv_sec - now.tv_sec; ++ sleeptime_set = true; ++ } ++ break; ++ default: ++ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */ ++ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n", ++ info->tid, err, strerror(err)); ++ break; ++ } ++ } ++ pthread_mutex_unlock(&active_thread_list_lock); ++ ++ return sleeptime; ++} ++ ++static void * ++watchdog_thread_fn(void *UNUSED(arg)) ++{ ++ unsigned int sleeptime; ++ ++ for (;;) { ++ sleeptime = scan_active_thread_list(); ++ printerr(4, "watchdog: sleeping %u secs\n", sleeptime); ++ sleep(sleeptime); ++ } ++ return (void *)0; ++} ++ ++static int ++start_watchdog_thread(void) ++{ ++ pthread_attr_t attr; ++ pthread_t th; ++ int ret; ++ ++ ret = pthread_attr_init(&attr); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++ } ++ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ if (ret != 0) { ++ printerr(0, "ERROR: failed to create pthread attr: ret %d: %s\n", ++ ret, strerror(errno)); ++ return ret; ++ } ++ ret = pthread_create(&th, &attr, watchdog_thread_fn, NULL); ++ if (ret != 0) { ++ printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", ++ ret, strerror(errno)); ++ } ++ return ret; ++} ++ + static struct clnt_info * + gssd_get_clnt(struct topdir *tdi, const char *name) + { +@@ -810,7 +963,7 @@ sig_die(int signal) + static void + usage(char *progname) + { +- fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H]\n", ++ fprintf(stderr, "usage: %s [-f] [-l] [-M] [-n] [-v] [-r] [-p pipefsdir] [-k keytab] [-d ccachedir] [-t timeout] [-R preferred realm] [-D] [-H] [-U upcall timeout] [-C]\n", + progname); + exit(1); + } +@@ -831,6 +984,9 @@ read_gss_conf(void) + #endif + context_timeout = conf_get_num("gssd", "context-timeout", context_timeout); + rpc_timeout = conf_get_num("gssd", "rpc-timeout", rpc_timeout); ++ upcall_timeout = conf_get_num("gssd", "upcall-timeout", upcall_timeout); ++ cancel_timed_out_upcalls = conf_get_bool("gssd", "cancel-timed-out-upcalls", ++ cancel_timed_out_upcalls); + s = conf_get_str("gssd", "pipefs-directory"); + if (!s) + s = conf_get_str("general", "pipefs-directory"); +@@ -872,7 +1028,7 @@ main(int argc, char *argv[]) + verbosity = conf_get_num("gssd", "verbosity", verbosity); + rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity); + +- while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:")) != -1) { ++ while ((opt = getopt(argc, argv, "HDfvrlmnMp:k:d:t:T:R:U:C")) != -1) { + switch (opt) { + case 'f': + fg = 1; +@@ -923,6 +1079,12 @@ main(int argc, char *argv[]) + case 'H': + set_home = false; + break; ++ case 'U': ++ upcall_timeout = atoi(optarg); ++ break; ++ case 'C': ++ cancel_timed_out_upcalls = true; ++ break; + default: + usage(argv[0]); + break; +@@ -995,6 +1157,11 @@ main(int argc, char *argv[]) + else + progname = argv[0]; + ++ if (upcall_timeout > MAX_UPCALL_TIMEOUT) ++ upcall_timeout = MAX_UPCALL_TIMEOUT; ++ else if (upcall_timeout < MIN_UPCALL_TIMEOUT) ++ upcall_timeout = MIN_UPCALL_TIMEOUT; ++ + initerr(progname, verbosity, fg); + #ifdef HAVE_LIBTIRPC_SET_DEBUG + /* +@@ -1045,6 +1212,14 @@ main(int argc, char *argv[]) + gssd_inotify_cb, NULL); + event_add(inotify_ev, NULL); + ++ TAILQ_INIT(&active_thread_list); ++ ++ rc = start_watchdog_thread(); ++ if (rc != 0) { ++ printerr(0, "ERROR: failed to start watchdog thread: %d\n", rc); ++ exit(EXIT_FAILURE); ++ } ++ + TAILQ_INIT(&topdir_list); + gssd_scan(); + daemon_ready(); +diff -up nfs-utils-2.3.3/utils/gssd/gssd.h.orig nfs-utils-2.3.3/utils/gssd/gssd.h +--- nfs-utils-2.3.3/utils/gssd/gssd.h.orig 2021-07-19 09:45:40.449448272 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.h 2021-07-19 12:08:55.315182865 -0400 +@@ -50,6 +50,12 @@ + #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" + #define GSSD_SERVICE_NAME "nfs" + #define RPC_CHAN_BUF_SIZE 32768 ++ ++/* timeouts are in seconds */ ++#define MIN_UPCALL_TIMEOUT 5 ++#define DEF_UPCALL_TIMEOUT 30 ++#define MAX_UPCALL_TIMEOUT 600 ++ + /* + * The gss mechanisms that we can handle + */ +@@ -91,10 +97,22 @@ struct clnt_upcall_info { + char *service; + }; + ++struct upcall_thread_info { ++ TAILQ_ENTRY(upcall_thread_info) list; ++ pthread_t tid; ++ struct timespec timeout; ++ uid_t uid; ++ int fd; ++ unsigned short flags; ++#define UPCALL_THREAD_CANCELED 0x0001 ++#define UPCALL_THREAD_WARNED 0x0002 ++}; ++ + void handle_krb5_upcall(struct clnt_info *clp); + void handle_gssd_upcall(struct clnt_info *clp); + void free_upcall_info(struct clnt_upcall_info *info); + void gssd_free_client(struct clnt_info *clp); ++int do_error_downcall(int k5_fd, uid_t uid, int err); + + + #endif /* _RPC_GSSD_H_ */ +diff -up nfs-utils-2.3.3/utils/gssd/gssd.man.orig nfs-utils-2.3.3/utils/gssd/gssd.man +--- nfs-utils-2.3.3/utils/gssd/gssd.man.orig 2021-07-19 09:45:40.443448112 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.man 2021-07-19 12:08:55.315182865 -0400 +@@ -8,7 +8,7 @@ + rpc.gssd \- RPCSEC_GSS daemon + .SH SYNOPSIS + .B rpc.gssd +-.RB [ \-DfMnlvrH ] ++.RB [ \-DfMnlvrHC ] + .RB [ \-k + .IR keytab ] + .RB [ \-p +@@ -17,6 +17,10 @@ rpc.gssd \- RPCSEC_GSS daemon + .IR ccachedir ] + .RB [ \-t + .IR timeout ] ++.RB [ \-T ++.IR timeout ] ++.RB [ \-U ++.IR timeout ] + .RB [ \-R + .IR realm ] + .SH INTRODUCTION +@@ -290,7 +294,7 @@ seconds, which allows changing Kerberos + The default is no explicit timeout, which means the kernel context will live + the lifetime of the Kerberos service ticket used in its creation. + .TP +-.B -T timeout ++.BI "-T " timeout + Timeout, in seconds, to create an RPC connection with a server while + establishing an authenticated gss context for a user. + The default timeout is set to 5 seconds. +@@ -298,6 +302,18 @@ If you get messages like "WARNING: can't + %servername% for user with uid %uid%: RPC: Remote system error - + Connection timed out", you should consider an increase of this timeout. + .TP ++.BI "-U " timeout ++Timeout, in seconds, for upcall threads. Threads executing longer than ++.I timeout ++seconds will cause an error message to be logged. The default ++.I timeout ++is 30 seconds. The minimum is 5 seconds. The maximum is 600 seconds. ++.TP ++.B -C ++In addition to logging an error message for threads that have timed out, ++the thread will be canceled and an error of -ETIMEDOUT will be reported ++to the kernel. ++.TP + .B -H + Avoids setting $HOME to "/". This allows rpc.gssd to read per user k5identity + files versus trying to read /.k5identity for each user. +@@ -365,6 +381,17 @@ Equivalent to + Equivalent to + .BR -R . + .TP ++.B upcall-timeout ++Equivalent to ++.BR -U . ++.TP ++.B cancel-timed-out-upcalls ++Setting to ++.B true ++is equivalent to providing the ++.B -C ++flag. ++.TP + .B set-home + Setting to + .B false +diff -up nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig nfs-utils-2.3.3/utils/gssd/gssd_proc.c +--- nfs-utils-2.3.3/utils/gssd/gssd_proc.c.orig 2021-07-19 09:45:40.449448272 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd_proc.c 2021-07-19 12:08:55.316182891 -0400 +@@ -81,11 +81,24 @@ + #include "gss_names.h" + + extern pthread_mutex_t clp_lock; ++extern pthread_mutex_t active_thread_list_lock; ++extern int upcall_timeout; ++extern TAILQ_HEAD(active_thread_list_head, upcall_thread_info) active_thread_list; + + /* Encryption types supported by the kernel rpcsec_gss code */ + int num_krb5_enctypes = 0; + krb5_enctype *krb5_enctypes = NULL; + ++/* Args for the cleanup_handler() */ ++struct cleanup_args { ++ OM_uint32 *min_stat; ++ gss_buffer_t acceptor; ++ gss_buffer_t token; ++ struct authgss_private_data *pd; ++ AUTH **auth; ++ CLIENT **rpc_clnt; ++}; ++ + /* + * Parse the supported encryption type information + */ +@@ -184,7 +197,7 @@ out_err: + return; + } + +-static int ++int + do_error_downcall(int k5_fd, uid_t uid, int err) + { + char buf[1024]; +@@ -604,27 +617,66 @@ out: + } + + /* ++ * cleanup_handler: ++ * ++ * Free any resources allocated by process_krb5_upcall(). ++ * ++ * Runs upon normal termination of process_krb5_upcall as well as if the ++ * thread is canceled. ++ */ ++static void ++cleanup_handler(void *arg) ++{ ++ struct cleanup_args *args = (struct cleanup_args *)arg; ++ ++ gss_release_buffer(args->min_stat, args->acceptor); ++ if (args->token->value) ++ free(args->token->value); ++#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA ++ if (args->pd->pd_ctx_hndl.length != 0 || args->pd->pd_ctx != 0) ++ authgss_free_private_data(args->pd); ++#endif ++ if (*args->auth) ++ AUTH_DESTROY(*args->auth); ++ if (*args->rpc_clnt) ++ clnt_destroy(*args->rpc_clnt); ++} ++ ++/* ++ * process_krb5_upcall: ++ * + * this code uses the userland rpcsec gss library to create a krb5 + * context on behalf of the kernel ++ * ++ * This is the meat of the upcall thread. Note that cancelability is disabled ++ * and enabled at various points to ensure that any resources reserved by the ++ * lower level libraries are released safely. + */ + static void +-process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *srchost, +- char *tgtname, char *service) ++process_krb5_upcall(struct clnt_upcall_info *info) + { ++ struct clnt_info *clp = info->clp; ++ uid_t uid = info->uid; ++ int fd = info->fd; ++ char *srchost = info->srchost; ++ char *tgtname = info->target; ++ char *service = info->service; + CLIENT *rpc_clnt = NULL; + AUTH *auth = NULL; + struct authgss_private_data pd; + gss_buffer_desc token; +- int err, downcall_err = -EACCES; ++ int err, downcall_err; + OM_uint32 maj_stat, min_stat, lifetime_rec; + gss_name_t gacceptor = GSS_C_NO_NAME; + gss_OID mech; + gss_buffer_desc acceptor = {0}; ++ struct cleanup_args cleanup_args = {&min_stat, &acceptor, &token, &pd, &auth, &rpc_clnt}; + + token.length = 0; + token.value = NULL; + memset(&pd, 0, sizeof(struct authgss_private_data)); + ++ pthread_cleanup_push(cleanup_handler, &cleanup_args); + /* + * If "service" is specified, then the kernel is indicating that + * we must use machine credentials for this request. (Regardless +@@ -646,6 +698,8 @@ process_krb5_upcall(struct clnt_info *cl + * used for this case is not important. + * + */ ++ downcall_err = -EACCES; ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && + service == NULL)) { + +@@ -666,15 +720,21 @@ process_krb5_upcall(struct clnt_info *cl + goto out_return_error; + } + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (!authgss_get_private_data(auth, &pd)) { + printerr(1, "WARNING: Failed to obtain authentication " + "data for user with uid %d for server %s\n", + uid, clp->servername); + goto out_return_error; + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + + /* Grab the context lifetime and acceptor name out of the ctx. */ ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + maj_stat = gss_inquire_context(&min_stat, pd.pd_ctx, NULL, &gacceptor, + &lifetime_rec, &mech, NULL, NULL, NULL); + +@@ -686,37 +746,35 @@ process_krb5_upcall(struct clnt_info *cl + get_hostbased_client_buffer(gacceptor, mech, &acceptor); + gss_release_name(&min_stat, &gacceptor); + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + + /* + * The serialization can mean turning pd.pd_ctx into a lucid context. If + * that happens then the pd.pd_ctx will be unusable, so we must never + * try to use it after this point. + */ ++ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) { + printerr(1, "WARNING: Failed to serialize krb5 context for " + "user with uid %d for server %s\n", + uid, clp->servername); + goto out_return_error; + } ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); + + do_downcall(fd, uid, &pd, &token, lifetime_rec, &acceptor); + + out: +- gss_release_buffer(&min_stat, &acceptor); +- if (token.value) +- free(token.value); +-#ifdef HAVE_AUTHGSS_FREE_PRIVATE_DATA +- if (pd.pd_ctx_hndl.length != 0 || pd.pd_ctx != 0) +- authgss_free_private_data(&pd); +-#endif +- if (auth) +- AUTH_DESTROY(auth); +- if (rpc_clnt) +- clnt_destroy(rpc_clnt); ++ pthread_cleanup_pop(1); + + return; + + out_return_error: ++ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); ++ pthread_testcancel(); ++ + do_error_downcall(fd, uid, downcall_err); + goto out; + } +@@ -782,36 +840,69 @@ void free_upcall_info(struct clnt_upcall + } + + static void +-gssd_work_thread_fn(struct clnt_upcall_info *info) ++cleanup_clnt_upcall_info(void *arg) + { +- process_krb5_upcall(info->clp, info->uid, info->fd, info->srchost, info->target, info->service); ++ struct clnt_upcall_info *info = (struct clnt_upcall_info *)arg; ++ + free_upcall_info(info); + } + ++static void ++gssd_work_thread_fn(struct clnt_upcall_info *info) ++{ ++ pthread_cleanup_push(cleanup_clnt_upcall_info, info); ++ process_krb5_upcall(info); ++ pthread_cleanup_pop(1); ++} ++ ++static struct upcall_thread_info * ++alloc_upcall_thread_info(void) ++{ ++ struct upcall_thread_info *info; ++ ++ info = malloc(sizeof(struct upcall_thread_info)); ++ if (info == NULL) ++ return NULL; ++ memset(info, 0, sizeof(*info)); ++ return info; ++} ++ + static int +-start_upcall_thread(void (*func)(struct clnt_upcall_info *), void *info) ++start_upcall_thread(void (*func)(struct clnt_upcall_info *), struct clnt_upcall_info *info) + { + pthread_attr_t attr; + pthread_t th; ++ struct upcall_thread_info *tinfo; + int ret; + ++ tinfo = alloc_upcall_thread_info(); ++ if (!tinfo) ++ return -ENOMEM; ++ tinfo->fd = info->fd; ++ tinfo->uid = info->uid; ++ + ret = pthread_attr_init(&attr); + if (ret != 0) { + printerr(0, "ERROR: failed to init pthread attr: ret %d: %s\n", + ret, strerror(errno)); +- return ret; +- } +- ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); +- if (ret != 0) { +- printerr(0, "ERROR: failed to create pthread attr: ret %d: " +- "%s\n", ret, strerror(errno)); ++ free(tinfo); + return ret; + } + + ret = pthread_create(&th, &attr, (void *)func, (void *)info); +- if (ret != 0) ++ if (ret != 0) { + printerr(0, "ERROR: pthread_create failed: ret %d: %s\n", + ret, strerror(errno)); ++ free(tinfo); ++ return ret; ++ } ++ tinfo->tid = th; ++ pthread_mutex_lock(&active_thread_list_lock); ++ clock_gettime(CLOCK_MONOTONIC, &tinfo->timeout); ++ tinfo->timeout.tv_sec += upcall_timeout; ++ TAILQ_INSERT_TAIL(&active_thread_list, tinfo, list); ++ pthread_mutex_unlock(&active_thread_list_lock); ++ + return ret; + } + diff --git a/nfs-utils-2.3.3-gssd-usegssproxy.patch b/nfs-utils-2.3.3-gssd-usegssproxy.patch new file mode 100644 index 0000000..720752c --- /dev/null +++ b/nfs-utils-2.3.3-gssd-usegssproxy.patch @@ -0,0 +1,70 @@ +commit 104f90f4ce964ddcfe50d4d24cc5e7ff96952299 +Author: Steve Dickson +Date: Sat Oct 20 12:01:37 2018 -0400 + + gssd: Introduce use-gss-proxy boolean to nfs.conf + + Allow the used of the gssprox-mech(8) through a the + boolean variable in the [gssd] section of nfs.conf + + Signed-off-by: Steve Dickson + +diff --git a/nfs.conf b/nfs.conf +index 0d0ec9b..5546109 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -11,6 +11,7 @@ + #[gssd] + # use-memcache=0 + # use-machine-creds=1 ++# use-gss-proxy=0 + # avoid-dns=1 + # limit-to-legacy-enctypes=0 + # context-timeout=0 +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index 189b052..699db3f 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -213,6 +213,7 @@ for details. + Recognized values: + .BR use-memcache , + .BR use-machine-creds , ++.BR use-gss-proxy , + .BR avoid-dns , + .BR limit-to-legacy-enctypes , + .BR context-timeout , +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index 00df2fc..2e92f28 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -89,6 +89,7 @@ char *preferred_realm = NULL; + char *ccachedir = NULL; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; ++static bool use_gssproxy = false; + int thread_started = false; + pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t pcond = PTHREAD_COND_INITIALIZER; +@@ -872,6 +873,7 @@ read_gss_conf(void) + if (s) + preferred_realm = s; + ++ use_gssproxy = conf_get_bool("gssd", "use-gss-proxy", use_gssproxy); + } + + int +@@ -957,6 +959,14 @@ main(int argc, char *argv[]) + exit(1); + } + ++ if (use_gssproxy) { ++ if (setenv("GSS_USE_PROXY", "yes", 1) < 0) { ++ printerr(0, "gssd: Unable to set $GSS_USE_PROXY: %s\n", ++ strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ } ++ + if (ccachedir) { + char *ccachedir_copy; + char *ptr; diff --git a/nfs-utils-2.3.3-gssd-verbose.patch b/nfs-utils-2.3.3-gssd-verbose.patch new file mode 100644 index 0000000..2618ef2 --- /dev/null +++ b/nfs-utils-2.3.3-gssd-verbose.patch @@ -0,0 +1,52 @@ +commit 64d83364b08ab32c6b8fee903529314349175772 +Author: Pierguido Lambri +Date: Mon Mar 11 13:50:57 2019 -0400 + + gssd: add verbosity options to the rpc.gssd man page + + It also adds the commented out entries in the nfs.conf + default file. + + Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1668026 + + Signed-off-by: Pierguido Lambri + Signed-off-by: Steve Dickson + +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2019-03-19 11:04:16.903567972 -0400 ++++ nfs-utils-2.3.3/nfs.conf 2019-03-19 11:10:54.452251970 -0400 +@@ -9,6 +9,8 @@ + # debug=0 + # + [gssd] ++# verbosity=0 ++# rpc-verbosity=0 + # use-memcache=0 + # use-machine-creds=1 + use-gss-proxy=1 +diff -up nfs-utils-2.3.3/systemd/nfs.conf.man.orig nfs-utils-2.3.3/systemd/nfs.conf.man +--- nfs-utils-2.3.3/systemd/nfs.conf.man.orig 2019-03-19 11:04:16.911567926 -0400 ++++ nfs-utils-2.3.3/systemd/nfs.conf.man 2019-03-19 11:10:54.452251970 -0400 +@@ -211,6 +211,8 @@ for details. + .TP + .B gssd + Recognized values: ++.BR verbosity , ++.BR rpc-verbosity , + .BR use-memcache , + .BR use-machine-creds , + .BR use-gss-proxy , +diff -up nfs-utils-2.3.3/utils/gssd/gssd.c.orig nfs-utils-2.3.3/utils/gssd/gssd.c +--- nfs-utils-2.3.3/utils/gssd/gssd.c.orig 2019-03-19 11:04:16.893568031 -0400 ++++ nfs-utils-2.3.3/utils/gssd/gssd.c 2019-03-19 11:10:54.453251964 -0400 +@@ -890,8 +890,8 @@ main(int argc, char *argv[]) + + read_gss_conf(); + +- verbosity = conf_get_num("gssd", "Verbosity", verbosity); +- rpc_verbosity = conf_get_num("gssd", "RPC-Verbosity", rpc_verbosity); ++ verbosity = conf_get_num("gssd", "verbosity", verbosity); ++ rpc_verbosity = conf_get_num("gssd", "rpc-verbosity", rpc_verbosity); + + while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { + switch (opt) { diff --git a/nfs-utils-2.3.3-idmap-errmsg.patch b/nfs-utils-2.3.3-idmap-errmsg.patch new file mode 100644 index 0000000..c382703 --- /dev/null +++ b/nfs-utils-2.3.3-idmap-errmsg.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-2.3.3/utils/nfsidmap/nfsidmap.c.orig nfs-utils-2.3.3/utils/nfsidmap/nfsidmap.c +--- nfs-utils-2.3.3/utils/nfsidmap/nfsidmap.c.orig 2020-05-05 14:07:24.642693179 -0400 ++++ nfs-utils-2.3.3/utils/nfsidmap/nfsidmap.c 2020-05-05 14:08:39.054849153 -0400 +@@ -432,7 +432,7 @@ int main(int argc, char **argv) + + xlog_stderr(verbose); + if ((argc - optind) != 2) { +- xlog_warn("Bad arg count. Check /etc/request-key.conf"); ++ xlog_warn("Bad arg count. Check /etc/request-key.d/request-key.conf"); + xlog_warn(USAGE, progname); + return EXIT_FAILURE; + } diff --git a/nfs-utils-2.3.3-junction-err-fix.patch b/nfs-utils-2.3.3-junction-err-fix.patch new file mode 100644 index 0000000..fbaae1c --- /dev/null +++ b/nfs-utils-2.3.3-junction-err-fix.patch @@ -0,0 +1,57 @@ +commit efefa7845601f551820fa17cb0808dbb3c3cc3dd +Author: Steve Dickson +Date: Wed Nov 13 09:32:00 2019 -0500 + + junction: Fixed debug statement to compile with -Werror=format=2 flag + + Signed-off-by: Steve Dickson + +diff --git a/support/junction/xml.c b/support/junction/xml.c +index 79b0770..7005e95 100644 +--- a/support/junction/xml.c ++++ b/support/junction/xml.c +@@ -327,8 +327,8 @@ junction_parse_xml_read(const char *pathname, int fd, const char *name, + if (retval != FEDFS_OK) + return retval; + +- xlog(D_CALL, "%s: XML document contained in junction:\n%.*s", +- __func__, len, buf); ++ xlog(D_CALL, "%s: XML document contained in junction:\n%ld.%s", ++ __func__, len, (char *)buf); + + retval = junction_parse_xml_buf(pathname, name, buf, len, doc); + +commit f7c0c0dc4a02d87965d3fbbab69786ca07fdecea +Author: Guillaume Rousse +Date: Fri Nov 22 10:20:03 2019 -0500 + + fix compilation with -Werror=format on i586 + + Signed-off-by: Steve Dickson + +diff --git a/support/junction/xml.c b/support/junction/xml.c +index 7005e95..813110b 100644 +--- a/support/junction/xml.c ++++ b/support/junction/xml.c +@@ -327,7 +327,7 @@ junction_parse_xml_read(const char *pathname, int fd, const char *name, + if (retval != FEDFS_OK) + return retval; + +- xlog(D_CALL, "%s: XML document contained in junction:\n%ld.%s", ++ xlog(D_CALL, "%s: XML document contained in junction:\n%zu.%s", + __func__, len, (char *)buf); + + retval = junction_parse_xml_buf(pathname, name, buf, len, doc); +diff --git a/tools/locktest/testlk.c b/tools/locktest/testlk.c +index b392f71..ea51f78 100644 +--- a/tools/locktest/testlk.c ++++ b/tools/locktest/testlk.c +@@ -81,7 +81,7 @@ main(int argc, char **argv) + if (fl.l_type == F_UNLCK) { + printf("%s: no conflicting lock\n", fname); + } else { +- printf("%s: conflicting lock by %d on (%ld;%ld)\n", ++ printf("%s: conflicting lock by %d on (%zd;%zd)\n", + fname, fl.l_pid, fl.l_start, fl.l_len); + } + return 0; diff --git a/nfs-utils-2.3.3-junction-update.patch b/nfs-utils-2.3.3-junction-update.patch new file mode 100644 index 0000000..6921df9 --- /dev/null +++ b/nfs-utils-2.3.3-junction-update.patch @@ -0,0 +1,162 @@ +diff --git a/aclocal/libxml2.m4 b/aclocal/libxml2.m4 +index 5c399b2..8231553 100644 +--- a/aclocal/libxml2.m4 ++++ b/aclocal/libxml2.m4 +@@ -1,15 +1,17 @@ + dnl Checks for libxml2.so + AC_DEFUN([AC_LIBXML2], [ + +- if test "$enable_junction" = yes; then ++ PKG_PROG_PKG_CONFIG([0.9.0]) ++ AS_IF( ++ [test "$enable_junction" = "yes"], ++ [PKG_CHECK_MODULES([XML2], [libxml-2.0 >= 2.4], ++ [LIBXML2="${XML2_LIBS}" ++ AM_CPPFLAGS="${AM_CPPFLAGS} ${XML2_CFLAGS}" ++ AC_DEFINE([HAVE_LIBXML2], [1], ++ [Define to 1 if you have and wish to use libxml2.])], ++ [AC_MSG_ERROR([libxml2 not found.])])]) + +- dnl look for the library; do not add to LIBS if found +- AC_CHECK_LIB([xml2], [xmlParseFile], [LIBXML2=-lxml2], +- [AC_MSG_ERROR([libxml2 not found.])]) +- AC_SUBST(LIBXML2) +- +- dnl XXX should also check for presence of xml headers +- +- fi ++ AC_SUBST([AM_CPPFLAGS]) ++ AC_SUBST(LIBXML2) + + ])dnl +diff --git a/configure.ac b/configure.ac +index cf1c4b9..b458891 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -165,7 +165,7 @@ AC_ARG_ENABLE(uuid, + choose_blkid=default) + AC_ARG_ENABLE(mount, + [AC_HELP_STRING([--disable-mount], +- [Don't build mount.nfs and do use the util-linux mount(8) functionality. @<:@default=no@:>@])], ++ [Do not build mount.nfs and do use the util-linux mount(8) functionality. @<:@default=no@:>@])], + enable_mount=$enableval, + enable_mount=yes) + AM_CONDITIONAL(CONFIG_MOUNT, [test "$enable_mount" = "yes"]) +@@ -185,7 +185,13 @@ AC_ARG_ENABLE(junction, + [enable support for NFS junctions @<:@default=no@:>@])], + enable_junction=$enableval, + enable_junction=no) +-AM_CONDITIONAL(CONFIG_JUNCTION, [test "$enable_junction" = "yes" ]) ++ if test "$enable_junction" = yes; then ++ AC_DEFINE(HAVE_JUNCTION_SUPPORT, 1, ++ [Define this if you want junction support compiled in]) ++ else ++ enable_junction= ++ fi ++ AM_CONDITIONAL(CONFIG_JUNCTION, [test "$enable_junction" = "yes" ]) + + AC_ARG_ENABLE(tirpc, + [AC_HELP_STRING([--disable-tirpc], +diff --git a/support/junction/Makefile.am b/support/junction/Makefile.am +index 97e7426..be6958b 100644 +--- a/support/junction/Makefile.am ++++ b/support/junction/Makefile.am +@@ -30,5 +30,3 @@ libjunction_la_SOURCES = display.c export-cache.c junction.c \ + locations.c nfs.c path.c xml.c + + MAINTAINERCLEANFILES = Makefile.in +- +-AM_CPPFLAGS = -I. -I../include -I/usr/include/libxml2 +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index 6f42512..7e8d403 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -976,8 +976,9 @@ lookup_export(char *dom, char *path, struct addrinfo *ai) + return found; + } + +-#ifdef CONFIG_JUNCTION ++#ifdef HAVE_JUNCTION_SUPPORT + ++#include + #include "junction.h" + + struct nfs_fsloc_set { +@@ -1084,8 +1085,7 @@ static bool locations_to_fslocdata(struct nfs_fsloc_set *locations, + *ttl = 0; + + for (;;) { +- enum jp_status status; +- int len; ++ int len, status; + + status = get_next_location(locations, &server, + &rootpath, ttl); +@@ -1219,7 +1219,7 @@ nfs_get_basic_junction(const char *junct_path, struct nfs_fsloc_set **locset) + return EINVAL; + } + +- locset->ns_current = locset->ns_list; ++ new->ns_current = new->ns_list; + new->ns_ttl = 300; + *locset = new; + return 0; +@@ -1242,7 +1242,7 @@ static struct exportent *lookup_junction(char *dom, const char *pathname, + status = nfs_get_basic_junction(pathname, &locations); + switch (status) { + xlog(L_WARNING, "Dangling junction %s: %s", +- pathname, strerro(status)); ++ pathname, strerror(status)); + goto out; + } + +@@ -1252,8 +1252,8 @@ static struct exportent *lookup_junction(char *dom, const char *pathname, + + exp = locations_to_export(locations, pathname, parent); + +- nfs_free_locations(locset->ns_list); +- free(locset); ++ nfs_free_locations(locations->ns_list); ++ free(locations); + + out: + xmlCleanupParser(); +@@ -1273,7 +1273,7 @@ static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path + free(eep); + } + +-#else /* !CONFIG_JUNCTION */ ++#else /* !HAVE_JUNCTION_SUPPORT */ + + static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path, + struct addrinfo *UNUSED(ai)) +@@ -1281,7 +1281,7 @@ static void lookup_nonexport(int f, char *buf, int buflen, char *dom, char *path + dump_to_cache(f, buf, buflen, dom, path, NULL, 0); + } + +-#endif /* !CONFIG_JUNCTION */ ++#endif /* !HAVE_JUNCTION_SUPPORT */ + + static void nfsd_export(int f) + { +diff --git a/utils/nfsref/Makefile.am b/utils/nfsref/Makefile.am +index 2b2bb53..2409dd0 100644 +--- a/utils/nfsref/Makefile.am ++++ b/utils/nfsref/Makefile.am +@@ -27,13 +27,11 @@ noinst_HEADERS = nfsref.h + + sbin_PROGRAMS = nfsref + nfsref_SOURCES = add.c lookup.c nfsref.c remove.c +-LDADD = $(LIBXML2) $(LIBCAP) \ +- ../../support/nfs/libnfs.la \ +- ../../support/junction/libjunction.la ++LDADD = ../../support/nfs/libnfs.la \ ++ ../../support/junction/libjunction.la \ ++ $(LIBXML2) $(LIBCAP) + + man8_MANS = nfsref.man + + MAINTAINERCLEANFILES = Makefile.in + +-AM_CPPFLAGS = -I. -I../../support/include +-##AM_LDFLAGS = -Wl,--as-needed diff --git a/nfs-utils-2.3.3-lseek-error-handling.patch b/nfs-utils-2.3.3-lseek-error-handling.patch new file mode 100644 index 0000000..af76564 --- /dev/null +++ b/nfs-utils-2.3.3-lseek-error-handling.patch @@ -0,0 +1,49 @@ +From fd2e952319c748e1c7babb1db97b371ebf6748a9 Mon Sep 17 00:00:00 2001 +From: Alice J Mitchell +Date: Mon, 29 Jul 2019 15:47:40 +0100 +Subject: [PATCH] Fix the error handling if the lseek fails + +The error case when lseek returns a negative value was not correctly handled, +and the error cleanup routine was potentially leaking memory also. + +Signed-off-by: Alice J Mitchell +--- + support/nfs/conffile.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c +index b6400be..6ba8a35 100644 +--- a/support/nfs/conffile.c ++++ b/support/nfs/conffile.c +@@ -500,7 +500,7 @@ conf_readfile(const char *path) + + if ((stat (path, &sb) == 0) || (errno != ENOENT)) { + char *new_conf_addr = NULL; +- size_t sz = sb.st_size; ++ off_t sz; + int fd = open (path, O_RDONLY, 0); + + if (fd == -1) { +@@ -517,6 +517,11 @@ conf_readfile(const char *path) + + /* only after we have the lock, check the file size ready to read it */ + sz = lseek(fd, 0, SEEK_END); ++ if (sz < 0) { ++ xlog_warn("conf_readfile: unable to determine file size: %s", ++ strerror(errno)); ++ goto fail; ++ } + lseek(fd, 0, SEEK_SET); + + new_conf_addr = malloc(sz+1); +@@ -2162,6 +2167,7 @@ conf_write(const char *filename, const char *section, const char *arg, + ret = 0; + + cleanup: ++ flush_outqueue(&inqueue, NULL); + flush_outqueue(&outqueue, NULL); + + if (buff) +-- +1.8.3.1 + diff --git a/nfs-utils-2.3.3-man-tcpwrappers.patch b/nfs-utils-2.3.3-man-tcpwrappers.patch new file mode 100644 index 0000000..65fee3a --- /dev/null +++ b/nfs-utils-2.3.3-man-tcpwrappers.patch @@ -0,0 +1,89 @@ +diff -up nfs-utils-2.3.3/utils/mountd/mountd.man.orig nfs-utils-2.3.3/utils/mountd/mountd.man +--- nfs-utils-2.3.3/utils/mountd/mountd.man.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/mountd/mountd.man 2018-10-26 09:53:10.005127368 -0400 +@@ -232,36 +232,7 @@ section include + which each have same same meaning as given by + .BR rpc.nfsd (8). + +-.SH TCP_WRAPPERS SUPPORT +-You can protect your +-.B rpc.mountd +-listeners using the +-.B tcp_wrapper +-library or +-.BR iptables (8). +-.PP +-Note that the +-.B tcp_wrapper +-library supports only IPv4 networking. +-.PP +-Add the hostnames of NFS peers that are allowed to access +-.B rpc.mountd +-to +-.IR /etc/hosts.allow . +-Use the daemon name +-.B mountd +-even if the +-.B rpc.mountd +-binary has a different name. +-.PP +-Hostnames used in either access file will be ignored when +-they can not be resolved into IP addresses. +-For further information see the +-.BR tcpd (8) +-and +-.BR hosts_access (5) +-man pages. +-.SS IPv6 and TI-RPC support ++.SH IPv6 and TI-RPC support + TI-RPC is a pre-requisite for supporting NFS on IPv6. + If TI-RPC support is built into + .BR rpc.mountd , +@@ -288,7 +259,6 @@ table of clients accessing server's expo + .BR nfs (5), + .BR nfs.conf (5), + .BR tcpd (8), +-.BR hosts_access (5), + .BR iptables (8), + .BR netconfig (5) + .sp +diff -up nfs-utils-2.3.3/utils/statd/statd.man.orig nfs-utils-2.3.3/utils/statd/statd.man +--- nfs-utils-2.3.3/utils/statd/statd.man.orig 2018-10-26 09:52:27.609358805 -0400 ++++ nfs-utils-2.3.3/utils/statd/statd.man 2018-10-26 09:53:37.345978117 -0400 +@@ -319,28 +319,6 @@ chooses, simply use + .BR chown (1) + to set the owner of + the state directory. +-.PP +-You can also protect your +-.B rpc.statd +-listeners using the +-.B tcp_wrapper +-library or +-.BR iptables (8). +-To use the +-.B tcp_wrapper +-library, add the hostnames of peers that should be allowed access to +-.IR /etc/hosts.allow . +-Use the daemon name +-.B statd +-even if the +-.B rpc.statd +-binary has a different filename. +-.P +-For further information see the +-.BR tcpd (8) +-and +-.BR hosts_access (5) +-man pages. + .SH ADDITIONAL NOTES + Lock recovery after a reboot is critical to maintaining data integrity + and preventing unnecessary application hangs. +@@ -445,7 +423,6 @@ network transport capability database + .BR rpc.nfsd (8), + .BR rpcbind (8), + .BR tcpd (8), +-.BR hosts_access (5), + .BR iptables (8), + .BR netconfig (5) + .sp diff --git a/nfs-utils-2.3.3-memleak-on-error.patch b/nfs-utils-2.3.3-memleak-on-error.patch new file mode 100644 index 0000000..e9bf332 --- /dev/null +++ b/nfs-utils-2.3.3-memleak-on-error.patch @@ -0,0 +1,37 @@ +From ccdd8c803182f5c172580379a56e84a23789cf0d Mon Sep 17 00:00:00 2001 +From: Alice J Mitchell +Date: Mon, 29 Jul 2019 15:49:34 +0100 +Subject: [PATCH] Fix memory leak on error in nfs-server-generator + +Fix the trivial memory leak in the error handling of nfs-server-generator + +Resolves: bz1440524 +Signed-off-by: Alice J Mitchell +--- + systemd/nfs-server-generator.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/systemd/nfs-server-generator.c b/systemd/nfs-server-generator.c +index 737f109..eec98fd 100644 +--- a/systemd/nfs-server-generator.c ++++ b/systemd/nfs-server-generator.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #include "misc.h" + #include "nfslib.h" +@@ -98,7 +99,7 @@ int main(int argc, char *argv[]) + exit(1); + } + +- path = malloc(strlen(argv[1]) + sizeof(dirbase) + sizeof(filebase)); ++ path = alloca(strlen(argv[1]) + sizeof(dirbase) + sizeof(filebase)); + if (!path) + exit(2); + if (export_read(_PATH_EXPORTS, 1) + +-- +1.8.3.1 + diff --git a/nfs-utils-2.3.3-mount-ebusy.patch b/nfs-utils-2.3.3-mount-ebusy.patch new file mode 100644 index 0000000..890c553 --- /dev/null +++ b/nfs-utils-2.3.3-mount-ebusy.patch @@ -0,0 +1,14 @@ +diff -up nfs-utils-2.3.3/utils/mount/stropts.c.orig nfs-utils-2.3.3/utils/mount/stropts.c +--- nfs-utils-2.3.3/utils/mount/stropts.c.orig 2022-02-14 11:28:51.570084952 -0500 ++++ nfs-utils-2.3.3/utils/mount/stropts.c 2022-02-14 11:29:16.174450628 -0500 +@@ -966,7 +966,9 @@ fall_back: + if ((result = nfs_try_mount_v3v2(mi, FALSE))) + return result; + +- errno = olderrno; ++ if (errno != EBUSY && errno != EACCES) ++ errno = olderrno; ++ + return result; + } + diff --git a/nfs-utils-2.3.3-mount-fallback.patch b/nfs-utils-2.3.3-mount-fallback.patch new file mode 100644 index 0000000..68c72ae --- /dev/null +++ b/nfs-utils-2.3.3-mount-fallback.patch @@ -0,0 +1,48 @@ +commit a709f25c1da4a2fb44a1f3fd060298fbbd88aa3c +Author: Steve Dickson +Date: Tue May 14 15:52:50 2019 -0400 + + mount: Report correct error in the fall_back cases. + + In mount auto negotiation, a v3 mount is tried + when the v4 fails with error that could mean + v4 is not supported. + + When the v3 mount fails, the original v4 failure + should be used to set the errno, not the v3 failure. + + Fixes:https://bugzilla.redhat.com/show_bug.cgi?id=1709961 + Signed-off-by: Steve Dickson + +diff -up nfs-utils-2.3.3/utils/mount/stropts.c.orig nfs-utils-2.3.3/utils/mount/stropts.c +--- nfs-utils-2.3.3/utils/mount/stropts.c.orig 2019-08-12 10:58:32.610650773 -0400 ++++ nfs-utils-2.3.3/utils/mount/stropts.c 2019-08-12 11:10:39.661142985 -0400 +@@ -888,7 +888,7 @@ out: + */ + static int nfs_autonegotiate(struct nfsmount_info *mi) + { +- int result; ++ int result, olderrno; + + result = nfs_try_mount_v4(mi); + check_result: +@@ -948,7 +948,18 @@ fall_back: + if (mi->version.v_mode == V_GENERAL) + /* v2,3 fallback not allowed */ + return result; +- return nfs_try_mount_v3v2(mi, FALSE); ++ ++ /* ++ * Save the original errno in case the v3 ++ * mount fails from one of the fall_back cases. ++ * Report the first failure not the v3 mount failure ++ */ ++ olderrno = errno; ++ if ((result = nfs_try_mount_v3v2(mi, FALSE))) ++ return result; ++ ++ errno = olderrno; ++ return result; + } + + /* diff --git a/nfs-utils-2.3.3-mount-sharecache.patch b/nfs-utils-2.3.3-mount-sharecache.patch new file mode 100644 index 0000000..9737a08 --- /dev/null +++ b/nfs-utils-2.3.3-mount-sharecache.patch @@ -0,0 +1,47 @@ +diff -up nfs-utils-2.3.3/utils/mount/Makefile.am.orig nfs-utils-2.3.3/utils/mount/Makefile.am +--- nfs-utils-2.3.3/utils/mount/Makefile.am.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/mount/Makefile.am 2018-10-25 10:27:33.881804941 -0400 +@@ -27,6 +27,7 @@ endif + + mount_nfs_LDADD = ../../support/nfs/libnfs.la \ + ../../support/export/libexport.a \ ++ ../../support/misc/libmisc.a \ + $(LIBTIRPC) + + mount_nfs_SOURCES = $(mount_common) +diff -up nfs-utils-2.3.3/utils/mount/stropts.c.orig nfs-utils-2.3.3/utils/mount/stropts.c +--- nfs-utils-2.3.3/utils/mount/stropts.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/utils/mount/stropts.c 2018-10-25 10:27:59.733825016 -0400 +@@ -48,6 +48,7 @@ + #include "version.h" + #include "parse_dev.h" + #include "conffile.h" ++#include "misc.h" + + #ifndef NFS_PROGRAM + #define NFS_PROGRAM (100003) +@@ -1078,14 +1079,18 @@ static int nfsmount_fg(struct nfsmount_i + if (nfs_try_mount(mi)) + return EX_SUCCESS; + +- if (errno == EBUSY) +- /* The only cause of EBUSY is if exactly the desired +- * filesystem is already mounted. That can arguably +- * be seen as success. "mount -a" tries to optimise +- * out this case but sometimes fails. Help it out +- * by pretending everything is rosy ++#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" ++ if (errno == EBUSY && is_mountpoint(mi->node)) { ++#pragma GCC diagnostic warning "-Wdiscarded-qualifiers" ++ /* ++ * EBUSY can happen when mounting a filesystem that ++ * is already mounted or when the context= are ++ * different when using the -o sharecache ++ * ++ * Only error out in the latter case. + */ + return EX_SUCCESS; ++ } + + if (nfs_is_permanent_error(errno)) + break; diff --git a/nfs-utils-2.3.3-mount-sloppy.patch b/nfs-utils-2.3.3-mount-sloppy.patch new file mode 100644 index 0000000..e734c74 --- /dev/null +++ b/nfs-utils-2.3.3-mount-sloppy.patch @@ -0,0 +1,116 @@ +diff -up nfs-utils-2.3.3/utils/mount/nfs.man.save nfs-utils-2.3.3/utils/mount/nfs.man +--- nfs-utils-2.3.3/utils/mount/nfs.man.save 2021-07-28 14:42:20.977740892 -0400 ++++ nfs-utils-2.3.3/utils/mount/nfs.man 2021-07-28 14:42:01.133212815 -0400 +@@ -525,6 +525,13 @@ using the FS-Cache facility. See cachefi + and /Documentation/filesystems/caching + for detail on how to configure the FS-Cache facility. + Default value is nofsc. ++.TP 1.5i ++.B sloppy ++The ++.B sloppy ++option is an alternative to specifying ++.BR mount.nfs " -s " option. ++ + .SS "Options for NFS versions 2 and 3 only" + Use these options, along with the options in the above subsection, + for NFS versions 2 and 3 only. +diff -up nfs-utils-2.3.3/utils/mount/parse_opt.c.save nfs-utils-2.3.3/utils/mount/parse_opt.c +--- nfs-utils-2.3.3/utils/mount/parse_opt.c.save 2021-07-28 14:40:15.467400995 -0400 ++++ nfs-utils-2.3.3/utils/mount/parse_opt.c 2021-07-28 14:39:57.666927309 -0400 +@@ -178,6 +178,22 @@ static void options_tail_insert(struct m + options->count++; + } + ++static void options_head_insert(struct mount_options *options, ++ struct mount_option *option) ++{ ++ struct mount_option *ohead = options->head; ++ ++ option->prev = NULL; ++ option->next = ohead; ++ if (ohead) ++ ohead->prev = option; ++ else ++ options->tail = option; ++ options->head = option; ++ ++ options->count++; ++} ++ + static void options_delete(struct mount_options *options, + struct mount_option *option) + { +@@ -374,6 +390,23 @@ po_return_t po_join(struct mount_options + } + + /** ++ * po_insert - insert an option into a group of options ++ * @options: pointer to mount options ++ * @option: pointer to a C string containing the option to add ++ * ++ */ ++po_return_t po_insert(struct mount_options *options, char *str) ++{ ++ struct mount_option *option = option_create(str); ++ ++ if (option) { ++ options_head_insert(options, option); ++ return PO_SUCCEEDED; ++ } ++ return PO_FAILED; ++} ++ ++/** + * po_append - concatenate an option onto a group of options + * @options: pointer to mount options + * @option: pointer to a C string containing the option to add +diff -up nfs-utils-2.3.3/utils/mount/parse_opt.h.save nfs-utils-2.3.3/utils/mount/parse_opt.h +--- nfs-utils-2.3.3/utils/mount/parse_opt.h.save 2021-07-28 14:40:54.292434148 -0400 ++++ nfs-utils-2.3.3/utils/mount/parse_opt.h 2021-07-28 14:39:57.666927309 -0400 +@@ -43,6 +43,7 @@ void po_replace(struct mount_options * + struct mount_options *); + po_return_t po_join(struct mount_options *, char **); + ++po_return_t po_insert(struct mount_options *, char *); + po_return_t po_append(struct mount_options *, char *); + po_found_t po_contains(struct mount_options *, char *); + po_found_t po_contains_prefix(struct mount_options *options, +diff -up nfs-utils-2.3.3/utils/mount/stropts.c.save nfs-utils-2.3.3/utils/mount/stropts.c +--- nfs-utils-2.3.3/utils/mount/stropts.c.save 2021-07-28 14:41:14.842981010 -0400 ++++ nfs-utils-2.3.3/utils/mount/stropts.c 2021-07-28 14:42:01.134212842 -0400 +@@ -336,13 +336,21 @@ static int nfs_verify_lock_option(struct + return 1; + } + +-static int nfs_append_sloppy_option(struct mount_options *options) ++static int nfs_insert_sloppy_option(struct mount_options *options) + { +- if (!sloppy || linux_version_code() < MAKE_VERSION(2, 6, 27)) ++ if (linux_version_code() < MAKE_VERSION(2, 6, 27)) + return 1; + +- if (po_append(options, "sloppy") == PO_FAILED) +- return 0; ++ if (po_contains(options, "sloppy")) { ++ po_remove_all(options, "sloppy"); ++ sloppy++; ++ } ++ ++ if (sloppy) { ++ if (po_insert(options, "sloppy") == PO_FAILED) ++ return 0; ++ } ++ + return 1; + } + +@@ -424,7 +432,7 @@ static int nfs_validate_options(struct n + if (!nfs_set_version(mi)) + return 0; + +- if (!nfs_append_sloppy_option(mi->options)) ++ if (!nfs_insert_sloppy_option(mi->options)) + return 0; + + return 1; diff --git a/nfs-utils-2.3.3-mountd-memleak.patch b/nfs-utils-2.3.3-mountd-memleak.patch new file mode 100644 index 0000000..d52d063 --- /dev/null +++ b/nfs-utils-2.3.3-mountd-memleak.patch @@ -0,0 +1,77 @@ +commit 50ef80739d9e1e0df6616289ef2ff626a94666ee +Author: Steve Dickson +Date: Thu May 23 09:24:49 2019 -0400 + + rpc.mountd: Fix e_hostname and e_uuid leaks + + strdup of exportent uuid and hostname in getexportent() ends up leaking + memory. Free the memory before getexportent() is called again from xtab_read() + + Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1713360 + Signed-off-by: Nikhil Kshirsagar + Signed-off-by: Steve Dickson + +diff --git a/support/export/xtab.c b/support/export/xtab.c +index d42eeef..1e1d679 100644 +--- a/support/export/xtab.c ++++ b/support/export/xtab.c +@@ -50,6 +50,14 @@ xtab_read(char *xtab, char *lockfn, int is_export) + while ((xp = getexportent(is_export==0, 0)) != NULL) { + if (!(exp = export_lookup(xp->e_hostname, xp->e_path, is_export != 1)) && + !(exp = export_create(xp, is_export!=1))) { ++ if(xp->e_hostname) { ++ free(xp->e_hostname); ++ xp->e_hostname=NULL; ++ } ++ if(xp->e_uuid) { ++ free(xp->e_uuid); ++ xp->e_uuid=NULL; ++ } + continue; + } + switch (is_export) { +@@ -62,7 +70,16 @@ xtab_read(char *xtab, char *lockfn, int is_export) + if ((xp->e_flags & NFSEXP_FSID) && xp->e_fsid == 0) + v4root_needed = 0; + break; +- } ++ } ++ if(xp->e_hostname) { ++ free(xp->e_hostname); ++ xp->e_hostname=NULL; ++ } ++ if(xp->e_uuid) { ++ free(xp->e_uuid); ++ xp->e_uuid=NULL; ++ } ++ + } + endexportent(); + xfunlock(lockid); +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index 5f4cb95..a7582ca 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -179,9 +179,20 @@ getexportent(int fromkernel, int fromexports) + } + ee.e_hostname = xstrdup(hostname); + +- if (parseopts(opt, &ee, fromexports && !has_default_subtree_opts, NULL) < 0) +- return NULL; ++ if (parseopts(opt, &ee, fromexports && !has_default_subtree_opts, NULL) < 0) { ++ if(ee.e_hostname) ++ { ++ xfree(ee.e_hostname); ++ ee.e_hostname=NULL; ++ } ++ if(ee.e_uuid) ++ { ++ xfree(ee.e_uuid); ++ ee.e_uuid=NULL; ++ } + ++ return NULL; ++ } + /* resolve symlinks */ + if (realpath(ee.e_path, rpath) != NULL) { + rpath[sizeof (rpath) - 1] = '\0'; diff --git a/nfs-utils-2.3.3-mountd-pseudofs.patch b/nfs-utils-2.3.3-mountd-pseudofs.patch new file mode 100644 index 0000000..04eb082 --- /dev/null +++ b/nfs-utils-2.3.3-mountd-pseudofs.patch @@ -0,0 +1,61 @@ +diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c +index d735dbfe..8ec33fb0 100644 +--- a/utils/mountd/v4root.c ++++ b/utils/mountd/v4root.c +@@ -36,9 +36,9 @@ static nfs_export pseudo_root = { + .m_export = { + .e_hostname = "*", + .e_path = "/", +- .e_flags = NFSEXP_READONLY | NFSEXP_ROOTSQUASH ++ .e_flags = NFSEXP_READONLY + | NFSEXP_NOSUBTREECHECK | NFSEXP_FSID +- | NFSEXP_V4ROOT, ++ | NFSEXP_V4ROOT | NFSEXP_INSECURE_PORT, + .e_anonuid = 65534, + .e_anongid = 65534, + .e_squids = NULL, +@@ -57,15 +57,11 @@ static nfs_export pseudo_root = { + }; + + static void +-set_pseudofs_security(struct exportent *pseudo, int flags) ++set_pseudofs_security(struct exportent *pseudo) + { + struct flav_info *flav; + int i; + +- if (flags & NFSEXP_INSECURE_PORT) +- pseudo->e_flags |= NFSEXP_INSECURE_PORT; +- if ((flags & NFSEXP_ROOTSQUASH) == 0) +- pseudo->e_flags &= ~NFSEXP_ROOTSQUASH; + for (flav = flav_map; flav < flav_map + flav_map_size; flav++) { + struct sec_entry *new; + +@@ -75,8 +71,7 @@ set_pseudofs_security(struct exportent *pseudo, int flags) + i = secinfo_addflavor(flav, pseudo); + new = &pseudo->e_secinfo[i]; + +- if (flags & NFSEXP_INSECURE_PORT) +- new->flags |= NFSEXP_INSECURE_PORT; ++ new->flags |= NFSEXP_INSECURE_PORT; + } + } + +@@ -95,7 +90,7 @@ v4root_create(char *path, nfs_export *export) + strncpy(eep.e_path, path, sizeof(eep.e_path)-1); + if (strcmp(path, "/") != 0) + eep.e_flags &= ~NFSEXP_FSID; +- set_pseudofs_security(&eep, curexp->e_flags); ++ set_pseudofs_security(&eep); + exp = export_create(&eep, 0); + if (exp == NULL) + return NULL; +@@ -143,7 +138,7 @@ pseudofs_update(char *hostname, char *path, nfs_export *source) + return 0; + } + /* Update an existing V4ROOT export: */ +- set_pseudofs_security(&exp->m_export, source->m_export.e_flags); ++ set_pseudofs_security(&exp->m_export); + return 0; + } + diff --git a/nfs-utils-2.3.3-mountd-v4-logging.patch b/nfs-utils-2.3.3-mountd-v4-logging.patch new file mode 100644 index 0000000..59c823d --- /dev/null +++ b/nfs-utils-2.3.3-mountd-v4-logging.patch @@ -0,0 +1,976 @@ +diff --git a/nfs.conf b/nfs.conf +index 05247ff9..86ed7d53 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -38,6 +38,8 @@ use-gss-proxy=1 + # reverse-lookup=n + # state-directory-path=/var/lib/nfs + # ha-callout= ++# cache-use-ipaddr=n ++# ttl=1800 + # + [nfsdcld] + # debug=0 +diff --git a/support/export/Makefile.am b/support/export/Makefile.am +index 13f7a49c..d6ee502f 100644 +--- a/support/export/Makefile.am ++++ b/support/export/Makefile.am +@@ -11,7 +11,8 @@ EXTRA_DIST = mount.x + + noinst_LIBRARIES = libexport.a + libexport_a_SOURCES = client.c export.c hostname.c \ +- xtab.c mount_clnt.c mount_xdr.c ++ xtab.c mount_clnt.c mount_xdr.c \ ++ cache.c auth.c v4root.c v4clients.c + BUILT_SOURCES = $(GENFILES) + + noinst_HEADERS = mount.h +diff --git a/utils/mountd/auth.c b/support/export/auth.c +similarity index 98% +rename from utils/mountd/auth.c +rename to support/export/auth.c +index 8299256e..73ad6f73 100644 +--- a/utils/mountd/auth.c ++++ b/support/export/auth.c +@@ -22,7 +22,7 @@ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +-#include "mountd.h" ++#include "export.h" + #include "v4root.h" + + enum auth_error +@@ -43,11 +43,13 @@ extern int use_ipaddr; + + extern struct state_paths etab; + ++/* + void + auth_init(void) + { + auth_reload(); + } ++*/ + + /* + * A client can match many different netgroups and it's tough to know +@@ -64,6 +66,10 @@ check_useipaddr(void) + int old_use_ipaddr = use_ipaddr; + unsigned int len = 0; + ++ if (use_ipaddr > 1) ++ /* fixed - don't check */ ++ return; ++ + /* add length of m_hostname + 1 for the comma */ + for (clp = clientlist[MCL_NETGROUP]; clp; clp = clp->m_next) + len += (strlen(clp->m_hostname) + 1); +diff --git a/utils/mountd/cache.c b/support/export/cache.c +similarity index 95% +rename from utils/mountd/cache.c +rename to support/export/cache.c +index c73e29be..98d50828 100644 +--- a/utils/mountd/cache.c ++++ b/support/export/cache.c +@@ -29,21 +29,18 @@ + #include "misc.h" + #include "nfslib.h" + #include "exportfs.h" +-#include "mountd.h" +-#include "fsloc.h" ++#include "export.h" + #include "pseudoflavors.h" + #include "xcommon.h" + ++#ifdef HAVE_JUNCTION_SUPPORT ++#include "../../utils/mountd/fsloc.h" ++#endif ++ + #ifdef USE_BLKID + #include "blkid/blkid.h" + #endif + +-/* +- * Invoked by RPC service loop +- */ +-void cache_set_fds(fd_set *fdset); +-int cache_process_req(fd_set *readfds); +- + enum nfsd_fsid { + FSID_DEV = 0, + FSID_NUM, +@@ -63,7 +60,6 @@ enum nfsd_fsid { + * Record is terminated with newline. + * + */ +-static int cache_export_ent(char *buf, int buflen, char *domain, struct exportent *exp, char *path); + + #define INITIAL_MANAGED_GROUPS 100 + +@@ -81,6 +77,7 @@ static void auth_unix_ip(int f) + char class[20]; + char ipaddr[INET6_ADDRSTRLEN + 1]; + char *client = NULL; ++ struct addrinfo *ai = NULL; + struct addrinfo *tmp = NULL; + char buf[RPC_CHAN_BUF_SIZE], *bp; + int blen; +@@ -106,21 +103,26 @@ static void auth_unix_ip(int f) + + auth_reload(); + +- /* addr is a valid, interesting address, find the domain name... */ +- if (!use_ipaddr) { +- struct addrinfo *ai = NULL; +- +- ai = client_resolve(tmp->ai_addr); +- if (ai) { +- client = client_compose(ai); +- freeaddrinfo(ai); +- } ++ /* addr is a valid address, find the domain name... */ ++ ai = client_resolve(tmp->ai_addr); ++ if (ai) { ++ client = client_compose(ai); ++ freeaddrinfo(ai); + } ++ if (!client) ++ xlog(D_AUTH, "failed authentication for IP %s", ipaddr); ++ else if (!use_ipaddr) ++ xlog(D_AUTH, "successful authentication for IP %s as %s", ++ ipaddr, *client ? client : "DEFAULT"); ++ else ++ xlog(D_AUTH, "successful authentication for IP %s", ++ ipaddr); ++ + bp = buf; blen = sizeof(buf); + qword_add(&bp, &blen, "nfsd"); + qword_add(&bp, &blen, ipaddr); +- qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL); +- if (use_ipaddr) { ++ qword_adduint(&bp, &blen, time(0) + default_ttl); ++ if (use_ipaddr && client) { + memmove(ipaddr + 1, ipaddr, strlen(ipaddr) + 1); + ipaddr[0] = '$'; + qword_add(&bp, &blen, ipaddr); +@@ -192,7 +194,7 @@ static void auth_unix_gid(int f) + + bp = buf; blen = sizeof(buf); + qword_adduint(&bp, &blen, uid); +- qword_adduint(&bp, &blen, time(0) + DEFAULT_TTL); ++ qword_adduint(&bp, &blen, time(0) + default_ttl); + if (rv >= 0) { + qword_adduint(&bp, &blen, ngroups); + for (i=0; im_export.e_mountpoint && +- !is_mountpoint(exp->m_export.e_mountpoint[0]? +- exp->m_export.e_mountpoint: +- exp->m_export.e_path)) +- dev_missing ++; + + if (!match_fsid(&parsed, exp, path)) + continue; +@@ -794,7 +790,7 @@ static void nfsd_fh(int f) + !is_mountpoint(found->e_mountpoint[0]? + found->e_mountpoint: + found->e_path)) { +- /* Cannot export this yet ++ /* Cannot export this yet + * should log a warning, but need to rate limit + xlog(L_WARNING, "%s not exported as %d not a mountpoint", + found->e_path, found->e_mountpoint); +@@ -802,16 +798,6 @@ static void nfsd_fh(int f) + /* FIXME we need to make sure we re-visit this later */ + goto out; + } +- if (!found && dev_missing) { +- /* The missing dev could be what we want, so just be +- * quite rather than returning stale yet +- */ +- goto out; +- } +- +- if (found) +- if (cache_export_ent(buf, sizeof(buf), dom, found, found_path) < 0) +- found = 0; + + bp = buf; blen = sizeof(buf); + qword_add(&bp, &blen, dom); +@@ -831,6 +817,8 @@ static void nfsd_fh(int f) + qword_addeol(&bp, &blen); + if (blen <= 0 || write(f, buf, bp - buf) != bp - buf) + xlog(L_ERROR, "nfsd_fh: error writing reply"); ++ if (!found) ++ xlog(D_AUTH, "denied access to %s", *dom == '$' ? dom+1 : dom); + out: + if (found_path) + free(found_path); +@@ -839,6 +827,7 @@ out: + xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL); + } + ++#ifdef HAVE_JUNCTION_SUPPORT + static void write_fsloc(char **bp, int *blen, struct exportent *ep) + { + struct servers *servers; +@@ -861,7 +850,7 @@ static void write_fsloc(char **bp, int *blen, struct exportent *ep) + qword_addint(bp, blen, servers->h_referral); + release_replicas(servers); + } +- ++#endif + static void write_secinfo(char **bp, int *blen, struct exportent *ep, int flag_mask) + { + struct sec_entry *p; +@@ -890,7 +879,7 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain, + time_t now = time(0); + + if (ttl <= 1) +- ttl = DEFAULT_TTL; ++ ttl = default_ttl; + + qword_add(&bp, &blen, domain); + qword_add(&bp, &blen, path); +@@ -903,7 +892,10 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain, + qword_addint(&bp, &blen, exp->e_anonuid); + qword_addint(&bp, &blen, exp->e_anongid); + qword_addint(&bp, &blen, exp->e_fsid); ++ ++#ifdef HAVE_JUNCTION_SUPPORT + write_fsloc(&bp, &blen, exp); ++#endif + write_secinfo(&bp, &blen, exp, flag_mask); + if (exp->e_uuid == NULL || different_fs) { + char u[16]; +@@ -917,8 +909,13 @@ static int dump_to_cache(int f, char *buf, int buflen, char *domain, + qword_add(&bp, &blen, "uuid"); + qword_addhex(&bp, &blen, u, 16); + } +- } else ++ xlog(D_AUTH, "granted access to %s for %s", ++ path, *domain == '$' ? domain+1 : domain); ++ } else { + qword_adduint(&bp, &blen, now + ttl); ++ xlog(D_AUTH, "denied access to %s for %s", ++ path, *domain == '$' ? domain+1 : domain); ++ } + qword_addeol(&bp, &blen); + if (blen <= 0) return -1; + if (write(f, buf, bp - buf) != bp - buf) return -1; +@@ -1421,6 +1418,40 @@ int cache_process_req(fd_set *readfds) + return cnt; + } + ++/** ++ * cache_process_loop - process incoming upcalls ++ */ ++void cache_process_loop(void) ++{ ++ fd_set readfds; ++ int selret; ++ ++ FD_ZERO(&readfds); ++ ++ for (;;) { ++ ++ cache_set_fds(&readfds); ++ v4clients_set_fds(&readfds); ++ ++ selret = select(FD_SETSIZE, &readfds, ++ (void *) 0, (void *) 0, (struct timeval *) 0); ++ ++ ++ switch (selret) { ++ case -1: ++ if (errno == EINTR || errno == ECONNREFUSED ++ || errno == ENETUNREACH || errno == EHOSTUNREACH) ++ continue; ++ xlog(L_ERROR, "my_svc_run() - select: %m"); ++ return; ++ ++ default: ++ cache_process_req(&readfds); ++ v4clients_process(&readfds); ++ } ++ } ++} ++ + + /* + * Give IP->domain and domain+path->options to kernel +diff --git a/support/export/export.h b/support/export/export.h +new file mode 100644 +index 00000000..8d5a0d30 +--- /dev/null ++++ b/support/export/export.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (C) 2021 Red Hat ++ * ++ * support/export/export.h ++ * ++ * Declarations for export support ++ */ ++ ++#ifndef EXPORT_H ++#define EXPORT_H ++ ++#include "nfslib.h" ++#include "exportfs.h" ++ ++unsigned int auth_reload(void); ++nfs_export * auth_authenticate(const char *what, ++ const struct sockaddr *caller, ++ const char *path); ++ ++void cache_open(void); ++void cache_set_fds(fd_set *fdset); ++int cache_process_req(fd_set *readfds); ++void cache_process_loop(void); ++ ++void v4clients_init(void); ++void v4clients_set_fds(fd_set *fdset); ++int v4clients_process(fd_set *fdset); ++ ++struct nfs_fh_len * ++ cache_get_filehandle(nfs_export *exp, int len, char *p); ++int cache_export(nfs_export *exp, char *path); ++ ++bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai); ++bool namelist_client_matches(nfs_export *exp, char *dom); ++bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai); ++ ++static inline bool is_ipaddr_client(char *dom) ++{ ++ return dom[0] == '$'; ++} ++#endif /* EXPORT__H */ +diff --git a/support/export/v4clients.c b/support/export/v4clients.c +new file mode 100644 +index 00000000..dd985463 +--- /dev/null ++++ b/support/export/v4clients.c +@@ -0,0 +1,227 @@ ++/* ++ * support/export/v4clients.c ++ * ++ * Montior clients appearing in, and disappearing from, /proc/fs/nfsd/clients ++ * and log relevant information. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "export.h" ++ ++/* search.h declares 'struct entry' and nfs_prot.h ++ * does too. Easiest fix is to trick search.h into ++ * calling its struct "struct Entry". ++ */ ++#define entry Entry ++#include ++#undef entry ++ ++static int clients_fd = -1; ++ ++void v4clients_init(void) ++{ ++ if (clients_fd >= 0) ++ return; ++ clients_fd = inotify_init1(IN_NONBLOCK); ++ if (clients_fd < 0) { ++ xlog_err("Unable to initialise v4clients watcher: %s\n", ++ strerror(errno)); ++ return; ++ } ++ if (inotify_add_watch(clients_fd, "/proc/fs/nfsd/clients", ++ IN_CREATE | IN_DELETE) < 0) { ++ xlog_err("Unable to watch /proc/fs/nfsd/clients: %s\n", ++ strerror(errno)); ++ close(clients_fd); ++ clients_fd = -1; ++ return; ++ } ++} ++ ++void v4clients_set_fds(fd_set *fdset) ++{ ++ if (clients_fd >= 0) ++ FD_SET(clients_fd, fdset); ++} ++ ++static void *tree_root; ++static int have_unconfirmed; ++ ++struct ent { ++ unsigned long num; ++ char *clientid; ++ char *addr; ++ int vers; ++ int unconfirmed; ++ int wid; ++}; ++ ++static int ent_cmp(const void *av, const void *bv) ++{ ++ const struct ent *a = av; ++ const struct ent *b = bv; ++ ++ if (a->num < b->num) ++ return -1; ++ if (a->num > b->num) ++ return 1; ++ return 0; ++} ++ ++static void free_ent(struct ent *ent) ++{ ++ free(ent->clientid); ++ free(ent->addr); ++ free(ent); ++} ++ ++static char *dup_line(char *line) ++{ ++ char *ret; ++ char *e = strchr(line, '\n'); ++ if (!e) ++ e = line + strlen(line); ++ ret = malloc(e - line + 1); ++ if (ret) { ++ memcpy(ret, line, e - line); ++ ret[e-line] = 0; ++ } ++ return ret; ++} ++ ++static void read_info(struct ent *key) ++{ ++ char buf[2048]; ++ char *path; ++ int was_unconfirmed = key->unconfirmed; ++ FILE *f; ++ ++ if (asprintf(&path, "/proc/fs/nfsd/clients/%lu/info", key->num) < 0) ++ return; ++ ++ f = fopen(path, "r"); ++ if (!f) { ++ free(path); ++ return; ++ } ++ if (key->wid < 0) ++ key->wid = inotify_add_watch(clients_fd, path, IN_MODIFY); ++ ++ while (fgets(buf, sizeof(buf), f)) { ++ if (strncmp(buf, "clientid: ", 10) == 0) { ++ free(key->clientid); ++ key->clientid = dup_line(buf+10); ++ } ++ if (strncmp(buf, "address: ", 9) == 0) { ++ free(key->addr); ++ key->addr = dup_line(buf+9); ++ } ++ if (strncmp(buf, "minor version: ", 15) == 0) ++ key->vers = atoi(buf+15); ++ if (strncmp(buf, "status: ", 8) == 0 && ++ strstr(buf, " unconfirmed") != NULL) { ++ key->unconfirmed = 1; ++ have_unconfirmed = 1; ++ } ++ if (strncmp(buf, "status: ", 8) == 0 && ++ strstr(buf, " confirmed") != NULL) ++ key->unconfirmed = 0; ++ } ++ fclose(f); ++ free(path); ++ ++ if (was_unconfirmed && !key->unconfirmed) ++ xlog(L_NOTICE, "v4.%d client attached: %s from %s", ++ key->vers, key->clientid ?: "-none-", ++ key->addr ?: "-none-"); ++ if (!key->unconfirmed && key->wid >= 0) { ++ inotify_rm_watch(clients_fd, key->wid); ++ key->wid = -1; ++ } ++} ++ ++static void add_id(int id) ++{ ++ struct ent **ent; ++ struct ent *key; ++ ++ key = calloc(1, sizeof(*key)); ++ if (!key) { ++ return; ++ } ++ key->num = id; ++ key->wid = -1; ++ ++ ent = tsearch(key, &tree_root, ent_cmp); ++ ++ if (!ent || *ent != key) ++ /* Already existed, or insertion failed */ ++ free_ent(key); ++ else ++ read_info(key); ++} ++ ++static void del_id(unsigned long id) ++{ ++ struct ent key = {.num = id}; ++ struct ent **e, *ent; ++ ++ e = tfind(&key, &tree_root, ent_cmp); ++ if (!e || !*e) ++ return; ++ ent = *e; ++ tdelete(ent, &tree_root, ent_cmp); ++ if (!ent->unconfirmed) ++ xlog(L_NOTICE, "v4.%d client detached: %s from %s", ++ ent->vers, ent->clientid, ent->addr); ++ if (ent->wid >= 0) ++ inotify_rm_watch(clients_fd, ent->wid); ++ free_ent(ent); ++} ++ ++static void check_id(unsigned long id) ++{ ++ struct ent key = {.num = id}; ++ struct ent **e, *ent; ++ ++ e = tfind(&key, &tree_root, ent_cmp); ++ if (!e || !*e) ++ return; ++ ent = *e; ++ if (ent->unconfirmed) ++ read_info(ent); ++} ++ ++int v4clients_process(fd_set *fdset) ++{ ++ char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event)))); ++ const struct inotify_event *ev; ++ ssize_t len; ++ char *ptr; ++ ++ if (clients_fd < 0 || ++ !FD_ISSET(clients_fd, fdset)) ++ return 0; ++ ++ while ((len = read(clients_fd, buf, sizeof(buf))) > 0) { ++ for (ptr = buf; ptr < buf + len; ++ ptr += sizeof(struct inotify_event) + ev->len) { ++ int id; ++ ev = (const struct inotify_event *)ptr; ++ ++ id = atoi(ev->name); ++ if (id <= 0) ++ continue; ++ if (ev->mask & IN_CREATE) ++ add_id(id); ++ if (ev->mask & IN_DELETE) ++ del_id(id); ++ if (ev->mask & IN_MODIFY) ++ check_id(id); ++ } ++ } ++ return 1; ++} +diff --git a/utils/mountd/v4root.c b/support/export/v4root.c +similarity index 99% +rename from utils/mountd/v4root.c +rename to support/export/v4root.c +index 8ec33fb0..4d33117f 100644 +--- a/utils/mountd/v4root.c ++++ b/support/export/v4root.c +@@ -47,7 +47,7 @@ static nfs_export pseudo_root = { + .e_nsqgids = 0, + .e_fsid = 0, + .e_mountpoint = NULL, +- .e_ttl = DEFAULT_TTL, ++ .e_ttl = 0, + }, + .m_exported = 0, + .m_xtabent = 1, +@@ -86,6 +86,7 @@ v4root_create(char *path, nfs_export *export) + struct exportent *curexp = &export->m_export; + + dupexportent(&eep, &pseudo_root.m_export); ++ eep.e_ttl = default_ttl; + eep.e_hostname = curexp->e_hostname; + strncpy(eep.e_path, path, sizeof(eep.e_path)-1); + if (strcmp(path, "/") != 0) +diff --git a/support/include/exportfs.h b/support/include/exportfs.h +index 4e0d9d13..bfae1957 100644 +--- a/support/include/exportfs.h ++++ b/support/include/exportfs.h +@@ -105,7 +105,8 @@ typedef struct mexport { + } nfs_export; + + #define HASH_TABLE_SIZE 1021 +-#define DEFAULT_TTL (30 * 60) ++ ++extern int default_ttl; + + typedef struct _exp_hash_entry { + nfs_export * p_first; +diff --git a/support/nfs/exports.c b/support/nfs/exports.c +index a7582cae..4dd2e5d3 100644 +--- a/support/nfs/exports.c ++++ b/support/nfs/exports.c +@@ -47,6 +47,8 @@ struct flav_info flav_map[] = { + + const int flav_map_size = sizeof(flav_map)/sizeof(flav_map[0]); + ++int default_ttl = 30 * 60; ++ + static char *efname = NULL; + static XFILE *efp = NULL; + static int first; +@@ -100,7 +102,7 @@ static void init_exportent (struct exportent *ee, int fromkernel) + ee->e_nsquids = 0; + ee->e_nsqgids = 0; + ee->e_uuid = NULL; +- ee->e_ttl = DEFAULT_TTL; ++ ee->e_ttl = default_ttl; + } + + struct exportent * +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index 498d93a9..aa4630bb 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -157,6 +157,8 @@ Recognized values: + .BR port , + .BR threads , + .BR reverse-lookup , ++.BR cache-use-upaddr , ++.BR ttl , + .BR state-directory-path , + .BR ha-callout . + +@@ -166,6 +168,14 @@ section, are used to configure mountd. See + .BR rpc.mountd (8) + for details. + ++Note that setting ++.B "\[dq]debug = auth\[dq]" ++for ++.B mountd ++is equivalent to providing the ++.B \-\-log\-auth ++option. ++ + The + .B state-directory-path + value in the +diff --git a/utils/mountd/Makefile.am b/utils/mountd/Makefile.am +index 73eeb3f3..c41f06de 100644 +--- a/utils/mountd/Makefile.am ++++ b/utils/mountd/Makefile.am +@@ -13,8 +13,8 @@ KPREFIX = @kprefix@ + sbin_PROGRAMS = mountd + + noinst_HEADERS = fsloc.h +-mountd_SOURCES = mountd.c mount_dispatch.c auth.c rmtab.c cache.c \ +- svc_run.c fsloc.c v4root.c mountd.h ++mountd_SOURCES = mountd.c mount_dispatch.c rmtab.c \ ++ svc_run.c fsloc.c mountd.h + mountd_LDADD = ../../support/export/libexport.a \ + ../../support/nfs/libnfs.la \ + ../../support/misc/libmisc.a \ +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c +index 0b891121..2b342377 100644 +--- a/utils/mountd/mountd.c ++++ b/utils/mountd/mountd.c +@@ -30,6 +30,7 @@ + #include "rpcmisc.h" + #include "pseudoflavors.h" + #include "nfslib.h" ++#include "export.h" + + extern void my_svc_run(void); + +@@ -73,8 +74,12 @@ static struct option longopts[] = + { "reverse-lookup", 0, 0, 'r' }, + { "manage-gids", 0, 0, 'g' }, + { "no-udp", 0, 0, 'u' }, ++ { "log-auth", 0, 0, 'l'}, ++ { "cache-use-ipaddr", 0, 0, 'i'}, ++ { "ttl", 1, 0, 'T'}, + { NULL, 0, 0, 0 } + }; ++static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:"; + + #define NFSVERSBIT(vers) (0x1 << (vers - 1)) + #define NFSVERSBIT_ALL (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4)) +@@ -669,6 +674,7 @@ main(int argc, char **argv) + int port = 0; + int descriptors = 0; + int c; ++ int ttl; + int vers; + struct sigaction sa; + struct rlimit rlim; +@@ -687,6 +693,8 @@ main(int argc, char **argv) + num_threads = conf_get_num("mountd", "threads", num_threads); + reverse_resolve = conf_get_bool("mountd", "reverse-lookup", reverse_resolve); + ha_callout_prog = conf_get_str("mountd", "ha-callout"); ++ if (conf_get_bool("mountd", "cache-use-ipaddr", 0)) ++ use_ipaddr = 2; + + s = conf_get_str("mountd", "state-directory-path"); + if (s && !state_setup_basedir(argv[0], s)) +@@ -710,10 +718,13 @@ main(int argc, char **argv) + NFSCTL_VERUNSET(nfs_version, vers); + } + ++ ttl = conf_get_num("mountd", "ttl", default_ttl); ++ if (ttl > 0) ++ default_ttl = ttl; + + /* Parse the command line options and arguments. */ + opterr = 0; +- while ((c = getopt_long(argc, argv, "o:nFd:p:P:hH:N:V:vurs:t:g", longopts, NULL)) != EOF) ++ while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) + switch (c) { + case 'g': + manage_gids = 1; +@@ -784,6 +795,21 @@ main(int argc, char **argv) + case 'u': + NFSCTL_UDPUNSET(_rpcprotobits); + break; ++ case 'l': ++ xlog_sconfig("auth", 1); ++ break; ++ case 'i': ++ use_ipaddr = 2; ++ break; ++ case 'T': ++ ttl = atoi(optarg); ++ if (ttl <= 0) { ++ fprintf(stderr, "%s: bad ttl number of seconds: %s\n", ++ argv[0], optarg); ++ usage(argv[0], 1); ++ } ++ default_ttl = ttl; ++ break; + case 0: + break; + case '?': +@@ -888,6 +914,8 @@ main(int argc, char **argv) + if (num_threads > 1) + fork_workers(); + ++ v4clients_init(); ++ + xlog(L_NOTICE, "Version " VERSION " starting"); + my_svc_run(); + +@@ -903,6 +931,7 @@ usage(const char *prog, int n) + { + fprintf(stderr, + "Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n" ++" [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n" + " [-o num|--descriptors num]\n" + " [-p|--port port] [-V version|--nfs-version version]\n" + " [-N version|--no-nfs-version version] [-n|--no-tcp]\n" +diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h +index f058f01d..d3077531 100644 +--- a/utils/mountd/mountd.h ++++ b/utils/mountd/mountd.h +@@ -60,9 +60,4 @@ bool ipaddr_client_matches(nfs_export *exp, struct addrinfo *ai); + bool namelist_client_matches(nfs_export *exp, char *dom); + bool client_matches(nfs_export *exp, char *dom, struct addrinfo *ai); + +-static inline bool is_ipaddr_client(char *dom) +-{ +- return dom[0] == '$'; +-} +- + #endif /* MOUNTD_H */ +diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man +index 8a7943f8..2a91e193 100644 +--- a/utils/mountd/mountd.man ++++ b/utils/mountd/mountd.man +@@ -13,24 +13,24 @@ The + .B rpc.mountd + daemon implements the server side of the NFS MOUNT protocol, + an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813]. ++It also responds to requests from the Linux kernel to authenticate ++clients and provides details of access permissions. + .PP +-An NFS server maintains a table of local physical file systems +-that are accessible to NFS clients. +-Each file system in this table is referred to as an +-.IR "exported file system" , +-or +-.IR export , +-for short. +-.PP +-Each file system in the export table has an access control list. +-.B rpc.mountd +-uses these access control lists to determine +-whether an NFS client is permitted to access a given file system. +-For details on how to manage your NFS server's export table, see the +-.BR exports (5) +-and +-.BR exportfs (8) +-man pages. ++The NFS server ++.RI ( nfsd ) ++maintains a cache of authentication and authorization information which ++is used to identify the source of each request, and then what access ++permissions that source has to any local filesystem. When required ++information is not found in the cache, the server sends a request to ++.B mountd ++to fill in the missing information. Mountd uses a table of information ++stored in ++.B /var/lib/nfs/etab ++and maintained by ++.BR exportfs (8), ++possibly based on the contents of ++.BR exports (5), ++to respond to each request. + .SS Mounting exported NFS File Systems + The NFS MOUNT protocol has several procedures. + The most important of these are +@@ -78,11 +78,69 @@ A client may continue accessing an export even after invoking UMNT. + If the client reboots without sending a UMNT request, stale entries + remain for that client in + .IR /var/lib/nfs/rmtab . ++.SS Mounting File Systems with NFSv4 ++Version 4 (and later) of NFS does not use a separate NFS MOUNT ++protocol. Instead mounting is performed using regular NFS requests ++handled by the NFS server in the Linux kernel ++.RI ( nfsd ). ++Consequently ++.I /var/lib/nfs/rmtab ++is not updated to reflect any NFSv4 activity. + .SH OPTIONS + .TP + .B \-d kind " or " \-\-debug kind + Turn on debugging. Valid kinds are: all, auth, call, general and parse. + .TP ++.BR \-l " or " \-\-log\-auth ++Enable logging of responses to authentication and access requests from ++nfsd. Each response is then cached by the kernel for 30 minutes (or as set by ++.B \-\-ttl ++below), and will be refreshed after 15 minutes (half the ttl time) if ++the relevant client remains active. ++Note that ++.B -l ++is equivalent to ++.B "-d auth" ++and so can be enabled in ++.B /etc/nfs.conf ++with ++.B "\[dq]debug = auth\[dq]" ++in the ++.B "[mountd]" ++section. ++.IP ++.B rpc.mountd ++will always log authentication responses to MOUNT requests when NFSv3 is ++used, but to get similar logs for NFSv4, this option is required. ++.TP ++.BR \-i " or " \-\-cache\-use\-ipaddr ++Normally each client IP address is matched against each host identifier ++(name, wildcard, netgroup etc) found in ++.B /etc/exports ++and a combined identity is formed from all matching identifiers. ++Often many clients will map to the same combined identity so performing ++this mapping reduces the number of distinct access details that the ++kernel needs to store. ++Specifying the ++.B \-i ++option suppresses this mapping so that access to each filesystem is ++requested and cached separately for each client IP address. Doing this ++can increase the burden of updating the cache slightly, but can make the ++log messages produced by the ++.B -l ++option easier to read. ++.TP ++.B \-T " or " \-\-ttl ++Provide a time-to-live (TTL) for cached information given to the kernel. ++The kernel will normally request an update if the information is needed ++after half of this time has expired. Increasing the provided number, ++which is in seconds, reduces the rate of cache update requests, and this ++is particularly noticeable when these requests are logged with ++.BR \-l . ++However increasing also means that changes to hostname to address ++mappings can take longer to be noticed. ++The default TTL is 1800 (30 minutes). ++.TP + .B \-F " or " \-\-foreground + Run in foreground (do not daemonize) + .TP +@@ -213,9 +271,11 @@ Values recognized in the + .B [mountd] + section include + .BR manage-gids , ++.BR cache\-use\-ipaddr , + .BR descriptors , + .BR port , + .BR threads , ++.BR ttl , + .BR reverse-lookup ", and" + .BR state-directory-path , + .B ha-callout +@@ -265,5 +325,9 @@ table of clients accessing server's exports + RFC 1094 - "NFS: Network File System Protocol Specification" + .br + RFC 1813 - "NFS Version 3 Protocol Specification" ++.br ++RFC 7530 - "Network File System (NFS) Version 4 Protocol" ++.br ++RFC 8881 - "Network File System (NFS) Version 4 Minor Version 1 Protocol" + .SH AUTHOR + Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others. +diff --git a/utils/mountd/svc_run.c b/utils/mountd/svc_run.c +index 41b96d7f..167b9757 100644 +--- a/utils/mountd/svc_run.c ++++ b/utils/mountd/svc_run.c +@@ -56,10 +56,9 @@ + #ifdef HAVE_LIBTIRPC + #include + #endif ++#include "export.h" + + void my_svc_run(void); +-void cache_set_fds(fd_set *fdset); +-int cache_process_req(fd_set *readfds); + + #if defined(__GLIBC__) && LONG_MAX != INT_MAX + /* bug in glibc 2.3.6 and earlier, we need +@@ -101,6 +100,7 @@ my_svc_run(void) + + readfds = svc_fdset; + cache_set_fds(&readfds); ++ v4clients_set_fds(&readfds); + + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, (struct timeval *) 0); +@@ -116,6 +116,7 @@ my_svc_run(void) + + default: + selret -= cache_process_req(&readfds); ++ selret -= v4clients_process(&readfds); + if (selret) + svc_getreqset(&readfds); + } diff --git a/nfs-utils-2.3.3-nconnect-manpage.patch b/nfs-utils-2.3.3-nconnect-manpage.patch new file mode 100644 index 0000000..33a8d56 --- /dev/null +++ b/nfs-utils-2.3.3-nconnect-manpage.patch @@ -0,0 +1,40 @@ +commit 3ff6fad27d2cd0772a40ddb65694ce04f3da83bc +Author: Trond Myklebust +Date: Wed Jan 29 10:42:03 2020 -0500 + + manpage: Add a description of the 'nconnect' mount option + + Add a description of the 'nconnect' mount option on the 'nfs' generic + manpage. + + Signed-off-by: Trond Myklebust + Signed-off-by: Steve Dickson + +diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man +index 6ba9cef..84462cd 100644 +--- a/utils/mount/nfs.man ++++ b/utils/mount/nfs.man +@@ -369,6 +369,23 @@ using an automounter (refer to + .BR automount (8) + for details). + .TP 1.5i ++.BR nconnect= n ++When using a connection oriented protocol such as TCP, it may ++sometimes be advantageous to set up multiple connections between ++the client and server. For instance, if your clients and/or servers ++are equipped with multiple network interface cards (NICs), using multiple ++connections to spread the load may improve overall performance. ++In such cases, the ++.BR nconnect ++option allows the user to specify the number of connections ++that should be established between the client and server up to ++a limit of 16. ++.IP ++Note that the ++.BR nconnect ++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 diff --git a/nfs-utils-2.3.3-nfsclnts-cmd.patch b/nfs-utils-2.3.3-nfsclnts-cmd.patch new file mode 100644 index 0000000..9363c2a --- /dev/null +++ b/nfs-utils-2.3.3-nfsclnts-cmd.patch @@ -0,0 +1,481 @@ +diff -up nfs-utils-2.3.3/configure.ac.orig nfs-utils-2.3.3/configure.ac +--- nfs-utils-2.3.3/configure.ac.orig 2020-06-09 10:58:50.178258035 -0400 ++++ nfs-utils-2.3.3/configure.ac 2020-06-09 11:02:04.203102954 -0400 +@@ -639,6 +639,7 @@ AC_CONFIG_FILES([ + tools/rpcgen/Makefile + tools/mountstats/Makefile + tools/nfs-iostat/Makefile ++ tools/nfsdclnts/Makefile + tools/nfsconf/Makefile + tools/nfsdclddb/Makefile + utils/Makefile +diff -up nfs-utils-2.3.3/tools/Makefile.am.orig nfs-utils-2.3.3/tools/Makefile.am +--- nfs-utils-2.3.3/tools/Makefile.am.orig 2020-06-09 10:58:50.178258035 -0400 ++++ nfs-utils-2.3.3/tools/Makefile.am 2020-06-09 11:02:04.203102954 -0400 +@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD + OPTDIRS += nfsdclddb + endif + +-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff -up nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am.orig nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am +--- nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am.orig 2020-06-09 11:02:04.203102954 -0400 ++++ nfs-utils-2.3.3/tools/nfsdclnts/Makefile.am 2020-06-09 11:02:04.203102954 -0400 +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = nfsdclnts.py ++ ++man8_MANS = nfsdclnts.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) -m 755 nfsdclnts.py $(DESTDIR)$(sbindir)/nfsdclnts ++ ++MAINTAINERCLEANFILES=Makefile.in +diff -up nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man.orig nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man +--- nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man.orig 2020-06-09 11:02:04.203102954 -0400 ++++ nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.man 2020-06-09 11:02:04.203102954 -0400 +@@ -0,0 +1,180 @@ ++.\" ++.\" nfsdclnts(8) ++.\" ++.TH "NFSDCLTS" "8" "2020-05-09" "nfsdclnts" "nfsdclnts" ++.ie \n(.g .ds Aq \(aq ++.el .ds Aq ' ++.ss \n[.ss] 0 ++.nh ++.ad l ++.de URL ++\fI\\$2\fP <\\$1>\\$3 ++.. ++.als MTO URL ++.if \n[.g] \{\ ++. mso www.tmac ++. am URL ++. ad l ++. . ++. am MTO ++. ad l ++. . ++. LINKSTYLE blue R < > ++.\} ++.SH "NAME" ++nfsdclnts \- print various nfs client information for knfsd server. ++.SH "SYNOPSIS" ++.sp ++\fBnfsdclnts\fP [\fI\-h\fP] [\fI\-t type\fP] [\fI\-\-clientinfo\fP] [\fI\-\-hostname\fP] [\fI\-q\fP] ++.SH "DESCRIPTION" ++.sp ++The nfsdclnts(8) command parses the content present in /proc/fs/nfsd/clients/ directories. nfsdclnts(8) displays files which are open, locked, delegated by the nfs\-client. It also prints useful client information such as hostname, clientID, NFS version mounted by the nfs\-client. ++.SH "OPTIONS" ++.sp ++\fB\-t, \-\-type\fP=TYPE ++.RS 4 ++Specify the type of file to be displayed. Takes only one TYPE at a time. ++.sp ++\fIopen\fP, \fIlock\fP, \fIdeleg\fP, \fIlayout\fP, or \fIall\fP ++.sp ++open: displays the open files by nfs\-client(s). ++.sp ++lock: displays the files locked by nfs\-client(s). ++.sp ++layout: displays the files for which layout is given. ++.sp ++deleg: displays delegated files information and delegation type. ++.sp ++all: prints all the above type. ++.RE ++.sp ++\fB\-\-clientinfo\fP ++.RS 4 ++displays various nfs\-client info fields such as version of nfs mounted at nfs\-client and clientID. ++.RE ++.sp ++\fB\-\-hostname\fP ++.RS 4 ++Print hostname of nfs\-client instead of ip-address. ++.RE ++.sp ++\fB\-q, \-\-quiet\fP ++.RS 4 ++Hide the header information. ++.RE ++.sp ++\fB\-v, \-\-verbose\fP ++.RS 4 ++Verbose operation, show debug messages. ++.RE ++.sp ++\fB\-f, \-\-file\fP ++.RS 4 ++Instead of processing all client directories under /proc/fs/nfsd/clients, one can provide a specific ++states file to process. One should make sure that info file resides in the same directory as states file. ++If the info file is not valid or present the fields would be marked as "N/A". ++.RE ++.sp ++\fB\-h, \-\-help\fP ++.RS 4 ++Print help explaining the command line options. ++.SH "EXAMPLES" ++.sp ++\fBnfsdclnts \-\-type open\fP ++.RS 4 ++List all files with open type only. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | ip address | Filename ++33823232 | open | r\- | \-\- | [::1]:757 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-type deleg\fP ++.RS 4 ++List all files with deleg type only. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | ip address | Filename ++33823232 | deleg | r | [::1]:757 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-hostname\fP ++.RS 4 ++Print hostname instead of ip\-address. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | Hostname | Filename ++33823232 | open | r\- | \-\- | nfs\-server | testfile ++33823232 | deleg | r | | nfs\-server | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-clientinfo\fP ++.RS 4 ++Print client information. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | ip address | Client ID | vers | Filename ++33823232 | open | r\- | \-\- | [::1]:757 | 0xc79a009f5eb65e84 | 4.2 | testfile ++33823232 | deleg | r | | [::1]:757 | 0xc79a009f5eb65e84 | 4.2 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-file /proc/fs/nfsd/clients/3/states -t open\fP ++.RS 4 ++Process specific states file. ++.RE ++.sp ++.if n .RS 4 ++.nf ++Inode number | Type | Access | Deny | ip address | Client ID | vers | Filename ++33823232 | open | r\- | \-\- | [::1]:757 | 0xc79a009f5eb65e84 | 4.2 | testfile ++.fi ++.if n .RE ++.sp ++\fBnfsdclnts \-\-quiet \-\-hostname\fP ++.RS 4 ++Hide the header information. ++.RE ++.sp ++.if n .RS 4 ++.nf ++33823232 | open | r\- | \-\- | nfs\-server | testfile ++33823232 | deleg | r | | nfs\-server | testfile ++.fi ++.if n .RE ++.SH "FILES" ++.sp ++\fB/proc/fs/nfsd/clients/\fP ++.sp ++Displays basic information about each NFSv4 client. ++.sp ++\fB/proc/fs/nfsd/clients/#/info\fP ++.sp ++Displays information about all the opens held by the given client, including open modes, device numbers, inode numbers, and open owners. ++.sp ++\fB/proc/fs/nfsd/clients/#/states\fP ++.SH "NOTES" ++.sp ++/proc/fs/nfsd/clients/ support was initially introduced in 5.3 kernel and is only implemented for mount points using NFSv4. ++.SH "BUGS" ++Please report any BUGs to \c ++.MTO "linux\-nfs\(atvger.kernel.org" "" "" ++.SH SEE ALSO ++.BR nfsd (8), ++.BR exportfs (8), ++.BR idmapd (8), ++.BR statd (8) ++.SH "AUTHORS" ++Achilles Gaikwad and ++Kenneth D'souza +diff -up nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py.orig nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py +--- nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py.orig 2020-06-09 11:02:04.203102954 -0400 ++++ nfs-utils-2.3.3/tools/nfsdclnts/nfsdclnts.py 2020-06-09 11:02:04.203102954 -0400 +@@ -0,0 +1,254 @@ ++#!/usr/bin/python3 ++# -*- python-mode -*- ++''' ++ Copyright (C) 2020 ++ Authors: Achilles Gaikwad ++ Kenneth D'souza ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++''' ++ ++import multiprocessing as mp ++import os ++import signal ++import sys ++ ++try: ++ import argparse ++except ImportError: ++ print('%s: Failed to import argparse - make sure argparse is installed!' ++ % sys.argv[0]) ++ sys.exit(1) ++try: ++ import yaml ++except ImportError: ++ print('%s: Failed to import yaml - make sure python3-pyyaml is installed!' ++ % sys.argv[0]) ++ sys.exit(1) ++ ++BBOLD = '\033[1;30;47m' #Bold black text with white background. ++ENDC = '\033[m' #Rest to defaults ++ ++def init_worker(): ++ signal.signal(signal.SIGINT, signal.SIG_IGN) ++ ++# this function converts the info file to a dictionary format, sorta. ++def file_to_dict(path): ++ client_info = {} ++ try: ++ with open(path) as f: ++ for line in f: ++ try: ++ (key, val) = line.split(':', 1) ++ client_info[key] = val.strip() ++ # FIXME: There has to be a better way of converting the info file to a dictionary. ++ except ValueError as reason: ++ if verbose: ++ print('Exception occured, %s' % reason) ++ ++ if len(client_info) == 0 and verbose: ++ print("Provided %s file is not valid" %path) ++ return client_info ++ ++ except OSError as reason: ++ if verbose: ++ print('%s' % reason) ++ ++# this function gets the paths from /proc/fs/nfsd/clients/ ++# returns a list of paths for each client which has nfs-share mounted. ++def getpaths(): ++ path = [] ++ try: ++ dirs = os.listdir('/proc/fs/nfsd/clients/') ++ except OSError as reason: ++ exit('%s' % reason) ++ if len(dirs) !=0: ++ for i in dirs: ++ path.append('/proc/fs/nfsd/clients/' + i + '/states') ++ return (path) ++ else: ++ exit('Nothing to process') ++ ++# A single function to rule them all, in this function we gather all the data ++# from already populated data_list and client_info. ++def printer(data_list, argument): ++ client_info_path = data_list.pop() ++ client_info = file_to_dict(client_info_path) ++ for i in data_list: ++ for key in i: ++ inode = i[key]['superblock'].split(':')[-1] ++ # The ip address is quoted, so we dequote it. ++ try: ++ client_ip = client_info['address'][1:-1] ++ except: ++ client_ip = "N/A" ++ try: ++ # if the nfs-server reboots while the nfs-client holds the files open, ++ # the nfs-server would print the filename as '/'. For such instaces we ++ # print the output as disconnected dentry instead of '/'. ++ if(i[key]['filename']=='/'): ++ fname = 'disconnected dentry' ++ else: ++ fname = i[key]['filename'].split('/')[-1] ++ except KeyError: ++ # for older kernels which do not have the fname patch in kernel, they ++ # won't be able to see the fname field. Therefore post it as N/A. ++ fname = "N/A" ++ otype = i[key]['type'] ++ try: ++ access = i[key]['access'] ++ except: ++ access = '' ++ try: ++ deny = i[key]['deny'] ++ except: ++ deny = '' ++ try: ++ hostname = client_info['name'].split()[-1].split('"')[0] ++ hostname = hostname.split('.')[0] ++ # if the hostname is too long, it messes up with the output being in columns, ++ # therefore we truncate the hostname followed by two '..' as suffix. ++ if len(hostname) > 20: ++ hostname = hostname[0:20] + '..' ++ except: ++ hostname = "N/A" ++ try: ++ clientid = client_info['clientid'] ++ except: ++ clientid = "N/A" ++ try: ++ minorversion = "4." + client_info['minor version'] ++ except: ++ minorversion = "N/A" ++ ++ otype = i[key]['type'] ++ # since some fields do not have deny column, we drop those if -t is either ++ # layout or lock. ++ drop = ['layout', 'lock'] ++ ++ # Printing the output this way instead of a single string which is concatenated ++ # this makes it better to quickly add more columns in future. ++ if(otype == argument.type or argument.type == 'all'): ++ print('%-13s' %inode, end='| ') ++ print('%-7s' %otype, end='| ') ++ if (argument.type not in drop): ++ print('%-7s' %access, end='| ') ++ if (argument.type not in drop and argument.type !='deleg'): ++ print('%-5s' %deny, end='| ') ++ if (argument.hostname == True): ++ print('%-22s' %hostname, end='| ') ++ else: ++ print('%-22s' %client_ip, end='| ') ++ if (argument.clientinfo == True) : ++ print('%-20s' %clientid, end='| ') ++ print('%-5s' %minorversion, end='| ') ++ print(fname) ++ ++def opener(path): ++ try: ++ with open(path, 'r') as nfsdata: ++ try: ++ data = yaml.load(nfsdata, Loader = yaml.BaseLoader) ++ if data is not None: ++ clientinfo = path.rsplit('/', 1)[0] + '/info' ++ data.append(clientinfo) ++ return data ++ except: ++ if verbose: ++ print("Exception occurred, Please make sure %s is a YAML file" %path) ++ ++ except OSError as reason: ++ if verbose: ++ print('%s' % reason) ++ ++def print_cols(argument): ++ title_inode = 'Inode number' ++ title_otype = 'Type' ++ title_access = 'Access' ++ title_deny = 'Deny' ++ title_fname = 'Filename' ++ title_clientID = 'Client ID' ++ title_hostname = 'Hostname' ++ title_ip = 'ip address' ++ title_nfsvers = 'vers' ++ ++ drop = ['lock', 'layout'] ++ print(BBOLD, end='') ++ print('%-13s' %title_inode, end='| ') ++ print('%-7s' %title_otype, end='| ') ++ if (argument.type not in drop): ++ print('%-7s' %title_access, end='| ') ++ if (argument.type not in drop and argument.type !='deleg'): ++ print('%-5s' %title_deny, end='| ') ++ if (argument.hostname == True): ++ print('%-22s' %title_hostname, end='| ') ++ else: ++ print('%-22s' %title_ip, end='| ') ++ if (argument.clientinfo == True): ++ print('%-20s' %title_clientID, end='| ') ++ print('%-5s' %title_nfsvers, end='| ') ++ print(title_fname, end='') ++ print(ENDC) ++ ++def nfsd4_show(): ++ ++ parser = argparse.ArgumentParser(description = 'Parse the nfsd states and clientinfo files.') ++ parser.add_argument('-t', '--type', metavar = 'type', type = str, choices = ['open', ++ 'deleg', 'lock', 'layout', 'all'], ++ default = 'all', ++ help = 'Input the type that you want to be printed: open, lock, deleg, layout, all') ++ parser.add_argument('--clientinfo', action = 'store_true', ++ help = 'output clients information, --hostname is implied.') ++ parser.add_argument('--hostname', action = 'store_true', ++ help = 'print hostname of client instead of its ip address. Longer hostnames are truncated.') ++ parser.add_argument('-v', '--verbose', action = 'store_true', ++ help = 'Verbose operation, show debug messages.') ++ parser.add_argument('-f', '--file', nargs='+', type = str, metavar='', ++ help = 'pass client states file, provided that info file resides in the same directory.') ++ parser.add_argument('-q', '--quiet', action = 'store_true', ++ help = 'don\'t print the header information') ++ ++ args = parser.parse_args() ++ ++ global verbose ++ verbose = False ++ if args.verbose: ++ verbose = True ++ ++ if args.file: ++ paths = args.file ++ else: ++ paths = getpaths() ++ ++ p = mp.Pool(mp.cpu_count(), init_worker) ++ try: ++ result = p.map(opener, paths) ++ ### Drop None entries from list ++ final_result = list(filter(None, result)) ++ p.close() ++ p.join() ++ ++ if len(final_result) !=0 and not args.quiet: ++ print_cols(args) ++ ++ for item in final_result: ++ printer(item, args) ++ ++ except KeyboardInterrupt: ++ print('Caught KeyboardInterrupt, terminating workers') ++ p.terminate() ++ p.join() ++ ++if __name__ == "__main__": ++ nfsd4_show() diff --git a/nfs-utils-2.3.3-nfsconf-inplace.patch b/nfs-utils-2.3.3-nfsconf-inplace.patch new file mode 100644 index 0000000..155925d --- /dev/null +++ b/nfs-utils-2.3.3-nfsconf-inplace.patch @@ -0,0 +1,276 @@ +diff -up nfs-utils-2.3.3/support/nfs/conffile.c.orig nfs-utils-2.3.3/support/nfs/conffile.c +--- nfs-utils-2.3.3/support/nfs/conffile.c.orig 2018-09-06 14:09:08.000000000 -0400 ++++ nfs-utils-2.3.3/support/nfs/conffile.c 2019-04-25 10:58:27.199907596 -0400 +@@ -50,6 +50,7 @@ + #include + #include + #include ++#include + + #include "conffile.h" + #include "xlog.h" +@@ -509,6 +510,17 @@ conf_readfile(const char *path) + return NULL; + } + ++ /* Grab a shared lock to ensure its not mid-rewrite */ ++ if (flock(fd, LOCK_SH)) { ++ xlog_warn("conf_readfile: attempt to grab read lock failed: %s", ++ strerror(errno)); ++ goto fail; ++ } ++ ++ /* only after we have the lock, check the file size ready to read it */ ++ sz = lseek(fd, 0, SEEK_END); ++ lseek(fd, 0, SEEK_SET); ++ + new_conf_addr = malloc(sz+1); + if (!new_conf_addr) { + xlog_warn("conf_readfile: malloc (%lu) failed", (unsigned long)sz); +@@ -1588,6 +1600,17 @@ flush_outqueue(struct tailhead *queue, F + return 0; + } + ++/* append one queue to another */ ++static void ++append_queue(struct tailhead *inq, struct tailhead *outq) ++{ ++ while (inq->tqh_first != NULL) { ++ struct outbuffer *ob = inq->tqh_first; ++ TAILQ_REMOVE(inq, ob, link); ++ TAILQ_INSERT_TAIL(outq, ob, link); ++ } ++} ++ + /* read one line of text from a file, growing the buffer as necessary */ + static int + read_line(char **buff, int *buffsize, FILE *in) +@@ -1728,6 +1751,16 @@ is_folded(const char *line) + return false; + } + ++static int ++lock_file(FILE *f) ++{ ++ int ret; ++ ret = flock(fileno(f), LOCK_EX); ++ if (ret) ++ xlog(L_ERROR, "Error could not lock the file"); ++ return ret; ++} ++ + /*** + * Write a value to an nfs.conf style filename + * +@@ -1738,15 +1771,14 @@ int + conf_write(const char *filename, const char *section, const char *arg, + const char *tag, const char *value) + { +- int fdout = -1; +- char *outpath = NULL; +- FILE *outfile = NULL; + FILE *infile = NULL; + int ret = 1; + struct tailhead outqueue; ++ struct tailhead inqueue; + char * buff = NULL; + int buffsize = 0; + ++ TAILQ_INIT(&inqueue); + TAILQ_INIT(&outqueue); + + if (!filename) { +@@ -1759,26 +1791,7 @@ conf_write(const char *filename, const c + return ret; + } + +- if (asprintf(&outpath, "%s.XXXXXX", filename) == -1) { +- xlog(L_ERROR, "conf_write: error composing temp filename"); +- return ret; +- } +- +- fdout = mkstemp(outpath); +- if (fdout < 0) { +- xlog(L_ERROR, "conf_write: open temp file %s failed: %s", +- outpath, strerror(errno)); +- goto cleanup; +- } +- +- outfile = fdopen(fdout, "w"); +- if (!outfile) { +- xlog(L_ERROR, "conf_write: fdopen temp file failed: %s", +- strerror(errno)); +- goto cleanup; +- } +- +- infile = fopen(filename, "r"); ++ infile = fopen(filename, "r+"); + if (!infile) { + if (!value) { + xlog_warn("conf_write: config file \"%s\" not found, nothing to do", filename); +@@ -1787,18 +1800,29 @@ conf_write(const char *filename, const c + } + + xlog_warn("conf_write: config file \"%s\" not found, creating.", filename); +- if (append_line(&outqueue, NULL, make_section(section, arg))) ++ infile = fopen(filename, "wx"); ++ if (!infile) { ++ xlog(L_ERROR, "conf_write: Error creating config file \"%s\".", filename); ++ goto cleanup; ++ } ++ ++ if (lock_file(infile)) + goto cleanup; + +- if (append_line(&outqueue, NULL, make_tagline(tag, value))) ++ if (append_line(&inqueue, NULL, make_section(section, arg))) + goto cleanup; + +- if (flush_outqueue(&outqueue, outfile)) ++ if (append_line(&inqueue, NULL, make_tagline(tag, value))) + goto cleanup; ++ ++ append_queue(&inqueue, &outqueue); + } else { + bool found = false; + int err = 0; + ++ if (lock_file(infile)) ++ goto cleanup; ++ + buffsize = 4096; + buff = calloc(1, buffsize); + if (buff == NULL) { +@@ -1813,7 +1837,7 @@ conf_write(const char *filename, const c + /* read in one section worth of lines */ + do { + if (*buff != '\0') { +- if (append_line(&outqueue, NULL, strdup(buff))) ++ if (append_line(&inqueue, NULL, strdup(buff))) + goto cleanup; + } + +@@ -1821,7 +1845,7 @@ conf_write(const char *filename, const c + } while (err == 0 && buff[0] != '['); + + /* find the section header */ +- where = TAILQ_FIRST(&outqueue); ++ where = TAILQ_FIRST(&inqueue); + while (where != NULL) { + if (where->text != NULL && where->text[0] == '[') + break; +@@ -1845,7 +1869,7 @@ conf_write(const char *filename, const c + /* remove current tag */ + do { + struct outbuffer *next = TAILQ_NEXT(where, link); +- TAILQ_REMOVE(&outqueue, where, link); ++ TAILQ_REMOVE(&inqueue, where, link); + if (is_folded(where->text)) + again = true; + else +@@ -1857,14 +1881,14 @@ conf_write(const char *filename, const c + + /* insert new tag */ + if (value) { +- if (append_line(&outqueue, prev, make_tagline(tag, value))) ++ if (append_line(&inqueue, prev, make_tagline(tag, value))) + goto cleanup; + } + } else + /* no existing assignment found and we need to add one */ + if (value) { + /* rewind past blank lines and comments */ +- struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead); ++ struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead); + + /* comments immediately before a section usually relate + * to the section below them */ +@@ -1876,7 +1900,7 @@ conf_write(const char *filename, const c + tail = TAILQ_PREV(tail, tailhead, link); + + /* now add the tag here */ +- if (append_line(&outqueue, tail, make_tagline(tag, value))) ++ if (append_line(&inqueue, tail, make_tagline(tag, value))) + goto cleanup; + + found = true; +@@ -1886,49 +1910,45 @@ conf_write(const char *filename, const c + /* EOF and correct section not found, so add one */ + if (err && !found && value) { + /* did the last section end in a blank line */ +- struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead); ++ struct outbuffer *tail = TAILQ_LAST(&inqueue, tailhead); + if (tail && !is_empty(tail->text)) { + /* no, so add one for clarity */ +- if (append_line(&outqueue, NULL, strdup("\n"))) ++ if (append_line(&inqueue, NULL, strdup("\n"))) + goto cleanup; + } + + /* add the new section header */ +- if (append_line(&outqueue, NULL, make_section(section, arg))) ++ if (append_line(&inqueue, NULL, make_section(section, arg))) + goto cleanup; + + /* now add the tag */ +- if (append_line(&outqueue, NULL, make_tagline(tag, value))) ++ if (append_line(&inqueue, NULL, make_tagline(tag, value))) + goto cleanup; + } + +- /* we are done with this section, write it out */ +- if (flush_outqueue(&outqueue, outfile)) +- goto cleanup; ++ /* we are done with this section, move it to the out queue */ ++ append_queue(&inqueue, &outqueue); + } while(err == 0); + } + +- if (infile) { +- fclose(infile); +- infile = NULL; +- } ++ /* now rewind and overwrite the file with the updated data */ ++ rewind(infile); + +- fdout = -1; +- if (fclose(outfile)) { +- xlog(L_ERROR, "Error writing config file: %s", strerror(errno)); ++ if (ftruncate(fileno(infile), 0)) { ++ xlog(L_ERROR, "Error truncating config file"); + goto cleanup; + } + +- /* now swap the old file for the new one */ +- if (rename(outpath, filename)) { +- xlog(L_ERROR, "Error updating config file: %s: %s\n", filename, strerror(errno)); +- ret = 1; +- } else { +- ret = 0; +- free(outpath); +- outpath = NULL; ++ if (flush_outqueue(&outqueue, infile)) ++ goto cleanup; ++ ++ if (infile) { ++ fclose(infile); ++ infile = NULL; + } + ++ ret = 0; ++ + cleanup: + flush_outqueue(&outqueue, NULL); + +@@ -1936,11 +1956,5 @@ cleanup: + free(buff); + if (infile) + fclose(infile); +- if (fdout != -1) +- close(fdout); +- if (outpath) { +- unlink(outpath); +- free(outpath); +- } + return ret; + } diff --git a/nfs-utils-2.3.3-nfsconf-manage-gids.patch b/nfs-utils-2.3.3-nfsconf-manage-gids.patch new file mode 100644 index 0000000..43de59a --- /dev/null +++ b/nfs-utils-2.3.3-nfsconf-manage-gids.patch @@ -0,0 +1,23 @@ +commit 268e3c0cff6d6aee3b8f5458545f8dab76d7d444 +Author: Steve Dickson +Date: Mon Feb 4 15:17:42 2019 -0500 + + nfs.conf: Fixed manage-gids option typo + + Reported-by: Adam DiFrischia + BugLink: https://bugzilla.linux-nfs.org/show_bug.cgi?id=333 + Signed-off-by: Steve Dickson + +diff --git a/nfs.conf b/nfs.conf +index 796bee4..722b024 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -26,7 +26,7 @@ + # + [mountd] + # debug=0 +-# manage_gids=n ++# manage-gids=n + # descriptors=0 + # port=0 + # threads=1 diff --git a/nfs-utils-2.3.3-nfsconf-rdmaport.patch b/nfs-utils-2.3.3-nfsconf-rdmaport.patch new file mode 100644 index 0000000..334d286 --- /dev/null +++ b/nfs-utils-2.3.3-nfsconf-rdmaport.patch @@ -0,0 +1,73 @@ +commit 0240df0e8ccf7be2706a6a10a2a620f8eda55275 +Author: Yongcheng Yang +Date: Thu Sep 5 07:36:26 2019 -0400 + + nfsd: Adjust nfs.conf setting/parsing of rdma port + + The rpc.nfsd program can use option "--rdma" to enable + RDMA on the standard port (nfsrdma/20049) or "--rdma=port" + for an alternate port. + + But now in /etc/nfs.conf, we need to specify the port + number (e.g. rdma=nfsrdma) to enable it, which is not + convenient. + The default setting "rdma=n" may cause more confusion. + + Update to enable RDMA on standard port when setting + boolean YES to "rdma=". And using "rdma-port=" for an + alternate port if necessary. + + Also let previous config (e.g. rdma=nfsrdma) work as well. + + Signed-off-by: Yongcheng Yang + Signed-off-by: Steve Dickson + +diff --git a/nfs.conf b/nfs.conf +index 85097fd..186a5b1 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -63,6 +63,7 @@ + # vers4.1=y + # vers4.2=y + # rdma=n ++# rdma-port=20049 + # + [statd] + # debug=0 +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index b256bd9..a412a02 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -92,7 +92,14 @@ main(int argc, char **argv) + port = conf_get_str("nfsd", "port"); + if (!port) + port = "nfs"; +- rdma_port = conf_get_str("nfsd", "rdma"); ++ if (conf_get_bool("nfsd", "rdma", false)) { ++ rdma_port = conf_get_str("nfsd", "rdma-port"); ++ if (!rdma_port) ++ rdma_port = "nfsrdma"; ++ } ++ /* backward compatibility - nfs.conf used to set rdma port directly */ ++ if (!rdma_port) ++ rdma_port = conf_get_str("nfsd", "rdma"); + if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(protobits))) + NFSCTL_UDPSET(protobits); + else +diff --git a/utils/nfsd/nfsd.man b/utils/nfsd/nfsd.man +index d83ef86..2701ba7 100644 +--- a/utils/nfsd/nfsd.man ++++ b/utils/nfsd/nfsd.man +@@ -144,7 +144,11 @@ The lease time for NFSv4, in seconds. + Set the port for TCP/UDP to bind to. + .TP + .B rdma +-Set RDMA port. Use "rdma=nfsrdma" to enable standard port. ++Enable RDMA port (with "on" or "yes" etc) on the standard port ++("nfsrdma", port 20049). ++.TP ++.B rdma-port ++Set an alternate RDMA port. + .TP + .B UDP + Enable (with "on" or "yes" etc) or disable ("off", "no") UDP support. diff --git a/nfs-utils-2.3.3-nfsconf-usegssproxy.patch b/nfs-utils-2.3.3-nfsconf-usegssproxy.patch new file mode 100644 index 0000000..e4f13a4 --- /dev/null +++ b/nfs-utils-2.3.3-nfsconf-usegssproxy.patch @@ -0,0 +1,82 @@ +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2018-10-22 13:34:58.927700353 -0400 ++++ nfs-utils-2.3.3/nfs.conf 2018-10-22 14:14:36.864110090 -0400 +@@ -2,16 +2,16 @@ + # This is a general configuration for the + # NFS daemons and tools + # +-#[general] ++[general] + # pipefs-directory=/var/lib/nfs/rpc_pipefs + # +-#[exportfs] ++[exportfs] + # debug=0 + # +-#[gssd] ++[gssd] + # use-memcache=0 + # use-machine-creds=1 +-# use-gss-proxy=0 ++use-gss-proxy=1 + # avoid-dns=1 + # limit-to-legacy-enctypes=0 + # context-timeout=0 +@@ -20,11 +20,11 @@ + # cred-cache-directory= + # preferred-realm= + # +-#[lockd] ++[lockd] + # port=0 + # udp-port=0 + # +-#[mountd] ++[mountd] + # debug=0 + # manage_gids=n + # descriptors=0 +@@ -34,18 +34,17 @@ + # state-directory-path=/var/lib/nfs + # ha-callout= + # +-#[nfsdcltrack] ++[nfsdcltrack] + # debug=0 + # storagedir=/var/lib/nfs/nfsdcltrack + # +-#[nfsd] ++[nfsd] + # debug=0 + # threads=8 + # host= + # port=0 + # grace-time=90 + # lease-time=90 +-# udp=n + # tcp=y + # vers2=n + # vers3=y +@@ -55,7 +54,7 @@ + # vers4.2=y + # rdma=n + # +-#[statd] ++[statd] + # debug=0 + # port=0 + # outgoing-port=0 +@@ -63,12 +62,10 @@ + # state-directory-path=/var/lib/nfs/statd + # ha-callout= + # +-#[sm-notify] ++[sm-notify] + # debug=0 + # retry-time=900 + # outgoing-port= + # outgoing-addr= + # lift-grace=y + # +-#[svcgssd] +-# principal= diff --git a/nfs-utils-2.3.3-nfsd-disable-v4.patch b/nfs-utils-2.3.3-nfsd-disable-v4.patch new file mode 100644 index 0000000..16b0330 --- /dev/null +++ b/nfs-utils-2.3.3-nfsd-disable-v4.patch @@ -0,0 +1,104 @@ +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index 2c14e5f..00df2fc 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -888,6 +888,9 @@ main(int argc, char *argv[]) + + read_gss_conf(); + ++ verbosity = conf_get_num("gssd", "Verbosity", verbosity); ++ rpc_verbosity = conf_get_num("gssd", "RPC-Verbosity", rpc_verbosity); ++ + while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { + switch (opt) { + case 'f': +diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c +index 8e918cc..ec49b61 100644 +--- a/utils/gssd/svcgssd.c ++++ b/utils/gssd/svcgssd.c +@@ -113,6 +113,10 @@ main(int argc, char *argv[]) + else + principal = s; + ++ verbosity = conf_get_num("svcgssd", "Verbosity", verbosity); ++ rpc_verbosity = conf_get_num("svcgssd", "RPC-Verbosity", rpc_verbosity); ++ idmap_verbosity = conf_get_num("svcgssd", "IDMAP-Verbosity", idmap_verbosity); ++ + while ((opt = getopt(argc, argv, "fivrnp:")) != -1) { + switch (opt) { + case 'f': +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index 4811e0f..d14eef7 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -261,6 +261,10 @@ main(int argc, char **argv) + strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir)); + CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); + CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); ++ if (conf_get_bool("General", "server-only", false)) ++ clientstart = 0; ++ if (conf_get_bool("General", "client-only", false)) ++ serverstart = 0; + } + } else { + conf_path = NFS_CONFFILE; +@@ -276,6 +280,10 @@ main(int argc, char **argv) + "cache-expiration", DEFAULT_IDMAP_CACHE_EXPIRY); + CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User")); + CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group")); ++ if (conf_get_bool("General", "server-only", false)) ++ clientstart = 0; ++ if (conf_get_bool("General", "client-only", false)) ++ serverstart = 0; + } + + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index 6b57e2b..b256bd9 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -83,6 +83,9 @@ main(int argc, char **argv) + + conf_init_file(NFS_CONFFILE); + xlog_from_conffile("nfsd"); ++ ++ nfssvc_get_minormask(&minormask); ++ + count = conf_get_num("nfsd", "threads", count); + grace = conf_get_num("nfsd", "grace-time", grace); + lease = conf_get_num("nfsd", "lease-time", lease); +@@ -101,13 +104,19 @@ main(int argc, char **argv) + for (i = 2; i <= 4; i++) { + char tag[20]; + sprintf(tag, "vers%d", i); +- if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(versbits, i))) ++ if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(versbits, i))) { + NFSCTL_VERSET(versbits, i); +- else ++ if (i == 4) ++ minorvers = minorversset = minormask; ++ } else { + NFSCTL_VERUNSET(versbits, i); ++ if (i == 4) { ++ minorvers = 0; ++ minorversset = minormask; ++ } ++ } + } + +- nfssvc_get_minormask(&minormask); + /* We assume the kernel will default all minor versions to 'on', + * and allow the config file to disable some. + */ +diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c +index 7a48473..29dad38 100644 +--- a/utils/statd/sm-notify.c ++++ b/utils/statd/sm-notify.c +@@ -503,6 +503,7 @@ main(int argc, char **argv) + s = conf_get_str("statd", "state-directory-path"); + if (s && !nsm_setup_pathnames(argv[0], s)) + exit(1); ++ opt_update_state = conf_get_bool("sm-notify", "update-state", opt_update_state); + + while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) { + switch (c) { diff --git a/nfs-utils-2.3.3-nfsdcld-upstream-update.patch b/nfs-utils-2.3.3-nfsdcld-upstream-update.patch new file mode 100644 index 0000000..e583183 --- /dev/null +++ b/nfs-utils-2.3.3-nfsdcld-upstream-update.patch @@ -0,0 +1,4048 @@ +diff --git a/.gitignore b/.gitignore +index e91e7a25..e97b31f5 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -54,6 +54,7 @@ utils/rquotad/rquotad + utils/rquotad/rquota.h + utils/rquotad/rquota_xdr.c + utils/showmount/showmount ++utils/nfsdcld/nfsdcld + utils/nfsdcltrack/nfsdcltrack + utils/statd/statd + tools/locktest/testlk +diff --git a/aclocal/ax_gcc_func_attribute.m4 b/aclocal/ax_gcc_func_attribute.m4 +new file mode 100644 +index 00000000..098c9aad +--- /dev/null ++++ b/aclocal/ax_gcc_func_attribute.m4 +@@ -0,0 +1,238 @@ ++# =========================================================================== ++# https://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html ++# =========================================================================== ++# ++# SYNOPSIS ++# ++# AX_GCC_FUNC_ATTRIBUTE(ATTRIBUTE) ++# ++# DESCRIPTION ++# ++# This macro checks if the compiler supports one of GCC's function ++# attributes; many other compilers also provide function attributes with ++# the same syntax. Compiler warnings are used to detect supported ++# attributes as unsupported ones are ignored by default so quieting ++# warnings when using this macro will yield false positives. ++# ++# The ATTRIBUTE parameter holds the name of the attribute to be checked. ++# ++# If ATTRIBUTE is supported define HAVE_FUNC_ATTRIBUTE_. ++# ++# The macro caches its result in the ax_cv_have_func_attribute_ ++# variable. ++# ++# The macro currently supports the following function attributes: ++# ++# alias ++# aligned ++# alloc_size ++# always_inline ++# artificial ++# cold ++# const ++# constructor ++# constructor_priority for constructor attribute with priority ++# deprecated ++# destructor ++# dllexport ++# dllimport ++# error ++# externally_visible ++# fallthrough ++# flatten ++# format ++# format_arg ++# gnu_inline ++# hot ++# ifunc ++# leaf ++# malloc ++# noclone ++# noinline ++# nonnull ++# noreturn ++# nothrow ++# optimize ++# pure ++# sentinel ++# sentinel_position ++# unused ++# used ++# visibility ++# warning ++# warn_unused_result ++# weak ++# weakref ++# ++# Unsupported function attributes will be tested with a prototype ++# returning an int and not accepting any arguments and the result of the ++# check might be wrong or meaningless so use with care. ++# ++# LICENSE ++# ++# Copyright (c) 2013 Gabriele Svelto ++# ++# Copying and distribution of this file, with or without modification, are ++# permitted in any medium without royalty provided the copyright notice ++# and this notice are preserved. This file is offered as-is, without any ++# warranty. ++ ++#serial 9 ++ ++AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ ++ AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) ++ ++ AC_CACHE_CHECK([for __attribute__(($1))], [ac_var], [ ++ AC_LINK_IFELSE([AC_LANG_PROGRAM([ ++ m4_case([$1], ++ [alias], [ ++ int foo( void ) { return 0; } ++ int bar( void ) __attribute__(($1("foo"))); ++ ], ++ [aligned], [ ++ int foo( void ) __attribute__(($1(32))); ++ ], ++ [alloc_size], [ ++ void *foo(int a) __attribute__(($1(1))); ++ ], ++ [always_inline], [ ++ inline __attribute__(($1)) int foo( void ) { return 0; } ++ ], ++ [artificial], [ ++ inline __attribute__(($1)) int foo( void ) { return 0; } ++ ], ++ [cold], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [const], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [constructor_priority], [ ++ int foo( void ) __attribute__((__constructor__(65535/2))); ++ ], ++ [constructor], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [deprecated], [ ++ int foo( void ) __attribute__(($1(""))); ++ ], ++ [destructor], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [dllexport], [ ++ __attribute__(($1)) int foo( void ) { return 0; } ++ ], ++ [dllimport], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [error], [ ++ int foo( void ) __attribute__(($1(""))); ++ ], ++ [externally_visible], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [fallthrough], [ ++ int foo( void ) {switch (0) { case 1: __attribute__(($1)); case 2: break ; }}; ++ ], ++ [flatten], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [format], [ ++ int foo(const char *p, ...) __attribute__(($1(printf, 1, 2))); ++ ], ++ [format_arg], [ ++ char *foo(const char *p) __attribute__(($1(1))); ++ ], ++ [gnu_inline], [ ++ inline __attribute__(($1)) int foo( void ) { return 0; } ++ ], ++ [hot], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [ifunc], [ ++ int my_foo( void ) { return 0; } ++ static int (*resolve_foo(void))(void) { return my_foo; } ++ int foo( void ) __attribute__(($1("resolve_foo"))); ++ ], ++ [leaf], [ ++ __attribute__(($1)) int foo( void ) { return 0; } ++ ], ++ [malloc], [ ++ void *foo( void ) __attribute__(($1)); ++ ], ++ [noclone], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [noinline], [ ++ __attribute__(($1)) int foo( void ) { return 0; } ++ ], ++ [nonnull], [ ++ int foo(char *p) __attribute__(($1(1))); ++ ], ++ [noreturn], [ ++ void foo( void ) __attribute__(($1)); ++ ], ++ [nothrow], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [optimize], [ ++ __attribute__(($1(3))) int foo( void ) { return 0; } ++ ], ++ [pure], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [sentinel], [ ++ int foo(void *p, ...) __attribute__(($1)); ++ ], ++ [sentinel_position], [ ++ int foo(void *p, ...) __attribute__(($1(1))); ++ ], ++ [returns_nonnull], [ ++ void *foo( void ) __attribute__(($1)); ++ ], ++ [unused], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [used], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [visibility], [ ++ int foo_def( void ) __attribute__(($1("default"))); ++ int foo_hid( void ) __attribute__(($1("hidden"))); ++ int foo_int( void ) __attribute__(($1("internal"))); ++ int foo_pro( void ) __attribute__(($1("protected"))); ++ ], ++ [warning], [ ++ int foo( void ) __attribute__(($1(""))); ++ ], ++ [warn_unused_result], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [weak], [ ++ int foo( void ) __attribute__(($1)); ++ ], ++ [weakref], [ ++ static int foo( void ) { return 0; } ++ static int bar( void ) __attribute__(($1("foo"))); ++ ], ++ [ ++ m4_warn([syntax], [Unsupported attribute $1, the test may fail]) ++ int foo( void ) __attribute__(($1)); ++ ] ++ )], []) ++ ], ++ dnl GCC doesn't exit with an error if an unknown attribute is ++ dnl provided but only outputs a warning, so accept the attribute ++ dnl only if no warning were issued. ++ [AS_IF([test -s conftest.err], ++ [AS_VAR_SET([ac_var], [no])], ++ [AS_VAR_SET([ac_var], [yes])])], ++ [AS_VAR_SET([ac_var], [no])]) ++ ]) ++ ++ AS_IF([test yes = AS_VAR_GET([ac_var])], ++ [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_FUNC_ATTRIBUTE_$1), 1, ++ [Define to 1 if the system has the `$1' function attribute])], []) ++ ++ AS_VAR_POPDEF([ac_var]) ++]) +diff --git a/configure.ac b/configure.ac +index 48eb9eb6..13ea957f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -238,6 +238,12 @@ else + AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mount" = "yes"]) + fi + ++AC_ARG_ENABLE(nfsdcld, ++ [AC_HELP_STRING([--disable-nfsdcld], ++ [disable NFSv4 clientid tracking daemon @<:@default=no@:>@])], ++ enable_nfsdcld=$enableval, ++ enable_nfsdcld="yes") ++ + AC_ARG_ENABLE(nfsdcltrack, + [AC_HELP_STRING([--disable-nfsdcltrack], + [disable NFSv4 clientid tracking programs @<:@default=no@:>@])], +@@ -317,6 +323,20 @@ if test "$enable_nfsv4" = yes; then + dnl Check for sqlite3 + AC_SQLITE3_VERS + ++ if test "$enable_nfsdcld" = "yes"; then ++ AC_CHECK_HEADERS([libgen.h sys/inotify.h], , ++ AC_MSG_ERROR([Cannot find header needed for nfsdcld])) ++ ++ case $libsqlite3_cv_is_recent in ++ yes) ;; ++ unknown) ++ dnl do not fail when cross-compiling ++ AC_MSG_WARN([assuming sqlite is at least v3.3]) ;; ++ *) ++ AC_MSG_ERROR([nfsdcld requires sqlite-devel]) ;; ++ esac ++ fi ++ + if test "$enable_nfsdcltrack" = "yes"; then + AC_CHECK_HEADERS([libgen.h sys/inotify.h], , + AC_MSG_ERROR([Cannot find header needed for nfsdcltrack])) +@@ -332,6 +352,7 @@ if test "$enable_nfsv4" = yes; then + fi + + else ++ enable_nfsdcld="no" + enable_nfsdcltrack="no" + fi + +@@ -342,6 +363,7 @@ if test "$enable_nfsv41" = yes; then + fi + + dnl enable nfsidmap when its support by libnfsidmap ++AM_CONDITIONAL(CONFIG_NFSDCLD, [test "$enable_nfsdcld" = "yes" ]) + AM_CONDITIONAL(CONFIG_NFSDCLTRACK, [test "$enable_nfsdcltrack" = "yes" ]) + + +@@ -581,6 +603,7 @@ CHECK_CCSUPPORT([-Werror=format-overflow=2], [flg1]) + CHECK_CCSUPPORT([-Werror=int-conversion], [flg2]) + CHECK_CCSUPPORT([-Werror=incompatible-pointer-types], [flg3]) + CHECK_CCSUPPORT([-Werror=misleading-indentation], [flg4]) ++AX_GCC_FUNC_ATTRIBUTE([format]) + + AC_SUBST([AM_CFLAGS], ["$my_am_cflags $flg1 $flg2 $flg3 $flg4"]) + +@@ -617,8 +640,10 @@ AC_CONFIG_FILES([ + tools/mountstats/Makefile + tools/nfs-iostat/Makefile + tools/nfsconf/Makefile ++ tools/clddb-tool/Makefile + utils/Makefile + utils/blkmapd/Makefile ++ utils/nfsdcld/Makefile + utils/nfsdcltrack/Makefile + utils/exportfs/Makefile + utils/gssd/Makefile +diff --git a/nfs.conf b/nfs.conf +index d48a4e55..56172c49 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -36,6 +36,10 @@ use-gss-proxy=1 + # state-directory-path=/var/lib/nfs + # ha-callout= + # ++[nfsdcld] ++# debug=0 ++# storagedir=/var/lib/nfs/nfsdcld ++# + [nfsdcltrack] + # debug=0 + # storagedir=/var/lib/nfs/nfsdcltrack +diff --git a/support/include/cld.h b/support/include/cld.h +index f14a9ab0..88d3b63e 100644 +--- a/support/include/cld.h ++++ b/support/include/cld.h +@@ -23,16 +23,22 @@ + #define _NFSD_CLD_H + + /* latest upcall version available */ +-#define CLD_UPCALL_VERSION 1 ++#define CLD_UPCALL_VERSION 2 + + /* defined by RFC3530 */ + #define NFS4_OPAQUE_LIMIT 1024 + ++#ifndef SHA256_DIGEST_SIZE ++#define SHA256_DIGEST_SIZE 32 ++#endif ++ + enum cld_command { + Cld_Create, /* create a record for this cm_id */ + Cld_Remove, /* remove record of this cm_id */ + Cld_Check, /* is this cm_id allowed? */ + Cld_GraceDone, /* grace period is complete */ ++ Cld_GraceStart, /* grace start (upload client records) */ ++ Cld_GetVersion, /* query max supported upcall version */ + }; + + /* representation of long-form NFSv4 client ID */ +@@ -41,6 +47,17 @@ struct cld_name { + unsigned char cn_id[NFS4_OPAQUE_LIMIT]; /* client-provided */ + } __attribute__((packed)); + ++/* sha256 hash of the kerberos principal */ ++struct cld_princhash { ++ uint8_t cp_len; /* length of cp_data */ ++ unsigned char cp_data[SHA256_DIGEST_SIZE]; /* hash of principal */ ++} __attribute__((packed)); ++ ++struct cld_clntinfo { ++ struct cld_name cc_name; ++ struct cld_princhash cc_princhash; ++} __attribute__((packed)); ++ + /* message struct for communication with userspace */ + struct cld_msg { + uint8_t cm_vers; /* upcall version */ +@@ -50,7 +67,28 @@ struct cld_msg { + union { + int64_t cm_gracetime; /* grace period start time */ + struct cld_name cm_name; ++ uint8_t cm_version; /* for getting max version */ ++ } __attribute__((packed)) cm_u; ++} __attribute__((packed)); ++ ++/* version 2 message can include hash of kerberos principal */ ++struct cld_msg_v2 { ++ uint8_t cm_vers; /* upcall version */ ++ uint8_t cm_cmd; /* upcall command */ ++ int16_t cm_status; /* return code */ ++ uint32_t cm_xid; /* transaction id */ ++ union { ++ struct cld_name cm_name; ++ uint8_t cm_version; /* for getting max version */ ++ struct cld_clntinfo cm_clntinfo; /* name & princ hash */ + } __attribute__((packed)) cm_u; + } __attribute__((packed)); + ++struct cld_msg_hdr { ++ uint8_t cm_vers; /* upcall version */ ++ uint8_t cm_cmd; /* upcall command */ ++ int16_t cm_status; /* return code */ ++ uint32_t cm_xid; /* transaction id */ ++} __attribute__((packed)); ++ + #endif /* !_NFSD_CLD_H */ +diff --git a/support/include/xcommon.h b/support/include/xcommon.h +index 23c9a135..30b0403b 100644 +--- a/support/include/xcommon.h ++++ b/support/include/xcommon.h +@@ -9,6 +9,10 @@ + #ifndef _XMALLOC_H + #define _MALLOC_H + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + #include + #include +@@ -25,9 +29,15 @@ + + #define streq(s, t) (strcmp ((s), (t)) == 0) + +-/* Functions in sundries.c that are used in mount.c and umount.c */ ++#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT ++#define X_FORMAT(_x) __attribute__((__format__ _x)) ++#else ++#define X_FORMAT(_x) ++#endif ++ ++/* Functions in sundries.c that are used in mount.c and umount.c */ + char *canonicalize (const char *path); +-void nfs_error (const char *fmt, ...); ++void nfs_error (const char *fmt, ...) X_FORMAT((printf, 1, 2)); + void *xmalloc (size_t size); + void *xrealloc(void *p, size_t size); + void xfree(void *); +@@ -36,9 +46,9 @@ char *xstrndup (const char *s, int n); + char *xstrconcat2 (const char *, const char *); + char *xstrconcat3 (const char *, const char *, const char *); + char *xstrconcat4 (const char *, const char *, const char *, const char *); +-void die (int errcode, const char *fmt, ...); ++void die (int errcode, const char *fmt, ...) X_FORMAT((printf, 2, 3)); + +-extern void die(int err, const char *fmt, ...); ++extern void die(int err, const char *fmt, ...) X_FORMAT((printf, 2, 3)); + extern void (*at_die)(void); + + /* exit status - bits below are ORed */ +diff --git a/support/include/xlog.h b/support/include/xlog.h +index a11463ed..32ff5a1b 100644 +--- a/support/include/xlog.h ++++ b/support/include/xlog.h +@@ -7,6 +7,10 @@ + #ifndef XLOG_H + #define XLOG_H + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + + /* These are logged always. L_FATAL also does exit(1) */ +@@ -35,6 +39,12 @@ struct xlog_debugfac { + int df_fac; + }; + ++#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT ++#define XLOG_FORMAT(_x) __attribute__((__format__ _x)) ++#else ++#define XLOG_FORMAT(_x) ++#endif ++ + extern int export_errno; + void xlog_open(char *progname); + void xlog_stderr(int on); +@@ -43,10 +53,10 @@ void xlog_config(int fac, int on); + void xlog_sconfig(char *, int on); + void xlog_from_conffile(char *); + int xlog_enabled(int fac); +-void xlog(int fac, const char *fmt, ...); +-void xlog_warn(const char *fmt, ...); +-void xlog_err(const char *fmt, ...); +-void xlog_errno(int err, const char *fmt, ...); +-void xlog_backend(int fac, const char *fmt, va_list args); ++void xlog(int fac, const char *fmt, ...) XLOG_FORMAT((printf, 2, 3)); ++void xlog_warn(const char *fmt, ...) XLOG_FORMAT((printf, 1, 2)); ++void xlog_err(const char *fmt, ...) XLOG_FORMAT((printf, 1, 2)); ++void xlog_errno(int err, const char *fmt, ...) XLOG_FORMAT((printf, 2, 3)); ++void xlog_backend(int fac, const char *fmt, va_list args) XLOG_FORMAT((printf, 2, 0)); + + #endif /* XLOG_H */ +diff --git a/support/junction/junction.c b/support/junction/junction.c +index ab6caa61..41cce261 100644 +--- a/support/junction/junction.c ++++ b/support/junction/junction.c +@@ -23,6 +23,10 @@ + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + */ + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + #include + +diff --git a/support/misc/file.c b/support/misc/file.c +index 4065376e..74973169 100644 +--- a/support/misc/file.c ++++ b/support/misc/file.c +@@ -18,6 +18,10 @@ + * along with nfs-utils. If not, see . + */ + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + + #include +diff --git a/support/misc/mountpoint.c b/support/misc/mountpoint.c +index 9f9ce44e..4205b41c 100644 +--- a/support/misc/mountpoint.c ++++ b/support/misc/mountpoint.c +@@ -3,6 +3,10 @@ + * check if a given path is a mountpoint + */ + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + #include "xcommon.h" + #include +diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c +index 9dc4cf1c..7c4cf373 100644 +--- a/support/nfs/cacheio.c ++++ b/support/nfs/cacheio.c +@@ -15,6 +15,10 @@ + * + */ + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + #include + #include +diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c +index ef7ff05f..7b595f89 100644 +--- a/support/nfs/svc_create.c ++++ b/support/nfs/svc_create.c +@@ -184,7 +184,7 @@ svc_create_sock(const struct sockaddr *sap, socklen_t salen, + type = SOCK_STREAM; + break; + default: +- xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u", ++ xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %lu", + __func__, nconf->nc_semantics); + return -1; + } +diff --git a/support/nsm/rpc.c b/support/nsm/rpc.c +index ae49006c..08b4746f 100644 +--- a/support/nsm/rpc.c ++++ b/support/nsm/rpc.c +@@ -182,7 +182,7 @@ nsm_xmit_getport(const int sock, const struct sockaddr_in *sin, + uint32_t xid; + XDR xdr; + +- xlog(D_CALL, "Sending PMAP_GETPORT for %u, %u, udp", program, version); ++ xlog(D_CALL, "Sending PMAP_GETPORT for %lu, %lu, udp", program, version); + + nsm_init_xdrmem(msgbuf, NSM_MAXMSGSIZE, &xdr); + xid = nsm_init_rpc_header(PMAPPROG, PMAPVERS, +diff --git a/systemd/Makefile.am b/systemd/Makefile.am +index d54518bc..53458c62 100644 +--- a/systemd/Makefile.am ++++ b/systemd/Makefile.am +@@ -36,6 +36,11 @@ unit_files += \ + endif + endif + ++if CONFIG_NFSDCLD ++unit_files += \ ++ nfsdcld.service ++endif ++ + man5_MANS = nfs.conf.man + man7_MANS = nfs.systemd.man + EXTRA_DIST = $(unit_files) $(man5_MANS) $(man7_MANS) +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index 136552b5..24118d69 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -6,10 +6,12 @@ Requires= nfs-mountd.service + Wants=rpcbind.socket network-online.target + Wants=rpc-statd.service nfs-idmapd.service + Wants=rpc-statd-notify.service ++Wants=nfsdcld.service + + After= network-online.target local-fs.target + After= proc-fs-nfsd.mount rpcbind.socket nfs-mountd.service + After= nfs-idmapd.service rpc-statd.service ++After= nfsdcld.service + Before= rpc-statd-notify.service + + # GSS services dependencies and ordering +diff --git a/systemd/nfsdcld.service b/systemd/nfsdcld.service +new file mode 100644 +index 00000000..a32d2430 +--- /dev/null ++++ b/systemd/nfsdcld.service +@@ -0,0 +1,10 @@ ++[Unit] ++Description=NFSv4 Client Tracking Daemon ++DefaultDependencies=no ++Conflicts=umount.target ++Requires=rpc_pipefs.target proc-fs-nfsd.mount ++After=rpc_pipefs.target proc-fs-nfsd.mount ++ ++[Service] ++Type=forking ++ExecStart=/usr/sbin/nfsdcld +diff --git a/tools/Makefile.am b/tools/Makefile.am +index 4266da49..53e61170 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -8,6 +8,10 @@ endif + + OPTDIRS += nfsconf + ++if CONFIG_NFSDCLD ++OPTDIRS += clddb-tool ++endif ++ + SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/tools/clddb-tool/Makefile.am b/tools/clddb-tool/Makefile.am +new file mode 100644 +index 00000000..15a8fd47 +--- /dev/null ++++ b/tools/clddb-tool/Makefile.am +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = clddb-tool.py ++ ++man8_MANS = clddb-tool.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) -m 755 clddb-tool.py $(DESTDIR)$(sbindir)/clddb-tool ++ ++MAINTAINERCLEANFILES=Makefile.in +diff --git a/tools/clddb-tool/clddb-tool.man b/tools/clddb-tool/clddb-tool.man +new file mode 100644 +index 00000000..e80b2c05 +--- /dev/null ++++ b/tools/clddb-tool/clddb-tool.man +@@ -0,0 +1,83 @@ ++.\" ++.\" clddb-tool(8) ++.\" ++.TH clddb-tool 8 "07 Aug 2019" ++.SH NAME ++clddb-tool \- Tool for manipulating the nfsdcld sqlite database ++.SH SYNOPSIS ++.B clddb-tool ++.RB [ \-h | \-\-help ] ++.P ++.B clddb-tool ++.RB [ \-p | \-\-path ++.IR dbpath ] ++.B fix-table-names ++.RB [ \-h | \-\-help ] ++.P ++.B clddb-tool ++.RB [ \-p | \-\-path ++.IR dbpath ] ++.B downgrade-schema ++.RB [ \-h | \-\-help ] ++.RB [ \-v | \-\-version ++.IR to-version ] ++.P ++.B clddb-tool ++.RB [ \-p | \-\-path ++.IR dbpath ] ++.B print ++.RB [ \-h | \-\-help ] ++.RB [ \-s | \-\-summary ] ++.P ++ ++.SH DESCRIPTION ++.RB "The " clddb-tool " command is provided to perform some manipulation of the nfsdcld sqlite database schema and to print the contents of the database." ++.SS Sub-commands ++Valid ++.B clddb-tool ++subcommands are: ++.IP "\fBfix-table-names\fP" ++.RB "A previous version of " nfsdcld "(8) contained a bug that corrupted the reboot epoch table names. This sub-command will fix those table names." ++.IP "\fBdowngrade-schema\fP" ++Downgrade the database schema. Currently the schema can only to downgraded from version 4 to version 3. ++.IP "\fBprint\fP" ++Display the contents of the database. Prints the schema version and the values of the current and recovery epochs. If the ++.BR \-s | \-\-summary ++option is not given, also prints the clients in the reboot epoch tables. ++.SH OPTIONS ++.SS Options valid for all sub-commands ++.TP ++.B \-h, \-\-help ++Show the help message and exit ++.TP ++\fB\-p \fIdbpath\fR, \fB\-\-path \fIdbpath\fR ++Open the sqlite database located at ++.I dbpath ++instead of ++.IR /var/lib/nfs/nfsdcld/main.sqlite ". " ++This is mainly for testing purposes. ++.SS Options specific to the downgrade-schema sub-command ++.TP ++\fB\-v \fIto-version\fR, \fB\-\-version \fIto-version\fR ++The schema version to downgrade to. Currently the schema can only be downgraded to version 3. ++.SS Options specific to the print sub-command ++.TP ++.B \-s, \-\-summary ++Do not list the clients in the reboot epoch tables in the output. ++.SH NOTES ++The ++.B clddb-tool ++command will not allow the ++.B fix-table-names ++or ++.B downgrade-schema ++subcommands to be used if ++.BR nfsdcld (8) ++is running. ++.SH FILES ++.TP ++.B /var/lib/nfs/nfsdcld/main.sqlite ++.SH SEE ALSO ++.BR nfsdcld (8) ++.SH AUTHOR ++Scott Mayhew +diff --git a/tools/clddb-tool/clddb-tool.py b/tools/clddb-tool/clddb-tool.py +new file mode 100644 +index 00000000..8a661318 +--- /dev/null ++++ b/tools/clddb-tool/clddb-tool.py +@@ -0,0 +1,266 @@ ++#!/usr/bin/python3 ++"""Tool for manipulating the nfsdcld sqlite database ++""" ++ ++__copyright__ = """ ++Copyright (C) 2019 Scott Mayhew ++ ++This program is free software; you can redistribute it and/or ++modify it under the terms of the GNU General Public License ++as published by the Free Software Foundation; either version 2 ++of the License, or (at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ++MA 02110-1301, USA. ++""" ++ ++import argparse ++import os ++import sqlite3 ++import sys ++ ++ ++class CldDb(): ++ def __init__(self, path): ++ self.con = sqlite3.connect(path) ++ self.con.row_factory = sqlite3.Row ++ for row in self.con.execute('select value from parameters ' ++ 'where key = "version"'): ++ self.version = int(row['value']) ++ for row in self.con.execute('select * from grace'): ++ self.current = int(row['current']) ++ self.recovery = int(row['recovery']) ++ ++ def __del__(self): ++ self.con.close() ++ ++ def __str__(self): ++ return ('Schema version: {self.version} ' ++ 'current epoch: {self.current} ' ++ 'recovery epoch: {self.recovery}'.format(self=self)) ++ ++ def _print_clients(self, epoch): ++ if epoch: ++ for row in self.con.execute('select * from "rec-{:016x}"' ++ .format(epoch)): ++ if self.version >= 4: ++ if row['princhash'] is not None: ++ princhash = row['princhash'].hex() ++ else: ++ princhash = "(null)" ++ print('id = {}, princhash = {}' ++ .format(row['id'].decode(), princhash)) ++ else: ++ print('id = {}'.format(row['id'].decode())) ++ ++ def print_current_clients(self): ++ print('Clients in current epoch:') ++ self._print_clients(self.current) ++ ++ def print_recovery_clients(self): ++ if self.recovery: ++ print('Clients in recovery epoch:') ++ self._print_clients(self.recovery) ++ ++ def check_bad_table_names(self): ++ bad_names = [] ++ for row in self.con.execute('select name from sqlite_master ' ++ 'where type = "table" ' ++ 'and name like "%rec-%" ' ++ 'and length(name) < 20'): ++ bad_names.append(row['name']) ++ return bad_names ++ ++ def fix_bad_table_names(self): ++ try: ++ self.con.execute('begin exclusive transaction') ++ bad_names = self.check_bad_table_names() ++ for bad_name in bad_names: ++ epoch = int(bad_name.split('-')[1], base=16) ++ if epoch == self.current or epoch == self.recovery: ++ if epoch == self.current: ++ which = 'current' ++ else: ++ which = 'recovery' ++ print('found invalid table name {} for {} epoch' ++ .format(bad_name, which)) ++ self.con.execute('alter table "{}" ' ++ 'rename to "rec-{:016x}"' ++ .format(bad_name, epoch)) ++ print('renamed to rec-{:016x}'.format(epoch)) ++ else: ++ print('found invalid table name {} for unknown epoch {}' ++ .format(bad_name, epoch)) ++ self.con.execute('drop table "{}"'.format(bad_name)) ++ print('dropped table {}'.format(bad_name)) ++ except sqlite3.Error: ++ self.con.rollback() ++ else: ++ self.con.commit() ++ ++ def has_princ_data(self): ++ if self.version < 4: ++ return False ++ for row in self.con.execute('select count(*) ' ++ 'from "rec-{:016x}" ' ++ 'where princhash not null' ++ .format(self.current)): ++ count = row[0] ++ if self.recovery: ++ for row in self.con.execute('select count(*) ' ++ 'from "rec-{:016x}" ' ++ 'where princhash not null' ++ .format(self.current)): ++ count = count + row[0] ++ if count: ++ return True ++ return False ++ ++ def _downgrade_table_v4_to_v3(self, epoch): ++ if not self.con.in_transaction: ++ raise sqlite3.Error ++ try: ++ self.con.execute('create table "new_rec-{:016x}" ' ++ '(id blob primary key)'.format(epoch)) ++ self.con.execute('insert into "new_rec-{:016x}" ' ++ 'select id from "rec-{:016x}"' ++ .format(epoch, epoch)) ++ self.con.execute('drop table "rec-{:016x}"'.format(epoch)) ++ self.con.execute('alter table "new_rec-{:016x}" ' ++ 'rename to "rec-{:016x}"' ++ .format(epoch, epoch)) ++ except sqlite3.Error: ++ raise ++ ++ def downgrade_schema_v4_to_v3(self): ++ try: ++ self.con.execute('begin exclusive transaction') ++ for row in self.con.execute('select value from parameters ' ++ 'where key = "version"'): ++ version = int(row['value']) ++ if version != self.version: ++ raise sqlite3.Error ++ for row in self.con.execute('select * from grace'): ++ current = int(row['current']) ++ recovery = int(row['recovery']) ++ if current != self.current: ++ raise sqlite3.Error ++ if recovery != self.recovery: ++ raise sqlite3.Error ++ self._downgrade_table_v4_to_v3(current) ++ if recovery: ++ self._downgrade_table_v4_to_v3(recovery) ++ self.con.execute('update parameters ' ++ 'set value = "3" ' ++ 'where key = "version"') ++ self.version = 3 ++ except sqlite3.Error: ++ self.con.rollback() ++ print('Downgrade failed') ++ else: ++ self.con.commit() ++ print('Downgrade successful') ++ ++ ++def nfsdcld_active(): ++ rc = os.system('ps -C nfsdcld >/dev/null 2>/dev/null') ++ if rc == 0: ++ return True ++ return False ++ ++ ++def fix_table_names_command(db, args): ++ if nfsdcld_active(): ++ print('Warning: nfsdcld is running!') ++ ans = input('Continue? ') ++ if ans.lower() not in ['y', 'yes']: ++ print('Operation canceled.') ++ return ++ bad_names = db.check_bad_table_names() ++ if not bad_names: ++ print('No invalid table names found.') ++ return ++ db.fix_bad_table_names() ++ ++ ++def downgrade_schema_command(db, args): ++ if nfsdcld_active(): ++ print('Warning: nfsdcld is running!') ++ ans = input('Continue? ') ++ if ans.lower() not in ['y', 'yes']: ++ print('Operation canceled') ++ return ++ if db.version != 4: ++ print('Cannot downgrade database from schema version {}.' ++ .format(db.version)) ++ return ++ if args.version != 3: ++ print('Cannot downgrade to version {}.'.format(args.version)) ++ return ++ bad_names = db.check_bad_table_names() ++ if bad_names: ++ print('Invalid table names detected.') ++ print('Please run "{} fix-table-names" before downgrading the schema.' ++ .format(sys.argv[0])) ++ return ++ if db.has_princ_data(): ++ print('Warning: database has principal data, which will be erased.') ++ ans = input('Continue? ') ++ if ans.lower() not in ['y', 'yes']: ++ print('Operation canceled') ++ return ++ db.downgrade_schema_v4_to_v3() ++ ++ ++def print_command(db, args): ++ print(str(db)) ++ if not args.summary: ++ bad_names = db.check_bad_table_names() ++ if bad_names: ++ print('Invalid table names detected.') ++ print('Please run "{} fix-table-names".'.format(sys.argv[0])) ++ return ++ db.print_current_clients() ++ db.print_recovery_clients() ++ ++ ++def main(): ++ parser = argparse.ArgumentParser() ++ parser.add_argument('-p', '--path', ++ default='/var/lib/nfs/nfsdcld/main.sqlite', ++ help='path to the database ' ++ '(default: /var/lib/nfs/nfsdcld/main.sqlite)') ++ subparsers = parser.add_subparsers(help='sub-command help') ++ fix_parser = subparsers.add_parser('fix-table-names', ++ help='fix invalid table names') ++ fix_parser.set_defaults(func=fix_table_names_command) ++ downgrade_parser = subparsers.add_parser('downgrade-schema', ++ help='downgrade database schema') ++ downgrade_parser.add_argument('-v', '--version', type=int, choices=[3], ++ default=3, ++ help='version to downgrade to') ++ downgrade_parser.set_defaults(func=downgrade_schema_command) ++ print_parser = subparsers.add_parser('print', ++ help='print database info') ++ print_parser.add_argument('-s', '--summary', default=False, ++ action='store_true', ++ help='print summary only') ++ print_parser.set_defaults(func=print_command) ++ args = parser.parse_args() ++ if not os.path.exists(args.path): ++ return parser.print_usage() ++ clddb = CldDb(args.path) ++ return args.func(clddb, args) ++ ++ ++if __name__ == '__main__': ++ if len(sys.argv) == 1: ++ sys.argv.extend(['print', '--summary']) ++ main() +diff --git a/utils/Makefile.am b/utils/Makefile.am +index 0a5b062c..4c930a4b 100644 +--- a/utils/Makefile.am ++++ b/utils/Makefile.am +@@ -19,6 +19,10 @@ if CONFIG_MOUNT + OPTDIRS += mount + endif + ++if CONFIG_NFSDCLD ++OPTDIRS += nfsdcld ++endif ++ + if CONFIG_NFSDCLTRACK + OPTDIRS += nfsdcltrack + endif +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index cd3c979d..4b9634b7 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -644,6 +644,9 @@ out: + return result; + } + ++#ifdef HAVE_FUNC_ATTRIBUTE_FORMAT ++__attribute__((format (printf, 2, 3))) ++#endif + static char + dumpopt(char c, char *fmt, ...) + { +diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c +index eedbddab..8b0aaf1a 100644 +--- a/utils/mount/fstab.c ++++ b/utils/mount/fstab.c +@@ -7,6 +7,10 @@ + * - Moved code to nfs-utils/support/nfs from util-linux/mount. + */ + ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ + #include + #include + #include +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index a054ce6f..c73e29be 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -967,8 +967,7 @@ lookup_export(char *dom, char *path, struct addrinfo *ai) + } else if (found_type == i && found->m_warned == 0) { + xlog(L_WARNING, "%s exported to both %s and %s, " + "arbitrarily choosing options from first", +- path, found->m_client->m_hostname, exp->m_client->m_hostname, +- dom); ++ path, found->m_client->m_hostname, exp->m_client->m_hostname); + found->m_warned = 1; + } + } +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c +index 086c39bf..0b891121 100644 +--- a/utils/mountd/mountd.c ++++ b/utils/mountd/mountd.c +@@ -209,10 +209,10 @@ killer (int sig) + } + + static void +-sig_hup (int sig) ++sig_hup (int UNUSED(sig)) + { + /* don't exit on SIGHUP */ +- xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n", sig); ++ xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n"); + return; + } + +diff --git a/utils/nfsdcld/Makefile.am b/utils/nfsdcld/Makefile.am +new file mode 100644 +index 00000000..273d64f1 +--- /dev/null ++++ b/utils/nfsdcld/Makefile.am +@@ -0,0 +1,15 @@ ++## Process this file with automake to produce Makefile.in ++ ++man8_MANS = nfsdcld.man ++EXTRA_DIST = $(man8_MANS) ++ ++AM_CFLAGS += -D_LARGEFILE64_SOURCE ++sbin_PROGRAMS = nfsdcld ++ ++nfsdcld_SOURCES = nfsdcld.c sqlite.c legacy.c ++nfsdcld_LDADD = ../../support/nfs/libnfs.la $(LIBEVENT) $(LIBSQLITE) $(LIBCAP) ++ ++noinst_HEADERS = sqlite.h cld-internal.h legacy.h ++ ++MAINTAINERCLEANFILES = Makefile.in ++ +diff --git a/utils/nfsdcld/cld-internal.h b/utils/nfsdcld/cld-internal.h +new file mode 100644 +index 00000000..05f01be2 +--- /dev/null ++++ b/utils/nfsdcld/cld-internal.h +@@ -0,0 +1,44 @@ ++/* ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _CLD_INTERNAL_H_ ++#define _CLD_INTERNAL_H_ ++ ++#if CLD_UPCALL_VERSION >= 2 ++#define UPCALL_VERSION 2 ++#else ++#define UPCALL_VERSION 1 ++#endif ++ ++struct cld_client { ++ int cl_fd; ++ struct event cl_event; ++ union { ++ struct cld_msg cl_msg; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 cl_msg_v2; ++#endif ++ } cl_u; ++}; ++ ++uint64_t current_epoch; ++uint64_t recovery_epoch; ++int first_time; ++int num_cltrack_records; ++int num_legacy_records; ++ ++#endif /* _CLD_INTERNAL_H_ */ +diff --git a/utils/nfsdcld/legacy.c b/utils/nfsdcld/legacy.c +new file mode 100644 +index 00000000..3c6bea6c +--- /dev/null ++++ b/utils/nfsdcld/legacy.c +@@ -0,0 +1,185 @@ ++/* ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "cld.h" ++#include "sqlite.h" ++#include "xlog.h" ++#include "legacy.h" ++ ++#define NFSD_RECDIR_FILE "/proc/fs/nfsd/nfsv4recoverydir" ++ ++/* ++ * Loads client records from the v4recovery directory into the database. ++ * Records are prefixed with the string "hash:" and include the '\0' byte. ++ * ++ * Called during database initialization as part of a one-time "upgrade". ++ */ ++void ++legacy_load_clients_from_recdir(int *num_records) ++{ ++ int fd; ++ DIR *v4recovery; ++ struct dirent *entry; ++ char recdirname[PATH_MAX]; ++ char buf[NFS4_OPAQUE_LIMIT]; ++ struct stat st; ++ char *nl; ++ ++ fd = open(NFSD_RECDIR_FILE, O_RDONLY); ++ if (fd < 0) { ++ xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE); ++ return; ++ } ++ if (read(fd, recdirname, PATH_MAX) < 0) { ++ xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE); ++ return; ++ } ++ close(fd); ++ /* the output from the proc file isn't null-terminated */ ++ nl = strchr(recdirname, '\n'); ++ if (!nl) ++ return; ++ *nl = '\0'; ++ if (stat(recdirname, &st) < 0) { ++ xlog(D_GENERAL, "Unable to stat %s: %d", recdirname, errno); ++ return; ++ } ++ if (!S_ISDIR(st.st_mode)) { ++ xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdirname ++ , st.st_mode); ++ return; ++ } ++ v4recovery = opendir(recdirname); ++ if (!v4recovery) ++ return; ++ while ((entry = readdir(v4recovery))) { ++ int ret; ++ ++ /* skip "." and ".." */ ++ if (entry->d_name[0] == '.') { ++ switch (entry->d_name[1]) { ++ case '\0': ++ continue; ++ case '.': ++ if (entry->d_name[2] == '\0') ++ continue; ++ } ++ } ++ /* prefix legacy records with the string "hash:" */ ++ ret = snprintf(buf, sizeof(buf), "hash:%s", entry->d_name); ++ /* if there's a problem, then skip this entry */ ++ if (ret < 0 || (size_t)ret >= sizeof(buf)) { ++ xlog(L_WARNING, "%s: unable to build client string for %s!", ++ __func__, entry->d_name); ++ continue; ++ } ++ /* legacy client records need to include the null terminator */ ++ ret = sqlite_insert_client((unsigned char *)buf, strlen(buf) + 1); ++ if (ret) ++ xlog(L_WARNING, "%s: unable to insert %s: %d", __func__, ++ entry->d_name, ret); ++ else ++ (*num_records)++; ++ } ++ closedir(v4recovery); ++} ++ ++/* ++ * Cleans out the v4recovery directory. ++ * ++ * Called upon receipt of the first "GraceDone" upcall only. ++ */ ++void ++legacy_clear_recdir(void) ++{ ++ int fd; ++ DIR *v4recovery; ++ struct dirent *entry; ++ char recdirname[PATH_MAX]; ++ char dirname[PATH_MAX]; ++ struct stat st; ++ char *nl; ++ ++ fd = open(NFSD_RECDIR_FILE, O_RDONLY); ++ if (fd < 0) { ++ xlog(D_GENERAL, "Unable to open %s: %m", NFSD_RECDIR_FILE); ++ return; ++ } ++ if (read(fd, recdirname, PATH_MAX) < 0) { ++ xlog(D_GENERAL, "Unable to read from %s: %m", NFSD_RECDIR_FILE); ++ return; ++ } ++ close(fd); ++ /* the output from the proc file isn't null-terminated */ ++ nl = strchr(recdirname, '\n'); ++ if (!nl) ++ return; ++ *nl = '\0'; ++ if (stat(recdirname, &st) < 0) { ++ xlog(D_GENERAL, "Unable to stat %s: %d", recdirname, errno); ++ return; ++ } ++ if (!S_ISDIR(st.st_mode)) { ++ xlog(D_GENERAL, "%s is not a directory: mode=0%o", recdirname ++ , st.st_mode); ++ return; ++ } ++ v4recovery = opendir(recdirname); ++ if (!v4recovery) ++ return; ++ while ((entry = readdir(v4recovery))) { ++ int len; ++ ++ /* skip "." and ".." */ ++ if (entry->d_name[0] == '.') { ++ switch (entry->d_name[1]) { ++ case '\0': ++ continue; ++ case '.': ++ if (entry->d_name[2] == '\0') ++ continue; ++ } ++ } ++ len = snprintf(dirname, sizeof(dirname), "%s/%s", recdirname, ++ entry->d_name); ++ /* if there's a problem, then skip this entry */ ++ if (len < 0 || (size_t)len >= sizeof(dirname)) { ++ xlog(L_WARNING, "%s: unable to build filename for %s!", ++ __func__, entry->d_name); ++ continue; ++ } ++ len = rmdir(dirname); ++ if (len) ++ xlog(L_WARNING, "%s: unable to rmdir %s: %d", __func__, ++ dirname, len); ++ } ++ closedir(v4recovery); ++} +diff --git a/utils/nfsdcld/legacy.h b/utils/nfsdcld/legacy.h +new file mode 100644 +index 00000000..8988f6e8 +--- /dev/null ++++ b/utils/nfsdcld/legacy.h +@@ -0,0 +1,24 @@ ++/* ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _LEGACY_H_ ++#define _LEGACY_H_ ++ ++void legacy_load_clients_from_recdir(int *); ++void legacy_clear_recdir(void); ++ ++#endif /* _LEGACY_H_ */ +diff --git a/utils/nfsdcld/nfsdcld.c b/utils/nfsdcld/nfsdcld.c +new file mode 100644 +index 00000000..2ad10019 +--- /dev/null ++++ b/utils/nfsdcld/nfsdcld.c +@@ -0,0 +1,866 @@ ++/* ++ * nfsdcld.c -- NFSv4 client name tracking daemon ++ * ++ * Copyright (C) 2011 Red Hat, Jeff Layton ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_SYS_CAPABILITY_H ++#include ++#include ++#endif ++ ++#include "xlog.h" ++#include "nfslib.h" ++#include "cld.h" ++#include "cld-internal.h" ++#include "sqlite.h" ++#include "../mount/version.h" ++#include "conffile.h" ++#include "legacy.h" ++ ++#ifndef DEFAULT_PIPEFS_DIR ++#define DEFAULT_PIPEFS_DIR NFS_STATEDIR "/rpc_pipefs" ++#endif ++ ++#define DEFAULT_CLD_PATH "/nfsd/cld" ++ ++#ifndef CLD_DEFAULT_STORAGEDIR ++#define CLD_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcld" ++#endif ++ ++#define NFSD_END_GRACE_FILE "/proc/fs/nfsd/v4_end_grace" ++ ++/* private data structures */ ++ ++/* global variables */ ++static char pipefs_dir[PATH_MAX] = DEFAULT_PIPEFS_DIR; ++static char pipepath[PATH_MAX]; ++static int inotify_fd = -1; ++static struct event pipedir_event; ++static bool old_kernel = false; ++ ++static struct option longopts[] = ++{ ++ { "help", 0, NULL, 'h' }, ++ { "foreground", 0, NULL, 'F' }, ++ { "debug", 0, NULL, 'd' }, ++ { "pipefsdir", 1, NULL, 'p' }, ++ { "storagedir", 1, NULL, 's' }, ++ { NULL, 0, 0, 0 }, ++}; ++ ++/* forward declarations */ ++static void cldcb(int UNUSED(fd), short which, void *data); ++ ++static void ++usage(char *progname) ++{ ++ printf("%s [ -hFd ] [ -p pipefsdir ] [ -s storagedir ]\n", progname); ++} ++ ++static int ++cld_set_caps(void) ++{ ++ int ret = 0; ++#ifdef HAVE_SYS_CAPABILITY_H ++ unsigned long i; ++ cap_t caps; ++ ++ if (getuid() != 0) { ++ xlog(L_ERROR, "Not running as root. Daemon won't be able to " ++ "open the pipe after dropping capabilities!"); ++ return -EINVAL; ++ } ++ ++ /* prune the bounding set to nothing */ ++ for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0 ; ++i) { ++ ret = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); ++ if (ret) { ++ xlog(L_ERROR, "Unable to prune capability %lu from " ++ "bounding set: %m", i); ++ return -errno; ++ } ++ } ++ ++ /* get a blank capset */ ++ caps = cap_init(); ++ if (caps == NULL) { ++ xlog(L_ERROR, "Unable to get blank capability set: %m"); ++ return -errno; ++ } ++ ++ /* reset the process capabilities */ ++ if (cap_set_proc(caps) != 0) { ++ xlog(L_ERROR, "Unable to set process capabilities: %m"); ++ ret = -errno; ++ } ++ cap_free(caps); ++#endif ++ return ret; ++} ++ ++#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX) ++ ++static int ++cld_pipe_open(struct cld_client *clnt) ++{ ++ int fd; ++ ++ xlog(D_GENERAL, "%s: opening upcall pipe %s", __func__, pipepath); ++ fd = open(pipepath, O_RDWR, 0); ++ if (fd < 0) { ++ xlog(D_GENERAL, "%s: open of %s failed: %m", __func__, pipepath); ++ return -errno; ++ } ++ ++ if (event_initialized(&clnt->cl_event)) ++ event_del(&clnt->cl_event); ++ if (clnt->cl_fd >= 0) ++ close(clnt->cl_fd); ++ ++ clnt->cl_fd = fd; ++ event_set(&clnt->cl_event, clnt->cl_fd, EV_READ, cldcb, clnt); ++ /* event_add is done by the caller */ ++ return 0; ++} ++ ++static void ++cld_inotify_cb(int UNUSED(fd), short which, void *data) ++{ ++ int ret; ++ size_t elen; ++ ssize_t rret; ++ char evbuf[INOTIFY_EVENT_MAX]; ++ char *dirc = NULL, *pname; ++ struct inotify_event *event = (struct inotify_event *)evbuf; ++ struct cld_client *clnt = data; ++ ++ if (which != EV_READ) ++ return; ++ ++ xlog(D_GENERAL, "%s: called for EV_READ", __func__); ++ ++ dirc = strndup(pipepath, PATH_MAX); ++ if (!dirc) { ++ xlog(L_ERROR, "%s: unable to allocate memory", __func__); ++ goto out; ++ } ++ ++ rret = read(inotify_fd, evbuf, INOTIFY_EVENT_MAX); ++ if (rret < 0) { ++ xlog(L_ERROR, "%s: read from inotify fd failed: %m", __func__); ++ goto out; ++ } ++ ++ /* check to see if we have a filename in the evbuf */ ++ if (!event->len) { ++ xlog(D_GENERAL, "%s: no filename in inotify event", __func__); ++ goto out; ++ } ++ ++ pname = basename(dirc); ++ elen = strnlen(event->name, event->len); ++ ++ /* does the filename match our pipe? */ ++ if (strlen(pname) != elen || memcmp(pname, event->name, elen)) { ++ xlog(D_GENERAL, "%s: wrong filename (%s)", __func__, ++ event->name); ++ goto out; ++ } ++ ++ ret = cld_pipe_open(clnt); ++ switch (ret) { ++ case 0: ++ /* readd the event for the cl_event pipe */ ++ event_add(&clnt->cl_event, NULL); ++ break; ++ case -ENOENT: ++ /* pipe must have disappeared, wait for it to come back */ ++ goto out; ++ default: ++ /* anything else is fatal */ ++ xlog(L_FATAL, "%s: unable to open new pipe (%d). Aborting.", ++ __func__, ret); ++ exit(ret); ++ } ++ ++out: ++ event_add(&pipedir_event, NULL); ++ free(dirc); ++} ++ ++static int ++cld_inotify_setup(void) ++{ ++ int ret; ++ char *dirc, *dname; ++ ++ dirc = strndup(pipepath, PATH_MAX); ++ if (!dirc) { ++ xlog_err("%s: unable to allocate memory", __func__); ++ ret = -ENOMEM; ++ goto out_free; ++ } ++ ++ dname = dirname(dirc); ++ ++ inotify_fd = inotify_init(); ++ if (inotify_fd < 0) { ++ xlog_err("%s: inotify_init failed: %m", __func__); ++ ret = -errno; ++ goto out_free; ++ } ++ ++ ret = inotify_add_watch(inotify_fd, dname, IN_CREATE); ++ if (ret < 0) { ++ xlog_err("%s: inotify_add_watch failed: %m", __func__); ++ ret = -errno; ++ goto out_err; ++ } ++ ++out_free: ++ free(dirc); ++ return 0; ++out_err: ++ close(inotify_fd); ++ goto out_free; ++} ++ ++/* ++ * Set an inotify watch on the directory that should contain the pipe, and then ++ * try to open it. If it fails with anything but -ENOENT, return the error ++ * immediately. ++ * ++ * If it succeeds, then set up the pipe event handler. At that point, set up ++ * the inotify event handler and go ahead and return success. ++ */ ++static int ++cld_pipe_init(struct cld_client *clnt) ++{ ++ int ret; ++ ++ xlog(D_GENERAL, "%s: init pipe handlers", __func__); ++ ++ ret = cld_inotify_setup(); ++ if (ret != 0) ++ goto out; ++ ++ clnt->cl_fd = -1; ++ ret = cld_pipe_open(clnt); ++ switch (ret) { ++ case 0: ++ /* add the event and we're good to go */ ++ event_add(&clnt->cl_event, NULL); ++ break; ++ case -ENOENT: ++ /* ignore this error -- cld_inotify_cb will handle it */ ++ ret = 0; ++ break; ++ default: ++ /* anything else is fatal */ ++ close(inotify_fd); ++ goto out; ++ } ++ ++ /* set event for inotify read */ ++ event_set(&pipedir_event, inotify_fd, EV_READ, cld_inotify_cb, clnt); ++ event_add(&pipedir_event, NULL); ++out: ++ return ret; ++} ++ ++/* ++ * Older kernels will not tell nfsdcld when a grace period has started. ++ * Therefore we have to peek at the /proc/fs/nfsd/v4_end_grace file to ++ * see if nfsd is in grace. We have to do this for create and remove ++ * upcalls to ensure that the correct table is being updated - otherwise ++ * we could lose client records when the grace period is lifted. ++ */ ++static int ++cld_check_grace_period(void) ++{ ++ int fd, ret = 0; ++ char c; ++ ++ if (!old_kernel) ++ return 0; ++ if (recovery_epoch != 0) ++ return 0; ++ fd = open(NFSD_END_GRACE_FILE, O_RDONLY); ++ if (fd < 0) { ++ xlog(L_WARNING, "Unable to open %s: %m", ++ NFSD_END_GRACE_FILE); ++ return 1; ++ } ++ if (read(fd, &c, 1) < 0) { ++ xlog(L_WARNING, "Unable to read from %s: %m", ++ NFSD_END_GRACE_FILE); ++ return 1; ++ } ++ close(fd); ++ if (c == 'N') { ++ xlog(L_WARNING, "nfsd is in grace but didn't send a gracestart upcall, " ++ "please update the kernel"); ++ ret = sqlite_grace_start(); ++ } ++ return ret; ++} ++ ++#if UPCALL_VERSION >= 2 ++static ssize_t cld_message_size(void *msg) ++{ ++ struct cld_msg_hdr *hdr = (struct cld_msg_hdr *)msg; ++ ++ switch (hdr->cm_vers) { ++ case 1: ++ return sizeof(struct cld_msg); ++ case 2: ++ return sizeof(struct cld_msg_v2); ++ default: ++ xlog(L_FATAL, "%s invalid upcall version %d", __func__, ++ hdr->cm_vers); ++ exit(-EINVAL); ++ } ++} ++#else ++static ssize_t cld_message_size(void *UNUSED(msg)) ++{ ++ return sizeof(struct cld_msg); ++} ++#endif ++ ++static void ++cld_not_implemented(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ xlog(D_GENERAL, "%s: downcalling with not implemented error", __func__); ++ ++ /* set up reply */ ++ cmsg->cm_status = -EOPNOTSUPP; ++ ++ bsize = cld_message_size(cmsg); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ++ /* reopen pipe, just to be sure */ ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", __func__, ret); ++ exit(ret); ++ } ++} ++ ++static void ++cld_get_version(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ xlog(D_GENERAL, "%s: version = %u.", __func__, UPCALL_VERSION); ++ ++ cmsg->cm_u.cm_version = UPCALL_VERSION; ++ cmsg->cm_status = 0; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_create(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ ret = cld_check_grace_period(); ++ if (ret) ++ goto reply; ++ ++ xlog(D_GENERAL, "%s: create client record.", __func__); ++ ++#if UPCALL_VERSION >= 2 ++ if (cmsg->cm_vers >= 2) ++ ret = sqlite_insert_client_and_princhash( ++ cmsg->cm_u.cm_clntinfo.cc_name.cn_id, ++ cmsg->cm_u.cm_clntinfo.cc_name.cn_len, ++ cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data, ++ cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len); ++ else ++ ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++#else ++ ret = sqlite_insert_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++#endif ++ ++reply: ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_remove(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ ret = cld_check_grace_period(); ++ if (ret) ++ goto reply; ++ ++ xlog(D_GENERAL, "%s: remove client record.", __func__); ++ ++ ret = sqlite_remove_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++ ++reply: ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "%s: downcall with status %d", __func__, ++ cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_check(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ /* ++ * If we get a check upcall at all, it means we're talking to an old ++ * kernel. Furthermore, if we're not in grace it means this is the ++ * first client to do a reclaim. Log a message and use ++ * sqlite_grace_start() to advance the epoch numbers. ++ */ ++ if (recovery_epoch == 0) { ++ xlog(D_GENERAL, "%s: received a check upcall, please update the kernel", ++ __func__); ++ ret = sqlite_grace_start(); ++ if (ret) ++ goto reply; ++ } ++ ++ xlog(D_GENERAL, "%s: check client record", __func__); ++ ++ ret = sqlite_check_client(cmsg->cm_u.cm_name.cn_id, ++ cmsg->cm_u.cm_name.cn_len); ++ ++reply: ++ /* set up reply */ ++ cmsg->cm_status = ret ? -EACCES : ret; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "%s: downcall with status %d", __func__, ++ cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cld_gracedone(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ /* ++ * If we got a "gracedone" upcall while we're not in grace, then ++ * 1) we must be talking to an old kernel ++ * 2) no clients attempted to reclaim ++ * In that case, log a message and use sqlite_grace_start() to ++ * advance the epoch numbers, and then proceed as normal. ++ */ ++ if (recovery_epoch == 0) { ++ xlog(D_GENERAL, "%s: received gracedone upcall " ++ "while not in grace, please update the kernel", ++ __func__); ++ ret = sqlite_grace_start(); ++ if (ret) ++ goto reply; ++ } ++ ++ xlog(D_GENERAL, "%s: grace done.", __func__); ++ ++ ret = sqlite_grace_done(); ++ ++ if (first_time) { ++ if (num_cltrack_records > 0) ++ sqlite_delete_cltrack_records(); ++ if (num_legacy_records > 0) ++ legacy_clear_recdir(); ++ sqlite_first_time_done(); ++ first_time = 0; ++ } ++ ++reply: ++ /* set up reply: downcall with 0 status */ ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static int ++gracestart_callback(struct cld_client *clnt) { ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ cmsg->cm_status = -EINPROGRESS; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "Sending client %.*s", ++ cmsg->cm_u.cm_name.cn_len, cmsg->cm_u.cm_name.cn_id); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) ++ return -EIO; ++ return 0; ++} ++ ++static void ++cld_gracestart(struct cld_client *clnt) ++{ ++ int ret; ++ ssize_t bsize, wsize; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ xlog(D_GENERAL, "%s: updating grace epochs", __func__); ++ ++ ret = sqlite_grace_start(); ++ if (ret) ++ goto reply; ++ ++ xlog(D_GENERAL, "%s: sending client records to the kernel", __func__); ++ ++ ret = sqlite_iterate_recovery(&gracestart_callback, clnt); ++ ++reply: ++ /* set up reply: downcall with 0 status */ ++ cmsg->cm_status = ret ? -EREMOTEIO : ret; ++ ++ bsize = cld_message_size(cmsg); ++ xlog(D_GENERAL, "Doing downcall with status %d", cmsg->cm_status); ++ wsize = atomicio((void *)write, clnt->cl_fd, cmsg, bsize); ++ if (wsize != bsize) { ++ xlog(L_ERROR, "%s: problem writing to cld pipe (%zd): %m", ++ __func__, wsize); ++ ret = cld_pipe_open(clnt); ++ if (ret) { ++ xlog(L_FATAL, "%s: unable to reopen pipe: %d", ++ __func__, ret); ++ exit(ret); ++ } ++ } ++} ++ ++static void ++cldcb(int UNUSED(fd), short which, void *data) ++{ ++ ssize_t len; ++ struct cld_client *clnt = data; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ if (which != EV_READ) ++ goto out; ++ ++ len = atomicio(read, clnt->cl_fd, cmsg, sizeof(*cmsg)); ++ if (len <= 0) { ++ xlog(L_ERROR, "%s: pipe read failed: %m", __func__); ++ cld_pipe_open(clnt); ++ goto out; ++ } ++ ++ if (cmsg->cm_vers > UPCALL_VERSION) { ++ xlog(L_ERROR, "%s: unsupported upcall version: %hu", ++ __func__, cmsg->cm_vers); ++ cld_pipe_open(clnt); ++ goto out; ++ } ++ ++ switch(cmsg->cm_cmd) { ++ case Cld_Create: ++ cld_create(clnt); ++ break; ++ case Cld_Remove: ++ cld_remove(clnt); ++ break; ++ case Cld_Check: ++ cld_check(clnt); ++ break; ++ case Cld_GraceDone: ++ cld_gracedone(clnt); ++ break; ++ case Cld_GraceStart: ++ cld_gracestart(clnt); ++ break; ++ case Cld_GetVersion: ++ cld_get_version(clnt); ++ break; ++ default: ++ xlog(L_WARNING, "%s: command %u is not yet implemented", ++ __func__, cmsg->cm_cmd); ++ cld_not_implemented(clnt); ++ } ++out: ++ event_add(&clnt->cl_event, NULL); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int arg; ++ int rc = 0; ++ bool foreground = false; ++ char *progname; ++ char *storagedir = CLD_DEFAULT_STORAGEDIR; ++ struct cld_client clnt; ++ char *s; ++ first_time = 0; ++ num_cltrack_records = 0; ++ num_legacy_records = 0; ++ ++ memset(&clnt, 0, sizeof(clnt)); ++ ++ progname = strdup(basename(argv[0])); ++ if (!progname) { ++ fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]); ++ return 1; ++ } ++ ++ event_init(); ++ xlog_syslog(0); ++ xlog_stderr(1); ++ ++ conf_init_file(NFS_CONFFILE); ++ s = conf_get_str("general", "pipefs-directory"); ++ if (s) ++ strlcpy(pipefs_dir, s, sizeof(pipefs_dir)); ++ s = conf_get_str("nfsdcld", "storagedir"); ++ if (s) ++ storagedir = s; ++ rc = conf_get_num("nfsdcld", "debug", 0); ++ if (rc > 0) ++ xlog_config(D_ALL, 1); ++ ++ /* process command-line options */ ++ while ((arg = getopt_long(argc, argv, "hdFp:s:", longopts, ++ NULL)) != EOF) { ++ switch (arg) { ++ case 'd': ++ xlog_config(D_ALL, 1); ++ break; ++ case 'F': ++ foreground = true; ++ break; ++ case 'p': ++ strlcpy(pipefs_dir, optarg, sizeof(pipefs_dir)); ++ break; ++ case 's': ++ storagedir = optarg; ++ break; ++ default: ++ usage(progname); ++ return 0; ++ } ++ } ++ ++ strlcpy(pipepath, pipefs_dir, sizeof(pipepath)); ++ strlcat(pipepath, DEFAULT_CLD_PATH, sizeof(pipepath)); ++ ++ xlog_open(progname); ++ if (!foreground) { ++ xlog_syslog(1); ++ xlog_stderr(0); ++ rc = daemon(0, 0); ++ if (rc) { ++ xlog(L_ERROR, "Unable to daemonize: %m"); ++ goto out; ++ } ++ } ++ ++ /* drop all capabilities */ ++ rc = cld_set_caps(); ++ if (rc) ++ goto out; ++ ++ /* ++ * now see if the storagedir is writable by root w/o CAP_DAC_OVERRIDE. ++ * If it isn't then give the user a warning but proceed as if ++ * everything is OK. If the DB has already been created, then ++ * everything might still work. If it doesn't exist at all, then ++ * assume that the maindb init will be able to create it. Fail on ++ * anything else. ++ */ ++ if (access(storagedir, W_OK) == -1) { ++ switch (errno) { ++ case EACCES: ++ xlog(L_WARNING, "Storage directory %s is not writable. " ++ "Should be owned by root and writable " ++ "by owner!", storagedir); ++ break; ++ case ENOENT: ++ /* ignore and assume that we can create dir as root */ ++ break; ++ default: ++ xlog(L_ERROR, "Unexpected error when checking access " ++ "on %s: %m", storagedir); ++ rc = -errno; ++ goto out; ++ } ++ } ++ ++ if (linux_version_code() < MAKE_VERSION(4, 20, 0)) ++ old_kernel = true; ++ ++ /* set up storage db */ ++ rc = sqlite_prepare_dbh(storagedir); ++ if (rc) { ++ xlog(L_ERROR, "Failed to open main database: %d", rc); ++ goto out; ++ } ++ ++ /* set up event handler */ ++ rc = cld_pipe_init(&clnt); ++ if (rc) ++ goto out; ++ ++ xlog(D_GENERAL, "%s: Starting event dispatch handler.", __func__); ++ rc = event_dispatch(); ++ if (rc < 0) ++ xlog(L_ERROR, "%s: event_dispatch failed: %m", __func__); ++ ++ close(clnt.cl_fd); ++ close(inotify_fd); ++out: ++ free(progname); ++ return rc; ++} +diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man +new file mode 100644 +index 00000000..4c2b1e80 +--- /dev/null ++++ b/utils/nfsdcld/nfsdcld.man +@@ -0,0 +1,221 @@ ++.\" Automatically generated by Pod::Man 2.22 (Pod::Simple 3.13) ++.\" ++.\" Standard preamble: ++.\" ======================================================================== ++.de Sp \" Vertical space (when we can't use .PP) ++.if t .sp .5v ++.if n .sp ++.. ++.de Vb \" Begin verbatim text ++.ft CW ++.nf ++.ne \\$1 ++.. ++.de Ve \" End verbatim text ++.ft R ++.fi ++.. ++.\" Set up some character translations and predefined strings. \*(-- will ++.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left ++.\" double quote, and \*(R" will give a right double quote. \*(C+ will ++.\" give a nicer C++. Capital omega is used to do unbreakable dashes and ++.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, ++.\" nothing in troff, for use with C<>. ++.tr \(*W- ++.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' ++.ie n \{\ ++. ds -- \(*W- ++. ds PI pi ++. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch ++. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch ++. ds L" "" ++. ds R" "" ++. ds C` "" ++. ds C' "" ++'br\} ++.el\{\ ++. ds -- \|\(em\| ++. ds PI \(*p ++. ds L" `` ++. ds R" '' ++'br\} ++.\" ++.\" Escape single quotes in literal strings from groff's Unicode transform. ++.ie \n(.g .ds Aq \(aq ++.el .ds Aq ' ++.\" ++.\" If the F register is turned on, we'll generate index entries on stderr for ++.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index ++.\" entries marked with X<> in POD. Of course, you'll have to process the ++.\" output yourself in some meaningful fashion. ++.ie \nF \{\ ++. de IX ++. tm Index:\\$1\t\\n%\t"\\$2" ++.. ++. nr % 0 ++. rr F ++.\} ++.el \{\ ++. de IX ++.. ++.\} ++.\" ++.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). ++.\" Fear. Run. Save yourself. No user-serviceable parts. ++. \" fudge factors for nroff and troff ++.if n \{\ ++. ds #H 0 ++. ds #V .8m ++. ds #F .3m ++. ds #[ \f1 ++. ds #] \fP ++.\} ++.if t \{\ ++. ds #H ((1u-(\\\\n(.fu%2u))*.13m) ++. ds #V .6m ++. ds #F 0 ++. ds #[ \& ++. ds #] \& ++.\} ++. \" simple accents for nroff and troff ++.if n \{\ ++. ds ' \& ++. ds ` \& ++. ds ^ \& ++. ds , \& ++. ds ~ ~ ++. ds / ++.\} ++.if t \{\ ++. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" ++. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' ++. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' ++. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' ++. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' ++. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' ++.\} ++. \" troff and (daisy-wheel) nroff accents ++.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' ++.ds 8 \h'\*(#H'\(*b\h'-\*(#H' ++.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] ++.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' ++.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' ++.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] ++.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] ++.ds ae a\h'-(\w'a'u*4/10)'e ++.ds Ae A\h'-(\w'A'u*4/10)'E ++. \" corrections for vroff ++.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' ++.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' ++. \" for low resolution devices (crt and lpr) ++.if \n(.H>23 .if \n(.V>19 \ ++\{\ ++. ds : e ++. ds 8 ss ++. ds o a ++. ds d- d\h'-1'\(ga ++. ds D- D\h'-1'\(hy ++. ds th \o'bp' ++. ds Th \o'LP' ++. ds ae ae ++. ds Ae AE ++.\} ++.rm #[ #] #H #V #F C ++.\" ======================================================================== ++.\" ++.IX Title "NFSDCLD 8" ++.TH NFSDCLD 8 "2011-12-21" "" "" ++.\" For nroff, turn off justification. Always turn off hyphenation; it makes ++.\" way too many mistakes in technical documents. ++.if n .ad l ++.nh ++.SH "NAME" ++nfsdcld \- NFSv4 Client Tracking Daemon ++.SH "SYNOPSIS" ++.IX Header "SYNOPSIS" ++nfsdcld [\-d] [\-F] [\-p path] [\-s stable storage dir] ++.SH "DESCRIPTION" ++.IX Header "DESCRIPTION" ++nfsdcld is the NFSv4 client tracking daemon. It is not necessary to run ++this daemon on machines that are not acting as NFSv4 servers. ++.PP ++When a network partition is combined with a server reboot, there are ++edge conditions that can cause the server to grant lock reclaims when ++other clients have taken conflicting locks in the interim. A more detailed ++explanation of this issue is described in \s-1RFC\s0 3530, section 8.6.3. ++.PP ++In order to prevent these problems, the server must track a small amount ++of per-client information on stable storage. This daemon provides the ++userspace piece of that functionality. ++.SH "OPTIONS" ++.IX Header "OPTIONS" ++.IP "\fB\-d\fR, \fB\-\-debug\fR" 4 ++.IX Item "-d, --debug" ++Enable debug level logging. ++.IP "\fB\-F\fR, \fB\-\-foreground\fR" 4 ++.IX Item "-F, --foreground" ++Runs the daemon in the foreground and prints all output to stderr ++.IP "\fB\-p\fR \fIpath\fR, \fB\-\-pipefsdir\fR=\fIpath\fR" 4 ++.IX Item "-p path, --pipefsdir=path" ++Location of the rpc_pipefs filesystem. The default value is ++\&\fI/var/lib/nfs/rpc_pipefs\fR. ++.IP "\fB\-s\fR \fIstorage_dir\fR, \fB\-\-storagedir\fR=\fIstorage_dir\fR" 4 ++.IX Item "-s storagedir, --storagedir=storage_dir" ++Directory where stable storage information should be kept. The default ++value is \fI/var/lib/nfs/nfsdcld\fR. ++.SH "CONFIGURATION FILE" ++.IX Header "CONFIGURATION FILE" ++The following values are recognized in the \fB[nfsdcld]\fR section ++of the \fI/etc/nfs.conf\fR configuration file: ++.IP "\fBstoragedir\fR" 4 ++.IX Item "storagedir" ++Equivalent to \fB\-s\fR/\fB\-\-storagedir\fR. ++.IP "\fBdebug\fR" 4 ++.IX Item "debug" ++Setting "debug = 1" is equivalent to \fB\-d\fR/\fB\-\-debug\fR. ++.LP ++In addition, the following value is recognized from the \fB[general]\fR section: ++.IP "\fBpipefs\-directory\fR" 4 ++.IX Item "pipefs-directory" ++Equivalent to \fB\-p\fR/\fB\-\-pipefsdir\fR. ++.SH "NOTES" ++.IX Header "NOTES" ++The Linux kernel NFSv4 server has historically tracked this information ++on stable storage by manipulating information on the filesystem ++directly, in the directory to which \fI/proc/fs/nfsd/nfsv4recoverydir\fR ++points. ++.PP ++This changed with the original introduction of \fBnfsdcld\fR upcall in kernel version 3.4, ++which was later deprecated in favor of the \fBnfsdcltrack\fR(8) usermodehelper ++program, support for which was added in kernel version 3.8. However, since the ++usermodehelper upcall does not work in containers, support for a new version of ++the \fBnfsdcld\fR upcall was added in kernel version 5.2. ++.PP ++This daemon requires a kernel that supports the \fBnfsdcld\fR upcall. On older kernels, if ++the legacy client name tracking code was in use, then the kernel would not create the ++pipe that \fBnfsdcld\fR uses to talk to the kernel. On newer kernels, nfsd attempts to ++initialize client tracking in the following order: First, the \fBnfsdcld\fR upcall. Second, ++the \fBnfsdcltrack\fR usermodehelper upcall. Finally, the legacy client tracking. ++.PP ++This daemon should be run as root, as the pipe that it uses to communicate ++with the kernel is only accessable by root. The daemon however does drop all ++superuser capabilities after starting. Because of this, the \fIstoragedir\fR ++should be owned by root, and be readable and writable by owner. ++.PP ++The daemon now supports different upcall versions to allow the kernel to pass additional ++data to be stored in the on-disk database. The kernel will query the supported upcall ++version from \fBnfsdcld\fR during client tracking initialization. A restart of \fBnfsd\fR is ++not necessary after upgrading \fBnfsdcld\fR, however \fBnfsd\fR will not use a later upcall ++version until restart. A restart of \fBnfsd is necessary\fR after downgrading \fBnfsdcld\fR, ++to ensure that \fBnfsd\fR does not use an upcall version that \fBnfsdcld\fR does not support. ++Additionally, a downgrade of \fBnfsdcld\fR requires the schema of the on-disk database to ++be downgraded as well. That can be accomplished using the \fBclddb-tool\fR(8) utility. ++.SH FILES ++.TP ++.B /var/lib/nfs/nfsdcld/main.sqlite ++.SH SEE ALSO ++.BR nfsdcltrack "(8), " clddb-tool (8) ++.SH "AUTHORS" ++.IX Header "AUTHORS" ++The nfsdcld daemon was developed by Jeff Layton ++with modifications from Scott Mayhew . +diff --git a/utils/nfsdcld/sqlite.c b/utils/nfsdcld/sqlite.c +new file mode 100644 +index 00000000..6666c867 +--- /dev/null ++++ b/utils/nfsdcld/sqlite.c +@@ -0,0 +1,1406 @@ ++/* ++ * Copyright (C) 2011 Red Hat, Jeff Layton ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++/* ++ * Explanation: ++ * ++ * This file contains the code to manage the sqlite backend database for the ++ * nfsdcld client tracking daemon. ++ * ++ * The main database is called main.sqlite and contains the following tables: ++ * ++ * parameters: simple key/value pairs for storing database info ++ * ++ * grace: a "current" column containing an INTEGER representing the current ++ * epoch (where should new values be stored) and a "recovery" column ++ * containing an INTEGER representing the recovery epoch (from what ++ * epoch are we allowed to recover). A recovery epoch of 0 means ++ * normal operation (grace period not in force). Note: sqlite stores ++ * integers as signed values, so these must be cast to a uint64_t when ++ * retrieving them from the database and back to an int64_t when storing ++ * them in the database. ++ * ++ * rec-CCCCCCCCCCCCCCCC (where C is the hex representation of the epoch value): ++ * an "id" column containing a BLOB with the long-form clientid ++ * as sent by the client, and a "princhash" column containing a BLOB ++ * with the sha256 hash of the kerberos principal (if available). ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif /* HAVE_CONFIG_H */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xlog.h" ++#include "sqlite.h" ++#include "cld.h" ++#include "cld-internal.h" ++#include "conffile.h" ++#include "legacy.h" ++#include "nfslib.h" ++ ++#define CLD_SQLITE_LATEST_SCHEMA_VERSION 4 ++#define CLTRACK_DEFAULT_STORAGEDIR NFS_STATEDIR "/nfsdcltrack" ++ ++/* in milliseconds */ ++#define CLD_SQLITE_BUSY_TIMEOUT 10000 ++ ++/* private data structures */ ++ ++/* global variables */ ++static char *cltrack_storagedir = CLTRACK_DEFAULT_STORAGEDIR; ++ ++/* reusable pathname and sql command buffer */ ++static char buf[PATH_MAX]; ++ ++/* global database handle */ ++static sqlite3 *dbh; ++ ++/* forward declarations */ ++ ++/* make a directory, ignoring EEXIST errors unless it's not a directory */ ++static int ++mkdir_if_not_exist(const char *dirname) ++{ ++ int ret; ++ struct stat statbuf; ++ ++ ret = mkdir(dirname, S_IRWXU); ++ if (ret && errno != EEXIST) ++ return -errno; ++ ++ ret = stat(dirname, &statbuf); ++ if (ret) ++ return -errno; ++ ++ if (!S_ISDIR(statbuf.st_mode)) ++ ret = -ENOTDIR; ++ ++ return ret; ++} ++ ++static int ++sqlite_query_schema_version(void) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ /* prepare select query */ ++ ret = sqlite3_prepare_v2(dbh, ++ "SELECT value FROM parameters WHERE key == \"version\";", ++ -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(D_GENERAL, "Unable to prepare select statement: %s", ++ sqlite3_errmsg(dbh)); ++ ret = 0; ++ goto out; ++ } ++ ++ /* query schema version */ ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_ROW) { ++ xlog(D_GENERAL, "Select statement execution failed: %s", ++ sqlite3_errmsg(dbh)); ++ ret = 0; ++ goto out; ++ } ++ ++ ret = sqlite3_column_int(stmt, 0); ++out: ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++static int ++sqlite_query_first_time(int *first_time) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ /* prepare select query */ ++ ret = sqlite3_prepare_v2(dbh, ++ "SELECT value FROM parameters WHERE key == \"first_time\";", ++ -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(D_GENERAL, "Unable to prepare select statement: %s", ++ sqlite3_errmsg(dbh)); ++ goto out; ++ } ++ ++ /* query first_time */ ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_ROW) { ++ xlog(D_GENERAL, "Select statement execution failed: %s", ++ sqlite3_errmsg(dbh)); ++ goto out; ++ } ++ ++ *first_time = sqlite3_column_int(stmt, 0); ++ ret = 0; ++out: ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++static int ++sqlite_add_princ_col_cb(void *UNUSED(arg), int ncols, char **cols, ++ char **UNUSED(colnames)) ++{ ++ int ret; ++ char *err; ++ ++ if (ncols > 1) ++ return -EINVAL; ++ ret = snprintf(buf, sizeof(buf), "ALTER TABLE \"%s\" " ++ "ADD COLUMN princhash BLOB;", cols[0]); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return -EINVAL; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to add princhash column to table %s: %s", ++ cols[0], err); ++ goto out; ++ } ++ xlog(D_GENERAL, "Added princhash column to table %s", cols[0]); ++out: ++ sqlite3_free(err); ++ return ret; ++} ++ ++static int ++sqlite_maindb_update_v3_to_v4(void) ++{ ++ int ret; ++ char *err; ++ ++ ret = sqlite3_exec(dbh, "SELECT name FROM sqlite_master " ++ "WHERE type=\"table\" AND name LIKE \"%rec-%\";", ++ sqlite_add_princ_col_cb, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: Failed to update tables!: %s", __func__, err); ++ } ++ sqlite3_free(err); ++ return ret; ++} ++ ++static int ++sqlite_maindb_update_v1v2_to_v4(void) ++{ ++ int ret; ++ char *err; ++ ++ /* create grace table */ ++ ret = sqlite3_exec(dbh, "CREATE TABLE grace " ++ "(current INTEGER , recovery INTEGER);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create grace table: %s", err); ++ goto out; ++ } ++ ++ /* insert initial epochs into grace table */ ++ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO grace " ++ "values (1, 0);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to set initial epochs: %s", err); ++ goto out; ++ } ++ ++ /* create recovery table for current epoch */ ++ ret = sqlite3_exec(dbh, "CREATE TABLE \"rec-0000000000000001\" " ++ "(id BLOB PRIMARY KEY, princhash BLOB);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create recovery table " ++ "for current epoch: %s", err); ++ goto out; ++ } ++ ++ /* copy records from old clients table */ ++ ret = sqlite3_exec(dbh, "INSERT INTO \"rec-0000000000000001\" (id) " ++ "SELECT id FROM clients;", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to copy client records: %s", err); ++ goto out; ++ } ++ ++ /* drop the old clients table */ ++ ret = sqlite3_exec(dbh, "DROP TABLE clients;", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to drop old clients table: %s", err); ++ } ++out: ++ sqlite3_free(err); ++ return ret; ++} ++ ++static int ++sqlite_maindb_update_schema(int oldversion) ++{ ++ int ret, ret2; ++ char *err; ++ ++ /* begin transaction */ ++ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, ++ &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ goto rollback; ++ } ++ ++ /* ++ * Check schema version again. This time, under an exclusive ++ * transaction to guard against racing DB setup attempts ++ */ ++ ret = sqlite_query_schema_version(); ++ if (ret != oldversion) { ++ if (ret == CLD_SQLITE_LATEST_SCHEMA_VERSION) ++ /* Someone else raced in and set it up */ ++ ret = 0; ++ else ++ /* Something went wrong -- fail! */ ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ /* Still at old version -- do conversion */ ++ ++ switch (oldversion) { ++ case 3: ++ case 2: ++ ret = sqlite_maindb_update_v3_to_v4(); ++ break; ++ case 1: ++ ret = sqlite_maindb_update_v1v2_to_v4(); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (ret != SQLITE_OK) ++ goto rollback; ++ ++ ret = snprintf(buf, sizeof(buf), "UPDATE parameters SET value = %d " ++ "WHERE key = \"version\";", ++ CLD_SQLITE_LATEST_SCHEMA_VERSION); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to update schema version: %s", err); ++ goto rollback; ++ } ++ ++ ret = sqlite_query_first_time(&first_time); ++ if (ret != SQLITE_OK) { ++ /* insert first_time into parameters table */ ++ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO parameters " ++ "values (\"first_time\", \"1\");", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to insert into parameter table: %s", err); ++ goto rollback; ++ } ++ } ++ ++ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to commit transaction: %s", err); ++ goto rollback; ++ } ++out: ++ sqlite3_free(err); ++ return ret; ++rollback: ++ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); ++ if (ret2 != SQLITE_OK) ++ xlog(L_ERROR, "Unable to rollback transaction: %s", err); ++ goto out; ++} ++ ++/* ++ * Start an exclusive transaction and recheck the DB schema version. If it's ++ * still zero (indicating a new database) then set it up. If that all works, ++ * then insert schema version into the parameters table and commit the ++ * transaction. On any error, rollback the transaction. ++ */ ++static int ++sqlite_maindb_init_v4(void) ++{ ++ int ret, ret2; ++ char *err = NULL; ++ ++ /* Start a transaction */ ++ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, ++ &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ return ret; ++ } ++ ++ /* ++ * Check schema version again. This time, under an exclusive ++ * transaction to guard against racing DB setup attempts ++ */ ++ ret = sqlite_query_schema_version(); ++ switch (ret) { ++ case 0: ++ /* Query failed again -- set up DB */ ++ break; ++ case CLD_SQLITE_LATEST_SCHEMA_VERSION: ++ /* Someone else raced in and set it up */ ++ ret = 0; ++ goto rollback; ++ default: ++ /* Something went wrong -- fail! */ ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, "CREATE TABLE parameters " ++ "(key TEXT PRIMARY KEY, value TEXT);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create parameter table: %s", err); ++ goto rollback; ++ } ++ ++ /* create grace table */ ++ ret = sqlite3_exec(dbh, "CREATE TABLE grace " ++ "(current INTEGER , recovery INTEGER);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create grace table: %s", err); ++ goto rollback; ++ } ++ ++ /* insert initial epochs into grace table */ ++ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO grace " ++ "values (1, 0);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to set initial epochs: %s", err); ++ goto rollback; ++ } ++ ++ /* create recovery table for current epoch */ ++ ret = sqlite3_exec(dbh, "CREATE TABLE \"rec-0000000000000001\" " ++ "(id BLOB PRIMARY KEY, princhash BLOB);", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create recovery table " ++ "for current epoch: %s", err); ++ goto rollback; ++ } ++ ++ /* insert version into parameters table */ ++ ret = snprintf(buf, sizeof(buf), "INSERT OR FAIL INTO parameters " ++ "values (\"version\", \"%d\");", ++ CLD_SQLITE_LATEST_SCHEMA_VERSION); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to insert into parameter table: %s", err); ++ goto rollback; ++ } ++ ++ /* insert first_time into parameters table */ ++ ret = sqlite3_exec(dbh, "INSERT OR FAIL INTO parameters " ++ "values (\"first_time\", \"1\");", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to insert into parameter table: %s", err); ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to commit transaction: %s", err); ++ goto rollback; ++ } ++out: ++ sqlite3_free(err); ++ return ret; ++ ++rollback: ++ /* Attempt to rollback the transaction */ ++ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); ++ if (ret2 != SQLITE_OK) ++ xlog(L_ERROR, "Unable to rollback transaction: %s", err); ++ goto out; ++} ++ ++static int ++sqlite_startup_query_grace(void) ++{ ++ int ret; ++ uint64_t tcur; ++ uint64_t trec; ++ sqlite3_stmt *stmt = NULL; ++ ++ /* prepare select query */ ++ ret = sqlite3_prepare_v2(dbh, "SELECT * FROM grace;", -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(D_GENERAL, "Unable to prepare select statement: %s", ++ sqlite3_errmsg(dbh)); ++ goto out; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_ROW) { ++ xlog(D_GENERAL, "Select statement execution failed: %s", ++ sqlite3_errmsg(dbh)); ++ goto out; ++ } ++ ++ tcur = (uint64_t)sqlite3_column_int64(stmt, 0); ++ trec = (uint64_t)sqlite3_column_int64(stmt, 1); ++ ++ current_epoch = tcur; ++ recovery_epoch = trec; ++ ret = 0; ++ xlog(D_GENERAL, "%s: current_epoch=%"PRIu64" recovery_epoch=%"PRIu64, ++ __func__, current_epoch, recovery_epoch); ++out: ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++/* ++ * Helper for renaming a recovery table to fix the padding. ++ */ ++static int ++sqlite_fix_table_name(const char *name) ++{ ++ int ret; ++ uint64_t val; ++ char *err; ++ ++ if (sscanf(name, "rec-%" PRIx64, &val) != 1) ++ return -EINVAL; ++ ret = snprintf(buf, sizeof(buf), "ALTER TABLE \"%s\" " ++ "RENAME TO \"rec-%016" PRIx64 "\";", ++ name, val); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return -EINVAL; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to fix table for epoch %"PRIu64": %s", ++ val, err); ++ goto out; ++ } ++ xlog(D_GENERAL, "Renamed table %s to rec-%016" PRIx64, name, val); ++out: ++ sqlite3_free(err); ++ return ret; ++} ++ ++/* ++ * Callback for the sqlite_exec statement in sqlite_check_table_names. ++ * If the epoch encoded in the table name matches either the current ++ * epoch or the recovery epoch, then try to fix the padding. Otherwise, ++ * we bail. ++ */ ++static int ++sqlite_check_table_names_cb(void *UNUSED(arg), int ncols, char **cols, ++ char **UNUSED(colnames)) ++{ ++ int ret = SQLITE_OK; ++ uint64_t val; ++ ++ if (ncols > 1) ++ return -EINVAL; ++ if (sscanf(cols[0], "rec-%" PRIx64, &val) != 1) ++ return -EINVAL; ++ if (val == current_epoch || val == recovery_epoch) { ++ xlog(D_GENERAL, "found invalid table name %s for %s epoch", ++ cols[0], val == current_epoch ? "current" : "recovery"); ++ ret = sqlite_fix_table_name(cols[0]); ++ } else { ++ xlog(L_ERROR, "found invalid table name %s for unknown epoch %" ++ PRId64, cols[0], val); ++ return -EINVAL; ++ } ++ return ret; ++} ++ ++/* ++ * Look for recovery table names where the epoch isn't zero-padded ++ */ ++static int ++sqlite_check_table_names(void) ++{ ++ int ret; ++ char *err; ++ ++ ret = sqlite3_exec(dbh, "SELECT name FROM sqlite_master " ++ "WHERE type=\"table\" AND name LIKE \"%rec-%\" " ++ "AND length(name) < 20;", ++ sqlite_check_table_names_cb, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Table names check failed: %s", err); ++ } ++ sqlite3_free(err); ++ return ret; ++} ++ ++/* ++ * Simple db health check. For now we're just making sure that the recovery ++ * table names are of the format "rec-CCCCCCCCCCCCCCCC" (where C is the hex ++ * representation of the epoch value) and that epoch value matches either ++ * the current epoch or the recovery epoch. ++ */ ++static int ++sqlite_check_db_health(void) ++{ ++ int ret, ret2; ++ char *err; ++ ++ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, ++ &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ goto rollback; ++ } ++ ++ ret = sqlite_check_table_names(); ++ if (ret != SQLITE_OK) ++ goto rollback; ++ ++ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to commit transaction: %s", err); ++ goto rollback; ++ } ++ ++cleanup: ++ sqlite3_free(err); ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ return ret; ++rollback: ++ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); ++ if (ret2 != SQLITE_OK) ++ xlog(L_ERROR, "Unable to rollback transaction: %s", err); ++ goto cleanup; ++} ++ ++static int ++sqlite_attach_db(const char *path) ++{ ++ int ret; ++ char dbpath[PATH_MAX]; ++ struct stat stb; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = snprintf(dbpath, PATH_MAX - 1, "%s/main.sqlite", path); ++ if (ret < 0) ++ return ret; ++ ++ dbpath[PATH_MAX - 1] = '\0'; ++ ret = stat(dbpath, &stb); ++ if (ret < 0) ++ return ret; ++ ++ xlog(D_GENERAL, "attaching %s", dbpath); ++ ret = sqlite3_prepare_v2(dbh, "ATTACH DATABASE ? AS attached;", ++ -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: unable to prepare attach statement: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ ret = sqlite3_bind_text(stmt, 1, dbpath, strlen(dbpath), SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind text failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from attach: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ ++ sqlite3_finalize(stmt); ++ stmt = NULL; ++ return ret; ++} ++ ++static int ++sqlite_detach_db(void) ++{ ++ int ret; ++ char *err = NULL; ++ ++ xlog(D_GENERAL, "detaching database"); ++ ret = sqlite3_exec(dbh, "DETACH DATABASE attached;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to detach attached db: %s", err); ++ } ++ ++ sqlite3_free(err); ++ return ret; ++} ++ ++/* ++ * Copies client records from the nfsdcltrack database as part of a one-time ++ * "upgrade". ++ * ++ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0). ++ * Returns the number of records copied via "num_rec". ++ */ ++static int ++sqlite_copy_cltrack_records(int *num_rec) ++{ ++ int ret, ret2; ++ char *s; ++ char *err = NULL; ++ sqlite3_stmt *stmt = NULL; ++ ++ s = conf_get_str("nfsdcltrack", "storagedir"); ++ if (s) ++ cltrack_storagedir = s; ++ ret = sqlite_attach_db(cltrack_storagedir); ++ if (ret) ++ goto out; ++ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, ++ &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ goto rollback; ++ } ++ ret = snprintf(buf, sizeof(buf), "DELETE FROM \"rec-%016" PRIx64 "\";", ++ current_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to clear records from current epoch: %s", err); ++ goto rollback; ++ } ++ ret = snprintf(buf, sizeof(buf), "INSERT INTO \"rec-%016" PRIx64 "\" (id) " ++ "SELECT id FROM attached.clients;", ++ current_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: insert statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto rollback; ++ } ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_DONE) { ++ xlog(L_ERROR, "%s: unexpected return code from insert: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto rollback; ++ } ++ *num_rec = sqlite3_changes(dbh); ++ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to commit transaction: %s", err); ++ goto rollback; ++ } ++cleanup: ++ sqlite3_finalize(stmt); ++ sqlite3_free(err); ++ sqlite_detach_db(); ++out: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ return ret; ++rollback: ++ *num_rec = 0; ++ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); ++ if (ret2 != SQLITE_OK) ++ xlog(L_ERROR, "Unable to rollback transaction: %s", err); ++ goto cleanup; ++} ++ ++/* Open the database and set up the database handle for it */ ++int ++sqlite_prepare_dbh(const char *topdir) ++{ ++ int ret; ++ ++ /* Do nothing if the database handle is already set up */ ++ if (dbh) ++ return 0; ++ ++ ret = snprintf(buf, PATH_MAX - 1, "%s/main.sqlite", topdir); ++ if (ret < 0) ++ return ret; ++ ++ buf[PATH_MAX - 1] = '\0'; ++ ++ /* open a new DB handle */ ++ ret = sqlite3_open(buf, &dbh); ++ if (ret != SQLITE_OK) { ++ /* try to create the dir */ ++ ret = mkdir_if_not_exist(topdir); ++ if (ret) ++ goto out_close; ++ ++ /* retry open */ ++ ret = sqlite3_open(buf, &dbh); ++ if (ret != SQLITE_OK) ++ goto out_close; ++ } ++ ++ /* set busy timeout */ ++ ret = sqlite3_busy_timeout(dbh, CLD_SQLITE_BUSY_TIMEOUT); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to set sqlite busy timeout: %s", ++ sqlite3_errmsg(dbh)); ++ goto out_close; ++ } ++ ++ ret = sqlite_query_schema_version(); ++ switch (ret) { ++ case CLD_SQLITE_LATEST_SCHEMA_VERSION: ++ /* DB is already set up. Do nothing */ ++ ret = 0; ++ break; ++ case 3: ++ /* Old DB -- update to new schema */ ++ ret = sqlite_maindb_update_schema(3); ++ if (ret) ++ goto out_close; ++ break; ++ case 2: ++ /* Old DB -- update to new schema */ ++ ret = sqlite_maindb_update_schema(2); ++ if (ret) ++ goto out_close; ++ break; ++ ++ case 1: ++ /* Old DB -- update to new schema */ ++ ret = sqlite_maindb_update_schema(1); ++ if (ret) ++ goto out_close; ++ break; ++ case 0: ++ /* Query failed -- try to set up new DB */ ++ ret = sqlite_maindb_init_v4(); ++ if (ret) ++ goto out_close; ++ break; ++ default: ++ /* Unknown DB version -- downgrade? Fail */ ++ xlog(L_ERROR, "Unsupported database schema version! " ++ "Expected %d, got %d.", ++ CLD_SQLITE_LATEST_SCHEMA_VERSION, ret); ++ ret = -EINVAL; ++ goto out_close; ++ } ++ ++ ret = sqlite_startup_query_grace(); ++ ++ ret = sqlite_query_first_time(&first_time); ++ if (ret) ++ goto out_close; ++ ++ ret = sqlite_check_db_health(); ++ if (ret) { ++ xlog(L_ERROR, "Database health check failed! " ++ "Database must be fixed manually."); ++ goto out_close; ++ } ++ ++ /* one-time "upgrade" from older client tracking methods */ ++ if (first_time) { ++ sqlite_copy_cltrack_records(&num_cltrack_records); ++ xlog(D_GENERAL, "%s: num_cltrack_records = %d\n", ++ __func__, num_cltrack_records); ++ legacy_load_clients_from_recdir(&num_legacy_records); ++ xlog(D_GENERAL, "%s: num_legacy_records = %d\n", ++ __func__, num_legacy_records); ++ if (num_cltrack_records > 0 && num_legacy_records > 0) ++ xlog(L_WARNING, "%s: first-time upgrade detected " ++ "both cltrack and legacy records!\n", __func__); ++ } ++ ++ return ret; ++out_close: ++ sqlite3_close(dbh); ++ dbh = NULL; ++ return ret; ++} ++ ++/* ++ * Create a client record ++ * ++ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0) ++ */ ++int ++sqlite_insert_client(const unsigned char *clname, const size_t namelen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = snprintf(buf, sizeof(buf), "INSERT OR REPLACE INTO \"rec-%016" PRIx64 "\" (id) " ++ "VALUES (?);", current_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return ret; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ++ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: insert statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", __func__, ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from insert: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++#if UPCALL_VERSION >= 2 ++/* ++ * Create a client record including hash the kerberos principal ++ * ++ * Returns a non-zero sqlite error code, or SQLITE_OK (aka 0) ++ */ ++int ++sqlite_insert_client_and_princhash(const unsigned char *clname, const size_t namelen, ++ const unsigned char *clprinchash, const size_t princhashlen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ if (princhashlen > 0) ++ ret = snprintf(buf, sizeof(buf), "INSERT OR REPLACE INTO \"rec-%016" PRIx64 "\" " ++ "VALUES (?, ?);", current_epoch); ++ else ++ ret = snprintf(buf, sizeof(buf), "INSERT OR REPLACE INTO \"rec-%016" PRIx64 "\" (id) " ++ "VALUES (?);", current_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return ret; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ++ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: insert statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", __func__, ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ if (princhashlen > 0) { ++ ret = sqlite3_bind_blob(stmt, 2, (const void *)clprinchash, princhashlen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", __func__, ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from insert: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++#else ++int ++sqlite_insert_client_and_princhash(const unsigned char *clname, const size_t namelen, ++ const unsigned char *clprinchash, const size_t princhashlen) ++{ ++ return -EINVAL; ++} ++#endif ++ ++/* Remove a client record */ ++int ++sqlite_remove_client(const unsigned char *clname, const size_t namelen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = snprintf(buf, sizeof(buf), "DELETE FROM \"rec-%016" PRIx64 "\" " ++ "WHERE id==?;", current_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return ret; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ++ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL); ++ ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", __func__, ++ sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret == SQLITE_DONE) ++ ret = SQLITE_OK; ++ else ++ xlog(L_ERROR, "%s: unexpected return code from delete: %d", ++ __func__, ret); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++/* ++ * Is the given clname in the clients table? If so, then update its timestamp ++ * and return success. If the record isn't present, or the update fails, then ++ * return an error. ++ */ ++int ++sqlite_check_client(const unsigned char *clname, const size_t namelen) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++ ++ ret = snprintf(buf, sizeof(buf), "SELECT count(*) FROM \"rec-%016" PRIx64 "\" " ++ "WHERE id==?;", recovery_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return ret; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ++ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: select statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ ret = sqlite3_bind_blob(stmt, 1, (const void *)clname, namelen, ++ SQLITE_STATIC); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: bind blob failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ goto out_err; ++ } ++ ++ ret = sqlite3_step(stmt); ++ if (ret != SQLITE_ROW) { ++ xlog(L_ERROR, "%s: unexpected return code from select: %d", ++ __func__, ret); ++ goto out_err; ++ } ++ ++ ret = sqlite3_column_int(stmt, 0); ++ xlog(D_GENERAL, "%s: select returned %d rows", __func__, ret); ++ if (ret != 1) { ++ ret = -EACCES; ++ goto out_err; ++ } ++ ++ sqlite3_finalize(stmt); ++ ++ /* Now insert the client into the table for the current epoch */ ++ return sqlite_insert_client(clname, namelen); ++ ++out_err: ++ xlog(D_GENERAL, "%s: returning %d", __func__, ret); ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++int ++sqlite_grace_start(void) ++{ ++ int ret, ret2; ++ char *err; ++ uint64_t tcur = current_epoch; ++ uint64_t trec = recovery_epoch; ++ ++ /* begin transaction */ ++ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, ++ &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ goto rollback; ++ } ++ ++ if (trec == 0) { ++ /* ++ * A normal grace start - update the epoch values in the grace ++ * table and create a new table for the current reboot epoch. ++ */ ++ trec = tcur; ++ tcur++; ++ ++ ret = snprintf(buf, sizeof(buf), "UPDATE grace " ++ "SET current = %" PRId64 ", recovery = %" PRId64 ";", ++ (int64_t)tcur, (int64_t)trec); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ++ ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to update epochs: %s", err); ++ goto rollback; ++ } ++ ++ ret = snprintf(buf, sizeof(buf), "CREATE TABLE \"rec-%016" PRIx64 "\" " ++ "(id BLOB PRIMARY KEY, princhash blob);", ++ tcur); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ++ ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to create table for current epoch: %s", ++ err); ++ goto rollback; ++ } ++ } else { ++ /* Server restarted while in grace - don't update the epoch ++ * values in the grace table, just clear out the records for ++ * the current reboot epoch. ++ */ ++ ret = snprintf(buf, sizeof(buf), "DELETE FROM \"rec-%016" PRIx64 "\";", ++ tcur); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to clear table for current epoch: %s", ++ err); ++ goto rollback; ++ } ++ } ++ ++ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to commit transaction: %s", err); ++ goto rollback; ++ } ++ ++ current_epoch = tcur; ++ recovery_epoch = trec; ++ xlog(D_GENERAL, "%s: current_epoch=%"PRIu64" recovery_epoch=%"PRIu64, ++ __func__, current_epoch, recovery_epoch); ++ ++out: ++ sqlite3_free(err); ++ return ret; ++rollback: ++ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); ++ if (ret2 != SQLITE_OK) ++ xlog(L_ERROR, "Unable to rollback transaction: %s", err); ++ goto out; ++} ++ ++int ++sqlite_grace_done(void) ++{ ++ int ret, ret2; ++ char *err; ++ ++ /* begin transaction */ ++ ret = sqlite3_exec(dbh, "BEGIN EXCLUSIVE TRANSACTION;", NULL, NULL, ++ &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to begin transaction: %s", err); ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, "UPDATE grace SET recovery = \"0\";", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to clear recovery epoch: %s", err); ++ goto rollback; ++ } ++ ++ ret = snprintf(buf, sizeof(buf), "DROP TABLE \"rec-%016" PRIx64 "\";", ++ recovery_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ goto rollback; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ ret = -EINVAL; ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, (const char *)buf, NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to drop table for recovery epoch: %s", ++ err); ++ goto rollback; ++ } ++ ++ ret = sqlite3_exec(dbh, "COMMIT TRANSACTION;", NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to commit transaction: %s", err); ++ goto rollback; ++ } ++ ++ recovery_epoch = 0; ++ xlog(D_GENERAL, "%s: current_epoch=%"PRIu64" recovery_epoch=%"PRIu64, ++ __func__, current_epoch, recovery_epoch); ++ ++out: ++ sqlite3_free(err); ++ return ret; ++rollback: ++ ret2 = sqlite3_exec(dbh, "ROLLBACK TRANSACTION;", NULL, NULL, &err); ++ if (ret2 != SQLITE_OK) ++ xlog(L_ERROR, "Unable to rollback transaction: %s", err); ++ goto out; ++} ++ ++ ++int ++sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_client *clnt) ++{ ++ int ret; ++ sqlite3_stmt *stmt = NULL; ++#if UPCALL_VERSION >= 2 ++ struct cld_msg_v2 *cmsg = &clnt->cl_u.cl_msg_v2; ++#else ++ struct cld_msg *cmsg = &clnt->cl_u.cl_msg; ++#endif ++ ++ if (recovery_epoch == 0) { ++ xlog(D_GENERAL, "%s: not in grace!", __func__); ++ return -EINVAL; ++ } ++ ++ ret = snprintf(buf, sizeof(buf), "SELECT * FROM \"rec-%016" PRIx64 "\";", ++ recovery_epoch); ++ if (ret < 0) { ++ xlog(L_ERROR, "sprintf failed!"); ++ return ret; ++ } else if ((size_t)ret >= sizeof(buf)) { ++ xlog(L_ERROR, "sprintf output too long! (%d chars)", ret); ++ return -EINVAL; ++ } ++ ++ ret = sqlite3_prepare_v2(dbh, buf, -1, &stmt, NULL); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "%s: select statement prepare failed: %s", ++ __func__, sqlite3_errmsg(dbh)); ++ return ret; ++ } ++ ++ while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) { ++ memset(&cmsg->cm_u, 0, sizeof(cmsg->cm_u)); ++#if UPCALL_VERSION >= 2 ++ memcpy(&cmsg->cm_u.cm_clntinfo.cc_name.cn_id, ++ sqlite3_column_blob(stmt, 0), NFS4_OPAQUE_LIMIT); ++ cmsg->cm_u.cm_clntinfo.cc_name.cn_len = sqlite3_column_bytes(stmt, 0); ++ if (sqlite3_column_bytes(stmt, 1) > 0) { ++ memcpy(&cmsg->cm_u.cm_clntinfo.cc_princhash.cp_data, ++ sqlite3_column_blob(stmt, 1), SHA256_DIGEST_SIZE); ++ cmsg->cm_u.cm_clntinfo.cc_princhash.cp_len = sqlite3_column_bytes(stmt, 1); ++ } ++#else ++ memcpy(&cmsg->cm_u.cm_name.cn_id, sqlite3_column_blob(stmt, 0), ++ NFS4_OPAQUE_LIMIT); ++ cmsg->cm_u.cm_name.cn_len = sqlite3_column_bytes(stmt, 0); ++#endif ++ cb(clnt); ++ } ++ if (ret == SQLITE_DONE) ++ ret = 0; ++ sqlite3_finalize(stmt); ++ return ret; ++} ++ ++/* ++ * Cleans out the old nfsdcltrack database. ++ * ++ * Called upon receipt of the first "GraceDone" upcall only. ++ */ ++int ++sqlite_delete_cltrack_records(void) ++{ ++ int ret; ++ char *s; ++ char *err = NULL; ++ ++ s = conf_get_str("nfsdcltrack", "storagedir"); ++ if (s) ++ cltrack_storagedir = s; ++ ret = sqlite_attach_db(cltrack_storagedir); ++ if (ret) ++ goto out; ++ ret = sqlite3_exec(dbh, "DELETE FROM attached.clients;", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) { ++ xlog(L_ERROR, "Unable to clear records from cltrack db: %s", ++ err); ++ } ++ sqlite_detach_db(); ++out: ++ sqlite3_free(err); ++ return ret; ++} ++ ++/* ++ * Sets first_time to 0 in the parameters table to ensure we only ++ * copy old client tracking records into the database one time. ++ * ++ * Called upon receipt of the first "GraceDone" upcall only. ++ */ ++int ++sqlite_first_time_done(void) ++{ ++ int ret; ++ char *err = NULL; ++ ++ ret = sqlite3_exec(dbh, "UPDATE parameters SET value = \"0\" " ++ "WHERE key = \"first_time\";", ++ NULL, NULL, &err); ++ if (ret != SQLITE_OK) ++ xlog(L_ERROR, "Unable to clear first_time: %s", err); ++ ++ sqlite3_free(err); ++ return ret; ++} +diff --git a/utils/nfsdcld/sqlite.h b/utils/nfsdcld/sqlite.h +new file mode 100644 +index 00000000..0a26ad67 +--- /dev/null ++++ b/utils/nfsdcld/sqlite.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (C) 2011 Red Hat, Jeff Layton ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _SQLITE_H_ ++#define _SQLITE_H_ ++ ++struct cld_client; ++ ++int sqlite_prepare_dbh(const char *topdir); ++int sqlite_insert_client(const unsigned char *clname, const size_t namelen); ++int sqlite_insert_client_and_princhash(const unsigned char *clname, const size_t namelen, ++ const unsigned char *clprinchash, const size_t princhashlen); ++int sqlite_remove_client(const unsigned char *clname, const size_t namelen); ++int sqlite_check_client(const unsigned char *clname, const size_t namelen); ++int sqlite_grace_start(void); ++int sqlite_grace_done(void); ++int sqlite_iterate_recovery(int (*cb)(struct cld_client *clnt), struct cld_client *clnt); ++int sqlite_delete_cltrack_records(void); ++int sqlite_first_time_done(void); ++ ++#endif /* _SQLITE_H */ +diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c +index d3967a3a..4d219ef5 100644 +--- a/utils/nfsidmap/nfsidmap.c ++++ b/utils/nfsidmap/nfsidmap.c +@@ -18,7 +18,7 @@ + #include "xcommon.h" + + int verbose = 0; +-char *usage = "Usage: %s [-vh] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]"; ++#define USAGE "Usage: %s [-vh] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]" + + #define MAX_ID_LEN 11 + #define IDMAP_NAMESZ 128 +@@ -401,7 +401,7 @@ int main(int argc, char **argv) + break; + case 'h': + default: +- xlog_warn(usage, progname); ++ xlog_warn(USAGE, progname); + exit(opt == 'h' ? 0 : 1); + } + } +@@ -433,7 +433,7 @@ int main(int argc, char **argv) + xlog_stderr(verbose); + if ((argc - optind) != 2) { + xlog_warn("Bad arg count. Check /etc/request-key.conf"); +- xlog_warn(usage, progname); ++ xlog_warn(USAGE, progname); + return EXIT_FAILURE; + } + +@@ -451,7 +451,7 @@ int main(int argc, char **argv) + return EXIT_FAILURE; + } + if (verbose) { +- xlog_warn("key: 0x%lx type: %s value: %s timeout %ld", ++ xlog_warn("key: 0x%x type: %s value: %s timeout %d", + key, type, value, timeout); + } + +diff --git a/utils/statd/rmtcall.c b/utils/statd/rmtcall.c +index c4f6364f..5b261480 100644 +--- a/utils/statd/rmtcall.c ++++ b/utils/statd/rmtcall.c +@@ -247,7 +247,7 @@ process_reply(FD_SET_TYPE *rfds) + xlog_warn("%s: service %d not registered on localhost", + __func__, NL_MY_PROG(lp)); + } else { +- xlog(D_GENERAL, "%s: Callback to %s (for %d) succeeded", ++ xlog(D_GENERAL, "%s: Callback to %s (for %s) succeeded", + __func__, NL_MY_NAME(lp), NL_MON_NAME(lp)); + } + nlist_free(¬ify, lp); +diff --git a/utils/statd/statd.c b/utils/statd/statd.c +index 14673800..8eef2ff2 100644 +--- a/utils/statd/statd.c ++++ b/utils/statd/statd.c +@@ -136,7 +136,7 @@ static void log_modes(void) + strcat(buf, "TI-RPC "); + #endif + +- xlog_warn(buf); ++ xlog_warn("%s", buf); + } + + /* +diff --git a/utils/statd/svc_run.c b/utils/statd/svc_run.c +index d1dbd74a..e343c768 100644 +--- a/utils/statd/svc_run.c ++++ b/utils/statd/svc_run.c +@@ -53,6 +53,7 @@ + + #include + #include ++#include + #include "statd.h" + #include "notlist.h" + +@@ -104,8 +105,8 @@ my_svc_run(int sockfd) + + tv.tv_sec = NL_WHEN(notify) - now; + tv.tv_usec = 0; +- xlog(D_GENERAL, "Waiting for reply... (timeo %d)", +- tv.tv_sec); ++ xlog(D_GENERAL, "Waiting for reply... (timeo %jd)", ++ (intmax_t)tv.tv_sec); + selret = select(FD_SETSIZE, &readfds, + (void *) 0, (void *) 0, &tv); + } else { diff --git a/nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch b/nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch new file mode 100644 index 0000000..8d7991a --- /dev/null +++ b/nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch @@ -0,0 +1,29 @@ +commit 3e81185037cf97990e4598218f56d92dd70d6269 +Author: NeilBrown +Date: Tue Oct 20 13:19:10 2020 -0400 + + clddb-tool was recently renamed to nfsdclddb. + Unfortunately the nfsdcld man page wasn't told. + + Signed-off-by: NeilBrown + Signed-off-by: Steve Dickson + +diff --git a/utils/nfsdcld/nfsdcld.man b/utils/nfsdcld/nfsdcld.man +index 4c2b1e80..861f1c49 100644 +--- a/utils/nfsdcld/nfsdcld.man ++++ b/utils/nfsdcld/nfsdcld.man +@@ -209,12 +209,12 @@ not necessary after upgrading \fBnfsdcld\fR, however \fBnfsd\fR will not use a l + version until restart. A restart of \fBnfsd is necessary\fR after downgrading \fBnfsdcld\fR, + to ensure that \fBnfsd\fR does not use an upcall version that \fBnfsdcld\fR does not support. + Additionally, a downgrade of \fBnfsdcld\fR requires the schema of the on-disk database to +-be downgraded as well. That can be accomplished using the \fBclddb-tool\fR(8) utility. ++be downgraded as well. That can be accomplished using the \fBnfsdclddb\fR(8) utility. + .SH FILES + .TP + .B /var/lib/nfs/nfsdcld/main.sqlite + .SH SEE ALSO +-.BR nfsdcltrack "(8), " clddb-tool (8) ++.BR nfsdcltrack "(8), " nfsdclddb (8) + .SH "AUTHORS" + .IX Header "AUTHORS" + The nfsdcld daemon was developed by Jeff Layton diff --git a/nfs-utils-2.3.3-nfsdclddb-rename.patch b/nfs-utils-2.3.3-nfsdclddb-rename.patch new file mode 100644 index 0000000..6d47391 --- /dev/null +++ b/nfs-utils-2.3.3-nfsdclddb-rename.patch @@ -0,0 +1,130 @@ +commit 77d053e4881664e7dbbc3bbb9a242af005598e95 +Author: Steve Dickson +Date: Wed May 13 12:22:41 2020 -0400 + + nfsdclddb: Redname clddb-tool to nfsdclddb + + To try to maintain some type of name convention + rename clddb-tool to nfsdclddb + + Signed-off-by: Steve Dickson + +diff --git a/configure.ac b/configure.ac +index df88e58..0b1c8cc 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -695,7 +695,7 @@ AC_CONFIG_FILES([ + tools/mountstats/Makefile + tools/nfs-iostat/Makefile + tools/nfsconf/Makefile +- tools/clddb-tool/Makefile ++ tools/nfsdclddb/Makefile + utils/Makefile + utils/blkmapd/Makefile + utils/nfsdcld/Makefile +diff --git a/tools/Makefile.am b/tools/Makefile.am +index 53e6117..432d35d 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -9,7 +9,7 @@ endif + OPTDIRS += nfsconf + + if CONFIG_NFSDCLD +-OPTDIRS += clddb-tool ++OPTDIRS += nfsdclddb + endif + + SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat $(OPTDIRS) +diff --git a/tools/clddb-tool/Makefile.am b/tools/nfsdclddb/Makefile.am +similarity index 60% +rename from tools/clddb-tool/Makefile.am +rename to tools/nfsdclddb/Makefile.am +index 15a8fd4..18263fb 100644 +--- a/tools/clddb-tool/Makefile.am ++++ b/tools/nfsdclddb/Makefile.am +@@ -1,13 +1,13 @@ + ## Process this file with automake to produce Makefile.in +-PYTHON_FILES = clddb-tool.py ++PYTHON_FILES = nfsdclddb.py + +-man8_MANS = clddb-tool.man ++man8_MANS = nfsdclddb.man + + EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) + + all-local: $(PYTHON_FILES) + + install-data-hook: +- $(INSTALL) -m 755 clddb-tool.py $(DESTDIR)$(sbindir)/clddb-tool ++ $(INSTALL) -m 755 nfsdclddb.py $(DESTDIR)$(sbindir)/nfsdclddb + + MAINTAINERCLEANFILES=Makefile.in +diff --git a/tools/clddb-tool/clddb-tool.man b/tools/nfsdclddb/nfsdclddb.man +similarity index 84% +rename from tools/clddb-tool/clddb-tool.man +rename to tools/nfsdclddb/nfsdclddb.man +index e80b2c0..8ec7b18 100644 +--- a/tools/clddb-tool/clddb-tool.man ++++ b/tools/nfsdclddb/nfsdclddb.man +@@ -1,20 +1,20 @@ + .\" +-.\" clddb-tool(8) ++.\" nfsdclddb(8) + .\" +-.TH clddb-tool 8 "07 Aug 2019" ++.TH nfsdclddb 8 "07 Aug 2019" + .SH NAME +-clddb-tool \- Tool for manipulating the nfsdcld sqlite database ++nfsdclddb \- Tool for manipulating the nfsdcld sqlite database + .SH SYNOPSIS +-.B clddb-tool ++.B nfsdclddb + .RB [ \-h | \-\-help ] + .P +-.B clddb-tool ++.B nfsdclddb + .RB [ \-p | \-\-path + .IR dbpath ] + .B fix-table-names + .RB [ \-h | \-\-help ] + .P +-.B clddb-tool ++.B nfsdclddb + .RB [ \-p | \-\-path + .IR dbpath ] + .B downgrade-schema +@@ -22,7 +22,7 @@ clddb-tool \- Tool for manipulating the nfsdcld sqlite database + .RB [ \-v | \-\-version + .IR to-version ] + .P +-.B clddb-tool ++.B nfsdclddb + .RB [ \-p | \-\-path + .IR dbpath ] + .B print +@@ -31,10 +31,10 @@ clddb-tool \- Tool for manipulating the nfsdcld sqlite database + .P + + .SH DESCRIPTION +-.RB "The " clddb-tool " command is provided to perform some manipulation of the nfsdcld sqlite database schema and to print the contents of the database." ++.RB "The " nfsdclddb " command is provided to perform some manipulation of the nfsdcld sqlite database schema and to print the contents of the database." + .SS Sub-commands + Valid +-.B clddb-tool ++.B nfsdclddb + subcommands are: + .IP "\fBfix-table-names\fP" + .RB "A previous version of " nfsdcld "(8) contained a bug that corrupted the reboot epoch table names. This sub-command will fix those table names." +@@ -66,7 +66,7 @@ The schema version to downgrade to. Currently the schema can only be downgraded + Do not list the clients in the reboot epoch tables in the output. + .SH NOTES + The +-.B clddb-tool ++.B nfsdclddb + command will not allow the + .B fix-table-names + or +diff --git a/tools/clddb-tool/clddb-tool.py b/tools/nfsdclddb/nfsdclddb.py +similarity index 100% +rename from tools/clddb-tool/clddb-tool.py +rename to tools/nfsdclddb/nfsdclddb.py diff --git a/nfs-utils-2.3.3-nfsidmap-debug.patch b/nfs-utils-2.3.3-nfsidmap-debug.patch new file mode 100644 index 0000000..d972843 --- /dev/null +++ b/nfs-utils-2.3.3-nfsidmap-debug.patch @@ -0,0 +1,27 @@ +commit 0095435db8228d5a88ec35a63cb64271e2e648a8 +Author: Steve Dickson +Date: Thu Dec 19 12:48:31 2019 -0500 + + libnfsidmap: Turn off default verbosity + + Commit f080188e changed the library's verbosity + to be on by default. The patch turns it off by + default + + Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1774787 + + Signed-off-by: Steve Dickson + +diff --git a/support/nfsidmap/libnfsidmap.c b/support/nfsidmap/libnfsidmap.c +index 9299e652..d11710f1 100644 +--- a/support/nfsidmap/libnfsidmap.c ++++ b/support/nfsidmap/libnfsidmap.c +@@ -101,7 +101,7 @@ static void default_logger(const char *fmt, ...) + + #pragma GCC visibility pop + nfs4_idmap_log_function_t idmap_log_func = default_logger; +-int idmap_verbosity = 2; ++int idmap_verbosity = 0; + #pragma GCC visibility push(hidden) + + static int id_as_chars(char *name, uid_t *id) diff --git a/nfs-utils-2.3.3-nfsiostat-div-zero.patch b/nfs-utils-2.3.3-nfsiostat-div-zero.patch new file mode 100644 index 0000000..8e6d1b3 --- /dev/null +++ b/nfs-utils-2.3.3-nfsiostat-div-zero.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py +--- nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig 2020-12-10 10:38:26.462195326 -0500 ++++ nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py 2020-12-10 10:45:47.210671473 -0500 +@@ -380,6 +380,8 @@ class DeviceData: + sends = float(self.__rpc_data['rpcsends']) + if sample_time == 0: + sample_time = float(self.__nfs_data['age']) ++ if sample_time == 0: ++ sample_time = 1; + return (sends / sample_time) + + def display_iostats(self, sample_time, which): diff --git a/nfs-utils-2.3.3-nfsiostat-err-cnts.patch b/nfs-utils-2.3.3-nfsiostat-err-cnts.patch new file mode 100644 index 0000000..5fadb28 --- /dev/null +++ b/nfs-utils-2.3.3-nfsiostat-err-cnts.patch @@ -0,0 +1,159 @@ +diff --git a/tools/mountstats/mountstats.py b/tools/mountstats/mountstats.py +index 48ef0964..bda9af67 100755 +--- a/tools/mountstats/mountstats.py ++++ b/tools/mountstats/mountstats.py +@@ -308,6 +308,8 @@ class DeviceData: + op = words[0][:-1] + self.__rpc_data['ops'] += [op] + self.__rpc_data[op] = [int(word) for word in words[1:]] ++ if len(self.__rpc_data[op]) < 9: ++ self.__rpc_data[op] += [0] + + def parse_stats(self, lines): + """Turn a list of lines from a mount stat file into a +@@ -475,7 +477,9 @@ class DeviceData: + retrans = stats[2] - count + if retrans != 0: + print('\t%d retrans (%d%%)' % (retrans, ((retrans * 100) / count)), end=' ') +- print('\t%d major timeouts' % stats[3]) ++ print('\t%d major timeouts' % stats[3], end='') ++ if len(stats) >= 10 and stats[9] != 0: ++ print('\t%d errors (%d%%)' % (stats[9], ((stats[9] * 100) / count))) + else: + print('') + print('\tavg bytes sent per op: %d\tavg bytes received per op: %d' % \ +@@ -580,7 +584,7 @@ class DeviceData: + self.__nfs_data['fstype'] = 'nfs4' + self.__rpc_data['ops'] = ops + for op in ops: +- self.__rpc_data[op] = [0 for i in range(8)] ++ self.__rpc_data[op] = [0 for i in range(9)] + + def accumulate_iostats(self, new_stats): + """Accumulate counters from all RPC op buckets in new_stats. This is +@@ -605,6 +609,8 @@ class DeviceData: + queued_for = float(rpc_stats[5]) + rtt = float(rpc_stats[6]) + exe = float(rpc_stats[7]) ++ if len(rpc_stats) >= 9: ++ errs = int(rpc_stats[8]) + + # prevent floating point exceptions + if ops != 0: +@@ -613,12 +619,15 @@ class DeviceData: + rtt_per_op = rtt / ops + exe_per_op = exe / ops + queued_for_per_op = queued_for / ops ++ if len(rpc_stats) >= 9: ++ errs_percent = (errs * 100) / ops + else: + kb_per_op = 0.0 + retrans_percent = 0.0 + rtt_per_op = 0.0 + exe_per_op = 0.0 + queued_for_per_op = 0.0 ++ errs_percent = 0.0 + + op += ':' + print(format(op.lower(), '<16s'), end='') +@@ -628,7 +637,10 @@ class DeviceData: + print(format('retrans', '>16s'), end='') + print(format('avg RTT (ms)', '>16s'), end='') + print(format('avg exe (ms)', '>16s'), end='') +- print(format('avg queue (ms)', '>16s')) ++ print(format('avg queue (ms)', '>16s'), end='') ++ if len(rpc_stats) >= 9: ++ print(format('errors', '>16s'), end='') ++ print() + + print(format((ops / sample_time), '>24.3f'), end='') + print(format((kilobytes / sample_time), '>16.3f'), end='') +@@ -637,7 +649,11 @@ class DeviceData: + print(format(retransmits, '>16'), end='') + print(format(rtt_per_op, '>16.3f'), end='') + print(format(exe_per_op, '>16.3f'), end='') +- print(format(queued_for_per_op, '>16.3f')) ++ print(format(queued_for_per_op, '>16.3f'), end='') ++ if len(rpc_stats) >= 9: ++ errors = '{0:>10.0f} ({1:>3.1f}%)'.format(errs, errs_percent).strip() ++ print(format(errors, '>16'), end='') ++ print() + + def display_iostats(self, sample_time): + """Display NFS and RPC stats in an iostat-like way +diff --git a/tools/nfs-iostat/nfs-iostat.py b/tools/nfs-iostat/nfs-iostat.py +old mode 100644 +new mode 100755 +index f1556fb7..5b2260ad +--- a/tools/nfs-iostat/nfs-iostat.py ++++ b/tools/nfs-iostat/nfs-iostat.py +@@ -329,6 +329,8 @@ class DeviceData: + queued_for = float(rpc_stats[5]) + rtt = float(rpc_stats[6]) + exe = float(rpc_stats[7]) ++ if len(rpc_stats) >= 9: ++ errs = float(rpc_stats[8]) + + # prevent floating point exceptions + if ops != 0: +@@ -337,12 +339,16 @@ class DeviceData: + rtt_per_op = rtt / ops + exe_per_op = exe / ops + queued_for_per_op = queued_for / ops ++ if len(rpc_stats) >= 9: ++ errs_percent = (errs * 100) / ops + else: + kb_per_op = 0.0 + retrans_percent = 0.0 + rtt_per_op = 0.0 + exe_per_op = 0.0 + queued_for_per_op = 0.0 ++ if len(rpc_stats) >= 9: ++ errs_percent = 0.0 + + op += ':' + print(format(op.lower(), '<16s'), end='') +@@ -352,7 +358,10 @@ class DeviceData: + print(format('retrans', '>16s'), end='') + print(format('avg RTT (ms)', '>16s'), end='') + print(format('avg exe (ms)', '>16s'), end='') +- print(format('avg queue (ms)', '>16s')) ++ print(format('avg queue (ms)', '>16s'), end='') ++ if len(rpc_stats) >= 9: ++ print(format('errors', '>16s'), end='') ++ print() + + print(format((ops / sample_time), '>24.3f'), end='') + print(format((kilobytes / sample_time), '>16.3f'), end='') +@@ -361,7 +370,11 @@ class DeviceData: + print(format(retransmits, '>16'), end='') + print(format(rtt_per_op, '>16.3f'), end='') + print(format(exe_per_op, '>16.3f'), end='') +- print(format(queued_for_per_op, '>16.3f')) ++ print(format(queued_for_per_op, '>16.3f'), end='') ++ if len(rpc_stats) >= 9: ++ errors = '{0:>10.0f} ({1:>3.1f}%)'.format(errs, errs_percent).strip() ++ print(format(errors, '>16'), end='') ++ print() + + def ops(self, sample_time): + sends = float(self.__rpc_data['rpcsends']) +diff --git a/tools/nfs-iostat/nfsiostat.man b/tools/nfs-iostat/nfsiostat.man +index 9ae94c5f..940c0431 100644 +--- a/tools/nfs-iostat/nfsiostat.man ++++ b/tools/nfs-iostat/nfsiostat.man +@@ -97,6 +97,14 @@ This is the duration from the time the NFS client created the RPC request task t + .RE + .RE + .RE ++.RS 8 ++- \fBerrors\fR ++.RS ++This is the number of operations that completed with an error status (status < 0). This count is only available on kernels with RPC iostats version 1.1 or above. ++.RS ++.RE ++.RE ++.RE + .TP + Note that if an interval is used as argument to \fBnfsiostat\fR, then the diffrence from previous interval will be displayed, otherwise the results will be from the time that the share was mounted. + diff --git a/nfs-utils-2.3.3-nfsiostat-key-error.patch b/nfs-utils-2.3.3-nfsiostat-key-error.patch new file mode 100644 index 0000000..e628c09 --- /dev/null +++ b/nfs-utils-2.3.3-nfsiostat-key-error.patch @@ -0,0 +1,37 @@ +diff -up nfs-utils-2.3.3/tools/mountstats/mountstats.py.orig nfs-utils-2.3.3/tools/mountstats/mountstats.py +--- nfs-utils-2.3.3/tools/mountstats/mountstats.py.orig 2020-12-10 10:48:17.319579958 -0500 ++++ nfs-utils-2.3.3/tools/mountstats/mountstats.py 2020-12-10 10:52:42.481484160 -0500 +@@ -943,10 +943,11 @@ def print_iostat_summary(old, new, devic + if not old or device not in old: + stats.display_iostats(time) + else: +- old_stats = DeviceData() +- old_stats.parse_stats(old[device]) +- diff_stats = stats.compare_iostats(old_stats) +- diff_stats.display_iostats(time) ++ 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 iostat_command(args): + """iostat-like command for NFS mount points +diff -up nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py +--- nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py.orig 2020-12-10 10:48:17.316579880 -0500 ++++ nfs-utils-2.3.3/tools/nfs-iostat/nfs-iostat.py 2020-12-10 10:52:42.481484160 -0500 +@@ -467,10 +467,13 @@ def parse_stats_file(filename): + 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 +- devicelist = [x for x in old if x in devices] ++ for device in devices: ++ if "fstype autofs" not in str(old[device]): ++ devicelist.append(device) + else: + devicelist = devices + diff --git a/nfs-utils-2.3.3-nfsman-softreval.patch b/nfs-utils-2.3.3-nfsman-softreval.patch new file mode 100644 index 0000000..2db2db0 --- /dev/null +++ b/nfs-utils-2.3.3-nfsman-softreval.patch @@ -0,0 +1,53 @@ +commit b5381c96298d75ba66625a007e2390e2b501850d +Author: Trond Myklebust +Date: Wed Jan 29 10:45:39 2020 -0500 + + manpage: Add a description of the 'softreval' / 'nosoftreval' mount option + + Add a description of the 'softreval' / 'nosoftreval' mount options on + the 'nfs' generic manpage. + + Signed-off-by: Trond Myklebust + Signed-off-by: Steve Dickson + +diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man +index 84462cd7..6f79c63a 100644 +--- a/utils/mount/nfs.man ++++ b/utils/mount/nfs.man +@@ -121,6 +121,36 @@ option may mitigate some of the risks of using the + .B soft + option. + .TP 1.5i ++.BR softreval " / " nosoftreval ++In cases where the NFS server is down, it may be useful to ++allow the NFS client to continue to serve up paths and ++attributes from cache after ++.B retrans ++attempts to revalidate that cache have timed out. ++This may, for instance, be helpful when trying to unmount a ++filesystem tree from a server that is permanently down. ++.IP ++It is possible to combine ++.BR softreval ++with the ++.B soft ++mount option, in which case operations that cannot be served up ++from cache will time out and return an error after ++.B retrans ++attempts. The combination with the default ++.B hard ++mount option implies those uncached operations will continue to ++retry until a response is received from the server. ++.IP ++Note: the default mount option is ++.BR nosoftreval ++which disallows fallback to cache when revalidation fails, and ++instead follows the behavior dictated by the ++.B hard ++or ++.B soft ++mount option. ++.TP 1.5i + .BR intr " / " nointr + This option is provided for backward compatibility. + It is ignored after kernel 2.6.25. diff --git a/nfs-utils-2.3.3-nfsman-typo.patch b/nfs-utils-2.3.3-nfsman-typo.patch new file mode 100644 index 0000000..467b2c4 --- /dev/null +++ b/nfs-utils-2.3.3-nfsman-typo.patch @@ -0,0 +1,22 @@ +commit 2b78802c4eda6f74b77330832c54fd6b59991adf +Author: Josef Radinger +Date: Wed Jul 24 10:59:51 2019 -0400 + + nfs.man: Fixed small typo in man page + + Fixes: https://bugzilla.linux-nfs.org/show_bug.cgi?id=337 + Signed-off-by: Steve Dickson + +diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man +index 9ee9bd9..6ba9cef 100644 +--- a/utils/mount/nfs.man ++++ b/utils/mount/nfs.man +@@ -1252,7 +1252,7 @@ If absolute cache coherence among clients is required, + applications should use file locking. Alternatively, applications + can also open their files with the O_DIRECT flag + to disable data caching entirely. +-.SS "File timestamp maintainence" ++.SS "File timestamp maintenance" + NFS servers are responsible for managing file and directory timestamps + .RB ( atime , + .BR ctime ", and" diff --git a/nfs-utils-2.3.3-nfsrahead.patch b/nfs-utils-2.3.3-nfsrahead.patch new file mode 100644 index 0000000..2863a6f --- /dev/null +++ b/nfs-utils-2.3.3-nfsrahead.patch @@ -0,0 +1,399 @@ +diff --git a/.gitignore b/.gitignore +index e97b31f5..e504d492 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -60,6 +60,8 @@ utils/statd/statd + tools/locktest/testlk + tools/getiversion/getiversion + tools/nfsconf/nfsconf ++tools/nfsrahead/nfsrahead ++tools/nfsrahead/99-nfs_bdi.rules + support/export/mount.h + support/export/mount_clnt.c + support/export/mount_xdr.c +diff --git a/configure.ac b/configure.ac +index 6d464ac5..f462a645 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -639,6 +639,7 @@ AC_CONFIG_FILES([ + tools/rpcgen/Makefile + tools/mountstats/Makefile + tools/nfs-iostat/Makefile ++ tools/nfsrahead/Makefile + tools/rpcctl/Makefile + tools/nfsdclnts/Makefile + tools/nfsconf/Makefile +diff --git a/nfs.conf b/nfs.conf +index 86ed7d53..30f9e109 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -5,6 +5,10 @@ + [general] + # pipefs-directory=/var/lib/nfs/rpc_pipefs + # ++[nfsrahead] ++# nfs=15000 ++# nfs4=16000 ++# + [exportfs] + # debug=0 + # +diff --git a/systemd/nfs.conf.man b/systemd/nfs.conf.man +index f32c690b..ebbf28d0 100644 +--- a/systemd/nfs.conf.man ++++ b/systemd/nfs.conf.man +@@ -245,6 +245,17 @@ Only + .B debug= + is recognized. + ++.TP ++.B nfsrahead ++Recognized values: ++.BR nfs , ++.BR nfsv4 , ++.BR default . ++ ++See ++.BR nfsrahead (5) ++for deatils. ++ + .SH FILES + .I /etc/nfs.conf + .SH SEE ALSO +diff --git a/tools/Makefile.am b/tools/Makefile.am +index c3feabbe..40c17c37 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD + OPTDIRS += nfsdclddb + endif + +-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts nfsrahead $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/tools/nfsrahead/99-nfs.rules b/tools/nfsrahead/99-nfs.rules +new file mode 100644 +index 00000000..c74914b2 +--- /dev/null ++++ b/tools/nfsrahead/99-nfs.rules +@@ -0,0 +1 @@ ++SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="/usr/libexec/nfsrahead %k", ATTR{read_ahead_kb}="%c" +diff --git a/tools/nfsrahead/99-nfs.rules.in b/tools/nfsrahead/99-nfs.rules.in +new file mode 100644 +index 00000000..648813c5 +--- /dev/null ++++ b/tools/nfsrahead/99-nfs.rules.in +@@ -0,0 +1 @@ ++SUBSYSTEM=="bdi", ACTION=="add", PROGRAM="_libexecdir_/nfsrahead %k", ATTR{read_ahead_kb}="%c" +diff --git a/tools/nfsrahead/Makefile.am b/tools/nfsrahead/Makefile.am +new file mode 100644 +index 00000000..845ea0d5 +--- /dev/null ++++ b/tools/nfsrahead/Makefile.am +@@ -0,0 +1,16 @@ ++libexec_PROGRAMS = nfsrahead ++nfsrahead_SOURCES = main.c ++nfsrahead_LDFLAGS= -lmount ++nfsrahead_LDADD = ../../support/nfs/libnfsconf.la ++ ++man5_MANS = nfsrahead.man ++EXTRA_DIST = $(man5_MANS) ++ ++udev_rulesdir = /usr/lib/udev/rules.d/ ++udev_rules_DATA = 99-nfs.rules ++ ++99-nfs.rules: 99-nfs.rules.in $(builddefs) ++ $(SED) "s|_libexecdir_|@libexecdir@|g" 99-nfs.rules.in > $@ ++ ++clean-local: ++ $(RM) 99-nfs.rules +diff --git a/tools/nfsrahead/main.c b/tools/nfsrahead/main.c +new file mode 100644 +index 00000000..c83c6f71 +--- /dev/null ++++ b/tools/nfsrahead/main.c +@@ -0,0 +1,192 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "xlog.h" ++#include "conffile.h" ++ ++#ifndef MOUNTINFO_PATH ++#define MOUNTINFO_PATH "/proc/self/mountinfo" ++#endif ++ ++#define CONF_NAME "nfsrahead" ++#define NFS_DEFAULT_READAHEAD 128 ++ ++/* Device information from the system */ ++struct device_info { ++ char *device_number; ++ dev_t dev; ++ char *mountpoint; ++ char *fstype; ++}; ++ ++/* Convert a string in the format n:m to a device number */ ++static int fill_device_number(struct device_info *info) ++{ ++ char *s = strdup(info->device_number), *p; ++ char *maj_s, *min_s; ++ unsigned int maj, min; ++ int err = -EINVAL; ++ ++ maj_s = p = s; ++ for ( ; *p != ':' && *p != '\0'; p++) ++ ; ++ ++ if (*p == '\0') ++ goto out_free; ++ ++ err = 0; ++ *p = '\0'; ++ min_s = p + 1; ++ ++ maj = strtol(maj_s, NULL, 10); ++ min = strtol(min_s, NULL, 10); ++ ++ info->dev = makedev(maj, min); ++out_free: ++ free(s); ++ return err; ++} ++ ++#define sfree(ptr) if (ptr) free(ptr) ++ ++/* device_info maintenance */ ++static void init_device_info(struct device_info *di, const char *device_number) ++{ ++ di->device_number = strdup(device_number); ++ di->dev = 0; ++ di->mountpoint = NULL; ++ di->fstype = NULL; ++} ++ ++ ++static void free_device_info(struct device_info *di) ++{ ++ sfree(di->mountpoint); ++ sfree(di->fstype); ++ sfree(di->device_number); ++} ++ ++static int get_mountinfo(const char *device_number, struct device_info *device_info, const char *mountinfo_path) ++{ ++ int ret = 0; ++ struct libmnt_table *mnttbl; ++ struct libmnt_fs *fs; ++ char *target; ++ ++ init_device_info(device_info, device_number); ++ if ((ret = fill_device_number(device_info)) < 0) ++ goto out_free_device_info; ++ ++ mnttbl = mnt_new_table(); ++ ++ if ((ret = mnt_table_parse_file(mnttbl, mountinfo_path)) < 0) { ++ xlog(D_GENERAL, "Failed to parse %s\n", mountinfo_path); ++ goto out_free_tbl; ++ } ++ ++ if ((fs = mnt_table_find_devno(mnttbl, device_info->dev, MNT_ITER_FORWARD)) == NULL) { ++ ret = ENOENT; ++ goto out_free_tbl; ++ } ++ ++ if ((target = (char *)mnt_fs_get_target(fs)) == NULL) { ++ ret = ENOENT; ++ goto out_free_fs; ++ } ++ ++ device_info->mountpoint = strdup(target); ++ target = (char *)mnt_fs_get_fstype(fs); ++ if (target) ++ device_info->fstype = strdup(target); ++ ++out_free_fs: ++ mnt_free_fs(fs); ++out_free_tbl: ++ mnt_free_table(mnttbl); ++out_free_device_info: ++ free(device_info->device_number); ++ device_info->device_number = NULL; ++ return ret; ++} ++ ++static int get_device_info(const char *device_number, struct device_info *device_info) ++{ ++ int ret = ENOENT; ++ for (int retry_count = 0; retry_count < 10 && ret != 0; retry_count++) ++ ret = get_mountinfo(device_number, device_info, MOUNTINFO_PATH); ++ ++ return ret; ++} ++ ++static int conf_get_readahead(const char *kind) { ++ int readahead = 0; ++ ++ if((readahead = conf_get_num(CONF_NAME, kind, -1)) == -1) ++ readahead = conf_get_num(CONF_NAME, "default", NFS_DEFAULT_READAHEAD); ++ ++ return readahead; ++} ++ ++int main(int argc, char **argv) ++{ ++ int ret = 0, retry, opt; ++ struct device_info device; ++ unsigned int readahead = 128, log_level, log_stderr = 0; ++ ++ ++ log_level = D_ALL & ~D_GENERAL; ++ while((opt = getopt(argc, argv, "dF")) != -1) { ++ switch (opt) { ++ case 'd': ++ log_level = D_ALL; ++ break; ++ case 'F': ++ log_stderr = 1; ++ break; ++ } ++ } ++ ++ conf_init_file(NFS_CONFFILE); ++ ++ xlog_stderr(log_stderr); ++ xlog_syslog(~log_stderr); ++ xlog_config(log_level, 1); ++ xlog_open(CONF_NAME); ++ ++ // xlog_err causes the system to exit ++ if ((argc - optind) != 1) ++ xlog_err("expected the device number of a BDI; is udev ok?"); ++ ++ for (retry = 0; retry <= 10; retry++ ) ++ if ((ret = get_device_info(argv[optind], &device)) == 0) ++ break; ++ ++ if (ret != 0) { ++ xlog(D_GENERAL, "unable to find device %s\n", argv[optind]); ++ goto out; ++ } ++ ++ if (strncmp("nfs", device.fstype, 3) != 0) { ++ xlog(D_GENERAL, ++ "not setting readahead for non supported fstype %s on device %s\n", ++ device.fstype, argv[optind]); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ readahead = conf_get_readahead(device.fstype); ++ ++ xlog(D_FAC7, "setting %s readahead to %d\n", device.mountpoint, readahead); ++ ++ printf("%d\n", readahead); ++ ++out: ++ free_device_info(&device); ++ return ret; ++} +diff --git a/tools/nfsrahead/nfsrahead.man b/tools/nfsrahead/nfsrahead.man +new file mode 100644 +index 00000000..5488f633 +--- /dev/null ++++ b/tools/nfsrahead/nfsrahead.man +@@ -0,0 +1,72 @@ ++.\" Manpage for nfsrahead. ++.nh ++.ad l ++.TH man 5 "08 Mar 2022" "1.0" "nfsrahead man page" ++.SH NAME ++ ++nfsrahead \- Configure the readahead for NFS mounts ++ ++.SH SYNOPSIS ++ ++nfsrahead [-F] [-d] ++ ++.SH DESCRIPTION ++ ++\fInfsrahead\fR is a tool intended to be used with udev to set the \fIread_ahead_kb\fR parameter of NFS mounts, according to the configuration file (see \fICONFIGURATION\fR). \fIdevice\fR is the device number for the NFS backing device as provided by the kernel. ++ ++.SH OPTIONS ++.TP ++.B -F ++Send messages to ++.I stderr ++instead of ++.I syslog ++ ++.TP ++.B -d ++Increase the debugging level. ++ ++.SH CONFIGURATION ++.I nfsrahead ++is configured in ++.IR /etc/nfs.conf , ++in the section titled ++.IR nfsrahead . ++It accepts the following configurations. ++ ++.TP ++.B nfs= ++The readahead value applied to NFSv3 mounts. ++ ++.TP ++.B nfs4= ++The readahead value applied to NFSv4 mounts. ++ ++.TP ++.B default= ++The default configuration when none of the configurations above is set. ++ ++.SH EXAMPLE CONFIGURATION ++[nfsrahead] ++.br ++nfs=15000 # readahead of 15000 for NFSv3 mounts ++.br ++nfs4=16000 # readahead of 16000 for NFSv4 mounts ++.br ++default=128 # default is 128 ++ ++.SH SEE ALSO ++ ++.BR mount.nfs (8), ++.BR nfs (5), ++.BR nfs.conf (5), ++.BR udev (7), ++.BR bcc-readahead (8) ++ ++.SH BUGS ++ ++No known bugs. ++ ++.SH AUTHOR ++ ++Thiago Rafael Becker +diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man +index 2af16f31..1911c41b 100644 +--- a/utils/nfsidmap/nfsidmap.man ++++ b/utils/nfsidmap/nfsidmap.man +@@ -2,7 +2,7 @@ + .\"@(#)nfsidmap(8) - The NFS idmapper upcall program + .\" + .\" Copyright (C) 2010 Bryan Schumaker +-.TH nfsidmap 5 "1 October 2010" ++.TH nfsidmap 8 "1 October 2010" + .SH NAME + nfsidmap \- The NFS idmapper upcall program + .SH SYNOPSIS diff --git a/nfs-utils-2.3.3-remove-osd_login.patch b/nfs-utils-2.3.3-remove-osd_login.patch new file mode 100644 index 0000000..d626790 --- /dev/null +++ b/nfs-utils-2.3.3-remove-osd_login.patch @@ -0,0 +1,188 @@ +commit 80b17639d78e152306d8d1753d719654ebb40e01 +Author: Steve Dickson +Date: Fri Oct 19 10:26:10 2018 -0400 + + Remove osd_login + + This ancient script has not been used + in years, if used at all. + + Signed-off-by: Steve Dickson + +diff --git a/configure.ac b/configure.ac +index e82ff14..cf1c4b9 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -238,13 +238,6 @@ AC_ARG_ENABLE(nfsdcltrack, + enable_nfsdcltrack=$enableval, + enable_nfsdcltrack="yes") + +-AC_ARG_ENABLE(osdlogin, +- [AC_HELP_STRING([--enable-osdlogin], +- [enable osd_login scripts @<:@default=no@:>@])], +- enable_osdlogin=$enableval, +- enable_osdlogin="no") +- AM_CONDITIONAL(CONFIG_OSD_LOGIN, [test "$enable_osdlogin" = "yes" ]) +- + dnl Check for TI-RPC library and headers + AC_LIBTIRPC + +@@ -631,7 +624,6 @@ AC_CONFIG_FILES([ + utils/nfsidmap/Makefile + utils/showmount/Makefile + utils/statd/Makefile +- utils/osd_login/Makefile + systemd/Makefile + tests/Makefile + tests/nsm_client/Makefile]) +diff --git a/utils/Makefile.am b/utils/Makefile.am +index d361aea..0a5b062 100644 +--- a/utils/Makefile.am ++++ b/utils/Makefile.am +@@ -34,7 +34,6 @@ SUBDIRS = \ + nfsstat \ + showmount \ + statd \ +- osd_login \ + $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/utils/osd_login/Makefile.am b/utils/osd_login/Makefile.am +deleted file mode 100644 +index ded1fd3..0000000 +--- a/utils/osd_login/Makefile.am ++++ /dev/null +@@ -1,9 +0,0 @@ +-## Process this file with automake to produce Makefile.in +- +-# These binaries go in /sbin (not /usr/sbin), and that cannot be +-# overridden at config time. +-sbindir = /sbin +- +-dist_sbin_SCRIPTS = osd_login +- +-MAINTAINERCLEANFILES = Makefile.in +diff --git a/utils/osd_login/osd_login b/utils/osd_login/osd_login +deleted file mode 100644 +index 08cd2d2..0000000 +--- a/utils/osd_login/osd_login ++++ /dev/null +@@ -1,118 +0,0 @@ +-#!/bin/bash +-# +-# osd_login : This script is part of the autologin feature +-# mandated by the pnfs-objects standard. +-# It is called from objlayoutdriver.ko in the kernel. +- +-# Copyright (C) 2012, Sachin Bhamare +-# Copyright (C) 2012, Boaz Harrosh +-# +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License version 2 as +-# published by the Free Software Foundation. +-# +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +-# MA 02110-1301 USA +- +-umask 022 +- +-PATH="/sbin:/usr/sbin:/bin:/usr/bin" +- +-iscsiadm=/sbin/iscsiadm +- +-PARENT_PID=$BASHPID +-WATCHDOG_TIMEOUT=15 +- +-protocol="" +-portal="" +-uri="" +-osdname="" +-systemid="" +- +-usage() +-{ +- echo "Usage: $0 -u -o -s " +- echo "Options:" +- echo "-u target uri e.g. iscsi://:" +- echo "-o osdname of the target OSD" +- echo "-s systemid of the target OSD" +-} +- +-parse_cmdline() +-{ +- argc=$# +- if [ $# -lt 3 ]; then +- usage +- exit 1 +- fi +- +- # parse the input arguments +- while getopts "u:o:s:" options; do +- case $options in +- u ) uri=$OPTARG;; +- o ) osdname=$OPTARG;; +- s ) systemid=$OPTARG;; +- \? ) usage +- exit 1;; +- * ) usage +- exit 1;; +- esac +- done +- +- echo "-u : $uri" +- echo "-o : $osdname" +- echo "-s : $systemid" +- +- protocol=`echo $uri | awk -F ':' '{print $1}'` +- portal=`echo $uri | awk -F '//' '{print $2}'` +-} +- +-watchdog() +-{ +- timeout=$1 +- portal=$2 +- +- sleep $timeout +- if kill -9 $PARENT_PID; then +- echo "watchdog : Timed out (>$timeout seconds) while login into $portal" | logger -t "osd_login" +- fi +- echo "watchdog: exiting .." +- exit 2 +-} +- +-login_iscsi_osd() +-{ +- echo "login into: $1" +- if ! $iscsiadm -m discovery -o nonpersistent -t sendtargets -p $1 --login; then +- echo "$iscsiadm -m discovery -t sendtargets -p $1 --login returned error $? !" +- sleep 1; +- fi +-} +- +-echo "============= osd_login =========" +-echo "progname : $0" +-parse_cmdline "$@" +-echo "protocol: $protocol" +-echo "portal: $portal" +- +-watchdog $WATCHDOG_TIMEOUT $portal & +-watchdog_pid=$! +- +-case $protocol in +-iscsi) +- login_iscsi_osd $portal |& logger -t "osd_login" +- ;; +-*) +- echo "Error: protocol $protocol not supported !" | logger -t "osd_login" +- ;; +-esac +- +-kill -9 $watchdog_pid +-exit 0 diff --git a/nfs-utils-2.3.3-rpcctl-posixpath.patch b/nfs-utils-2.3.3-rpcctl-posixpath.patch new file mode 100644 index 0000000..ea1e5b8 --- /dev/null +++ b/nfs-utils-2.3.3-rpcctl-posixpath.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-2.3.3/tools/rpcctl/rpcctl.py.orig nfs-utils-2.3.3/tools/rpcctl/rpcctl.py +--- nfs-utils-2.3.3/tools/rpcctl/rpcctl.py.orig 2022-06-27 13:22:19.844747880 -0400 ++++ nfs-utils-2.3.3/tools/rpcctl/rpcctl.py 2022-06-27 13:23:02.168004219 -0400 +@@ -213,7 +213,7 @@ class RpcClient: + def __init__(self, path): + self.path = path + self.name = path.stem +- self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",") ++ self.switch = XprtSwitch(path / os.readlink(path / "switch"), sep=",") + + def __lt__(self, rhs): + return self.name < rhs.name diff --git a/nfs-utils-2.3.3-rpcctl-subparser.patch b/nfs-utils-2.3.3-rpcctl-subparser.patch new file mode 100644 index 0000000..6bdc813 --- /dev/null +++ b/nfs-utils-2.3.3-rpcctl-subparser.patch @@ -0,0 +1,34 @@ +From 2fdd10bebf395b51e931a10adbdc85f3a3f8a285 Mon Sep 17 00:00:00 2001 +From: Alice Mitchell +Date: Thu, 23 Jun 2022 16:04:45 +0100 +Subject: [PATCH] Remove subparser required option as that was added in py3.7 + +--- + tools/rpcctl/rpcctl.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py +index d2110ad6..2ac6ede9 100755 +--- a/tools/rpcctl/rpcctl.py ++++ b/tools/rpcctl/rpcctl.py +@@ -120,7 +120,7 @@ class Xprt: + set = subparser.add_parser("set", help="Change an xprt property") + set.add_argument("xprt", metavar="XPRT", nargs=1, + help="Name of a specific xprt to modify") +- subparser = set.add_subparsers(required=True) ++ subparser = set.add_subparsers() + online = subparser.add_parser("online", help="Set an xprt online") + online.set_defaults(func=Xprt.set_property, property="online") + offline = subparser.add_parser("offline", help="Set an xprt offline") +@@ -185,7 +185,7 @@ class XprtSwitch: + set = subparser.add_parser("set", help="Change an xprt switch property") + set.add_argument("switch", metavar="SWITCH", nargs=1, + help="Name of a specific xprt switch to modify") +- subparser = set.add_subparsers(required=True) ++ subparser = set.add_subparsers() + dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr") + dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1, + help="The new address for the xprt switch") +-- +2.36.1 + diff --git a/nfs-utils-2.3.3-rpcctl.patch b/nfs-utils-2.3.3-rpcctl.patch new file mode 100644 index 0000000..cec8437 --- /dev/null +++ b/nfs-utils-2.3.3-rpcctl.patch @@ -0,0 +1,384 @@ +diff --git a/configure.ac b/configure.ac +index f2f2303b..6d464ac5 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -639,6 +639,7 @@ AC_CONFIG_FILES([ + tools/rpcgen/Makefile + tools/mountstats/Makefile + tools/nfs-iostat/Makefile ++ tools/rpcctl/Makefile + tools/nfsdclnts/Makefile + tools/nfsconf/Makefile + tools/nfsdclddb/Makefile +diff --git a/tools/Makefile.am b/tools/Makefile.am +index 9b4b0803..c3feabbe 100644 +--- a/tools/Makefile.am ++++ b/tools/Makefile.am +@@ -12,6 +12,6 @@ if CONFIG_NFSDCLD + OPTDIRS += nfsdclddb + endif + +-SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat nfsdclnts $(OPTDIRS) ++SUBDIRS = locktest rpcdebug nlmtest mountstats nfs-iostat rpcctl nfsdclnts $(OPTDIRS) + + MAINTAINERCLEANFILES = Makefile.in +diff --git a/tools/rpcctl/Makefile.am b/tools/rpcctl/Makefile.am +new file mode 100644 +index 00000000..33fb431f +--- /dev/null ++++ b/tools/rpcctl/Makefile.am +@@ -0,0 +1,13 @@ ++## Process this file with automake to produce Makefile.in ++PYTHON_FILES = rpcctl.py ++ ++man8_MANS = rpcctl.man ++ ++EXTRA_DIST = $(man8_MANS) $(PYTHON_FILES) ++ ++all-local: $(PYTHON_FILES) ++ ++install-data-hook: ++ $(INSTALL) -m 755 rpcctl.py $(DESTDIR)$(sbindir)/rpcctl ++ ++MAINTAINERCLEANFILES=Makefile.in +diff --git a/tools/rpcctl/rpcctl.man b/tools/rpcctl/rpcctl.man +new file mode 100644 +index 00000000..b87ba0df +--- /dev/null ++++ b/tools/rpcctl/rpcctl.man +@@ -0,0 +1,67 @@ ++.\" ++.\" rpcctl(8) ++.\" ++.TH rpcctl 8 "15 Feb 2022" ++.SH NAME ++rpcctl \- Displays SunRPC connection information ++.SH SYNOPSIS ++.nf ++.BR rpcctl " [ \fB\-h \fR| \fB\-\-help \fR] { \fBclient \fR| \fBswitch \fR| \fBxprt \fR}" ++.P ++.BR "rpcctl client" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBshow \fR}" ++.BR "rpcctl client show " "\fR[ \fB\-h \f| \fB\-\-help \fR] [ \fIXPRT \fR]" ++.P ++.BR "rpcctl switch" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBset \fR| \fBshow \fR}" ++.BR "rpcctl switch set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fISWITCH \fBdstaddr \fINEWADDR" ++.BR "rpcctl switch show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fISWITCH \fR]" ++.P ++.BR "rpcctl xprt" " \fR[ \fB\-h \fR| \fB\-\-help \fR] { \fBremove \fR| \fBset \fR| \fBshow \fR}" ++.BR "rpcctl xprt remove" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT" ++.BR "rpcctl xprt set" " \fR[ \fB\-h \fR| \fB\-\-help \fR] \fIXPRT \fR{ \fBdstaddr \fINEWADDR \fR| \fBoffline \fR| \fBonline \fR}" ++.BR "rpcctl xprt show" " \fR[ \fB\-h \fR| \fB\-\-help \fR] [ \fIXPRT \fR]" ++.fi ++.SH DESCRIPTION ++.RB "The " rpcctl " command displays information collected in the SunRPC sysfs files about the system's SunRPC objects. ++.P ++.SS rpcctl client \fR- \fBCommands operating on RPC clients ++.IP "\fBshow \fR[ \fICLIENT \fR] \fB(default)" ++Show detailed information about the RPC clients on this system. ++If \fICLIENT \fRwas provided, then only show information about a single RPC client. ++.P ++.SS rpcctl switch \fR- \fBCommands operating on groups of transports ++.IP "\fBset \fISWITCH \fBdstaddr \fINEWADDR" ++Change the destination address of all transports in the \fISWITCH \fRto \fINEWADDR\fR. ++\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3). ++.IP "\fBshow \fR[ \fISWITCH \fR] \fB(default)" ++Show detailed information about groups of transports on this system. ++If \fISWITCH \fRwas provided, then only show information about a single transport group. ++.P ++.SS rpcctl xprt \fR- \fBCommands operating on individual transports ++.IP "\fBremove \fIXPRT" ++Removes the specified \fIXPRT \fRfrom the system. ++Note that "main" transports cannot be removed. ++.P ++.IP "\fBset \fIXPRT \fBdstaddr \fINEWADDR" ++Change the destination address of the specified \fIXPRT \fR to \fINEWADDR\fR. ++\fINEWADDR \fRcan be an IP address, DNS name, or anything else resolvable by \fBgethostbyname\fR(3). ++.P ++.IP "\fBset \fIXPRT \fBoffline" ++Sets the specified \fIXPRT\fR's state to offline. ++.P ++.IP "\fBset \fIXPRT \fBonline" ++Sets the specified \fIXPRT\fR's state to online. ++.IP "\fBshow \fR[ \fIXPRT \fR] \fB(default)" ++Show detailed information about this system's transports. ++If \fIXPRT \fRwas provided, then only show information about a single transport. ++.SH EXAMPLES ++.IP "\fBrpcctl switch show switch-2" ++Show details about the RPC switch named "switch-2". ++.IP "\fBrpcctl xprt remove xprt-4" ++Remove the xprt named "xprt-4" from the system. ++.IP "\fBrpcctl xprt set xprt-3 dstaddr https://linux-nfs.org ++Change the dstaddr of the xprt named "xprt-3" to point to linux-nfs.org ++.SH DIRECTORY ++.TP ++.B /sys/kernel/sunrpc/ ++.SH AUTHOR ++Anna Schumaker +diff --git a/tools/rpcctl/rpcctl.py b/tools/rpcctl/rpcctl.py +new file mode 100755 +index 00000000..d2110ad6 +--- /dev/null ++++ b/tools/rpcctl/rpcctl.py +@@ -0,0 +1,262 @@ ++#!/usr/bin/python3 ++import argparse ++import collections ++import errno ++import os ++import pathlib ++import socket ++import sys ++ ++with open("/proc/mounts", 'r') as f: ++ mount = [ line.split()[1] for line in f if "sysfs" in line ] ++ if len(mount) == 0: ++ print("ERROR: sysfs is not mounted") ++ sys.exit(1) ++ ++sunrpc = pathlib.Path(mount[0]) / "kernel" / "sunrpc" ++if not sunrpc.is_dir(): ++ print("ERROR: sysfs does not have sunrpc directory") ++ sys.exit(1) ++ ++def read_addr_file(path): ++ try: ++ with open(path, 'r') as f: ++ return f.readline().strip() ++ except: ++ return "(enoent)" ++ ++def write_addr_file(path, newaddr): ++ with open(path, 'w') as f: ++ f.write(newaddr) ++ return read_addr_file(path) ++ ++def read_info_file(path): ++ res = collections.defaultdict(int) ++ try: ++ with open(path) as info: ++ lines = [ l.split("=", 1) for l in info if "=" in l ] ++ res.update({ key:int(val.strip()) for (key, val) in lines }) ++ finally: ++ return res ++ ++ ++class Xprt: ++ def __init__(self, path): ++ self.path = path ++ self.name = path.stem.rsplit("-", 1)[0] ++ self.type = path.stem.split("-")[2] ++ self.info = read_info_file(path / "xprt_info") ++ self.dstaddr = read_addr_file(path / "dstaddr") ++ self.srcaddr = read_addr_file(path / "srcaddr") ++ self.read_state() ++ ++ def __lt__(self, rhs): ++ return self.name < rhs.name ++ ++ def _xprt(self): ++ main = ", main" if self.info.get("main_xprt") else "" ++ return f"{self.name}: {self.type}, {self.dstaddr}, " \ ++ f"port {self.info['dst_port']}, state <{self.state}>{main}" ++ ++ def _src_reqs(self): ++ return f" Source: {self.srcaddr}, port {self.info['src_port']}, " \ ++ f"Requests: {self.info['num_reqs']}" ++ ++ def _cong_slots(self): ++ return f" Congestion: cur {self.info['cur_cong']}, win {self.info['cong_win']}, " \ ++ f"Slots: min {self.info['min_num_slots']}, max {self.info['max_num_slots']}" ++ ++ def _queues(self): ++ return f" Queues: binding {self.info['binding_q_len']}, " \ ++ f"sending {self.info['sending_q_len']}, pending {self.info['pending_q_len']}, " \ ++ f"backlog {self.info['backlog_q_len']}, tasks {self.info['tasks_queuelen']}" ++ ++ def __str__(self): ++ if not self.path.exists(): ++ return f"{self.name}: has been removed" ++ return "\n".join([self._xprt(), self._src_reqs(), ++ self._cong_slots(), self._queues() ]) ++ ++ def read_state(self): ++ if self.path.exists(): ++ with open(self.path / "xprt_state") as f: ++ self.state = ','.join(f.readline().split()[1:]) ++ ++ def small_str(self): ++ main = " [main]" if self.info.get("main_xprt") else "" ++ return f"{self.name}: {self.type}, {self.dstaddr}{main}" ++ ++ def set_dstaddr(self, newaddr): ++ self.dstaddr = write_addr_file(self.path / "dstaddr", newaddr) ++ ++ def set_state(self, state): ++ if self.info.get("main_xprt"): ++ raise Exception(f"Main xprts cannot be set {state}") ++ with open(self.path / "xprt_state", 'w') as f: ++ f.write(state) ++ self.read_state() ++ ++ def remove(self): ++ if self.info.get("main_xprt"): ++ raise Exception("Main xprts cannot be removed") ++ self.set_state("offline") ++ self.set_state("remove") ++ ++ def add_command(subparser): ++ parser = subparser.add_parser("xprt", help="Commands for individual xprts") ++ parser.set_defaults(func=Xprt.show, xprt=None) ++ subparser = parser.add_subparsers() ++ ++ remove = subparser.add_parser("remove", help="Remove an xprt") ++ remove.add_argument("xprt", metavar="XPRT", nargs=1, ++ help="Name of the xprt to remove") ++ remove.set_defaults(func=Xprt.set_property, property="remove") ++ ++ show = subparser.add_parser("show", help="Show xprts") ++ show.add_argument("xprt", metavar="XPRT", nargs='?', ++ help="Name of a specific xprt to show") ++ show.set_defaults(func=Xprt.show) ++ ++ set = subparser.add_parser("set", help="Change an xprt property") ++ set.add_argument("xprt", metavar="XPRT", nargs=1, ++ help="Name of a specific xprt to modify") ++ subparser = set.add_subparsers(required=True) ++ online = subparser.add_parser("online", help="Set an xprt online") ++ online.set_defaults(func=Xprt.set_property, property="online") ++ offline = subparser.add_parser("offline", help="Set an xprt offline") ++ offline.set_defaults(func=Xprt.set_property, property="offline") ++ dstaddr = subparser.add_parser("dstaddr", help="Change an xprt's dstaddr") ++ dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1, ++ help="The new address for the xprt") ++ dstaddr.set_defaults(func=Xprt.set_property, property="dstaddr") ++ ++ def get_by_name(name): ++ glob = f"**/{name}-*" if name else "**/xprt-*" ++ res = [ Xprt(x) for x in (sunrpc / "xprt-switches").glob(glob) ] ++ if name and len(res) == 0: ++ raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), ++ f"{sunrpc / 'xprt-switches' / glob}") ++ return sorted(res) ++ ++ def show(args): ++ for xprt in Xprt.get_by_name(args.xprt): ++ print(xprt) ++ ++ def set_property(args): ++ for xprt in Xprt.get_by_name(args.xprt[0]): ++ if args.property == "dstaddr": ++ xprt.set_dstaddr(socket.gethostbyname(args.newaddr[0])) ++ elif args.property == "remove": ++ xprt.remove() ++ else: ++ xprt.set_state(args.property) ++ print(xprt) ++ ++ ++class XprtSwitch: ++ def __init__(self, path, sep=":"): ++ self.path = path ++ self.name = path.stem ++ self.info = read_info_file(path / "xprt_switch_info") ++ self.xprts = sorted([ Xprt(p) for p in self.path.iterdir() if p.is_dir() ]) ++ self.sep = sep ++ ++ def __lt__(self, rhs): ++ return self.name < rhs.name ++ ++ def __str__(self): ++ switch = f"{self.name}{self.sep} " \ ++ f"xprts {self.info['num_xprts']}, " \ ++ f"active {self.info['num_active']}, " \ ++ f"queue {self.info['queue_len']}" ++ xprts = [ f" {x.small_str()}" for x in self.xprts ] ++ return "\n".join([ switch ] + xprts) ++ ++ def add_command(subparser): ++ parser = subparser.add_parser("switch", help="Commands for xprt switches") ++ parser.set_defaults(func=XprtSwitch.show, switch=None) ++ subparser = parser.add_subparsers() ++ ++ show = subparser.add_parser("show", help="Show xprt switches") ++ show.add_argument("switch", metavar="SWITCH", nargs='?', ++ help="Name of a specific switch to show") ++ show.set_defaults(func=XprtSwitch.show) ++ ++ set = subparser.add_parser("set", help="Change an xprt switch property") ++ set.add_argument("switch", metavar="SWITCH", nargs=1, ++ help="Name of a specific xprt switch to modify") ++ subparser = set.add_subparsers(required=True) ++ dstaddr = subparser.add_parser("dstaddr", help="Change an xprt switch's dstaddr") ++ dstaddr.add_argument("newaddr", metavar="NEWADDR", nargs=1, ++ help="The new address for the xprt switch") ++ dstaddr.set_defaults(func=XprtSwitch.set_property, property="dstaddr") ++ ++ def get_by_name(name): ++ xprt_switches = sunrpc / "xprt-switches" ++ if name: ++ return [ XprtSwitch(xprt_switches / name) ] ++ return [ XprtSwitch(f) for f in sorted(xprt_switches.iterdir()) ] ++ ++ def show(args): ++ for switch in XprtSwitch.get_by_name(args.switch): ++ print(switch) ++ ++ def set_property(args): ++ for switch in XprtSwitch.get_by_name(args.switch[0]): ++ resolved = socket.gethostbyname(args.newaddr[0]) ++ for xprt in switch.xprts: ++ xprt.set_dstaddr(resolved) ++ print(switch) ++ ++ ++class RpcClient: ++ def __init__(self, path): ++ self.path = path ++ self.name = path.stem ++ self.switch = XprtSwitch(path / (path / "switch").readlink(), sep=",") ++ ++ def __lt__(self, rhs): ++ return self.name < rhs.name ++ ++ def __str__(self): ++ return f"{self.name}: {self.switch}" ++ ++ def add_command(subparser): ++ parser = subparser.add_parser("client", help="Commands for rpc clients") ++ parser.set_defaults(func=RpcClient.show, client=None) ++ subparser = parser.add_subparsers() ++ ++ show = subparser.add_parser("show", help="Show rpc clients") ++ show.add_argument("client", metavar="CLIENT", nargs='?', ++ help="Name of a specific rpc client to show") ++ parser.set_defaults(func=RpcClient.show) ++ ++ def get_by_name(name): ++ rpc_clients = sunrpc / "rpc-clients" ++ if name: ++ return [ RpcClient(rpc_clients / name) ] ++ return [ RpcClient(f) for f in sorted(rpc_clients.iterdir()) ] ++ ++ def show(args): ++ for client in RpcClient.get_by_name(args.client): ++ print(client) ++ ++ ++parser = argparse.ArgumentParser() ++ ++def show_small_help(args): ++ parser.print_usage() ++ print("sunrpc dir:", sunrpc) ++parser.set_defaults(func=show_small_help) ++ ++subparser = parser.add_subparsers(title="commands") ++RpcClient.add_command(subparser) ++XprtSwitch.add_command(subparser) ++Xprt.add_command(subparser) ++ ++args = parser.parse_args() ++try: ++ args.func(args) ++except Exception as e: ++ print(str(e)) ++ sys.exit(1) diff --git a/nfs-utils-2.3.3-statd-force.patch b/nfs-utils-2.3.3-statd-force.patch new file mode 100644 index 0000000..bd335f2 --- /dev/null +++ b/nfs-utils-2.3.3-statd-force.patch @@ -0,0 +1,73 @@ +diff -up nfs-utils-2.3.3/nfs.conf.orig nfs-utils-2.3.3/nfs.conf +--- nfs-utils-2.3.3/nfs.conf.orig 2019-03-05 10:30:28.100560625 -0500 ++++ nfs-utils-2.3.3/nfs.conf 2019-03-05 10:35:28.702004199 -0500 +@@ -64,6 +64,7 @@ use-gss-proxy=1 + # + [sm-notify] + # debug=0 ++# force=0 + # retry-time=900 + # outgoing-port= + # outgoing-addr= +diff -up nfs-utils-2.3.3/utils/statd/sm-notify.c.orig nfs-utils-2.3.3/utils/statd/sm-notify.c +--- nfs-utils-2.3.3/utils/statd/sm-notify.c.orig 2019-03-05 10:30:28.070560401 -0500 ++++ nfs-utils-2.3.3/utils/statd/sm-notify.c 2019-03-05 10:35:28.703004207 -0500 +@@ -49,6 +49,7 @@ + #define NLM_END_GRACE_FILE "/proc/fs/lockd/nlm_end_grace" + + int lift_grace = 1; ++int force = 0; + + struct nsm_host { + struct nsm_host * next; +@@ -480,19 +481,10 @@ nsm_lift_grace_period(void) + close(fd); + return; + } +- +-int +-main(int argc, char **argv) ++inline static void ++read_nfsconf(char **argv) + { +- int c, sock, force = 0; +- char * progname; +- char * s; +- +- progname = strrchr(argv[0], '/'); +- if (progname != NULL) +- progname++; +- else +- progname = argv[0]; ++ char *s; + + conf_init_file(NFS_CONFFILE); + xlog_from_conffile("sm-notify"); +@@ -500,10 +492,27 @@ main(int argc, char **argv) + opt_srcport = conf_get_str("sm-notify", "outgoing-port"); + opt_srcaddr = conf_get_str("sm-notify", "outgoing-addr"); + lift_grace = conf_get_bool("sm-notify", "lift-grace", lift_grace); ++ + s = conf_get_str("statd", "state-directory-path"); + if (s && !nsm_setup_pathnames(argv[0], s)) + exit(1); + opt_update_state = conf_get_bool("sm-notify", "update-state", opt_update_state); ++ force = conf_get_bool("sm-notify", "force", force); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int c, sock; ++ char * progname; ++ ++ progname = strrchr(argv[0], '/'); ++ if (progname != NULL) ++ progname++; ++ else ++ progname = argv[0]; ++ ++ read_nfsconf(argv); + + while ((c = getopt(argc, argv, "dm:np:v:P:f")) != -1) { + switch (c) { diff --git a/nfs-utils-2.3.3-statd-no-notify.patch b/nfs-utils-2.3.3-statd-no-notify.patch new file mode 100644 index 0000000..e29d2e4 --- /dev/null +++ b/nfs-utils-2.3.3-statd-no-notify.patch @@ -0,0 +1,105 @@ +commit 5394f939b591e65fec37a6bee826c13620d3f39b +Author: Justin Mitchell +Date: Mon Mar 4 11:53:09 2019 -0500 + + Add nfs.conf equivalent for the statd --no-notify cmdline option + + Also cleaned up how nfs.conf is read. + + Signed-off-by: Justin Mitchell + Signed-off-by: Steve Dickson + +diff --git a/nfs.conf b/nfs.conf +index f1ebfdb..d332375 100644 +--- a/nfs.conf ++++ b/nfs.conf +@@ -62,6 +62,7 @@ + # name= + # state-directory-path=/var/lib/nfs/statd + # ha-callout= ++# no-notify=0 + # + [sm-notify] + # debug=0 +diff --git a/utils/statd/statd.c b/utils/statd/statd.c +index 2cc6cf3..1467380 100644 +--- a/utils/statd/statd.c ++++ b/utils/statd/statd.c +@@ -238,6 +238,39 @@ static void set_nlm_port(char *type, int port) + fprintf(stderr, "%s: failed to open %s: %s\n", + name_p, pathbuf, strerror(errno)); + } ++int port = 0, out_port = 0; ++int nlm_udp = 0, nlm_tcp = 0; ++ ++inline static void ++read_nfsconf(char **argv) ++{ ++ char *s; ++ ++ conf_init_file(NFS_CONFFILE); ++ xlog_from_conffile("statd"); ++ ++ out_port = conf_get_num("statd", "outgoing-port", out_port); ++ port = conf_get_num("statd", "port", port); ++ ++ MY_NAME = conf_get_str("statd", "name"); ++ if (MY_NAME) ++ run_mode |= STATIC_HOSTNAME; ++ ++ s = conf_get_str("statd", "state-directory-path"); ++ if (s && !nsm_setup_pathnames(argv[0], s)) ++ exit(1); ++ ++ s = conf_get_str("statd", "ha-callout"); ++ if (s) ++ ha_callout_prog = s; ++ ++ nlm_tcp = conf_get_num("lockd", "port", nlm_tcp); ++ /* udp defaults to the same as tcp ! */ ++ nlm_udp = conf_get_num("lockd", "udp-port", nlm_tcp); ++ ++ if (conf_get_bool("statd", "no-notify", false)) ++ run_mode |= MODE_NO_NOTIFY; ++} + + /* + * Entry routine/main loop. +@@ -245,11 +278,8 @@ static void set_nlm_port(char *type, int port) + int main (int argc, char **argv) + { + extern char *optarg; +- char *s; + int pid; + int arg; +- int port = 0, out_port = 0; +- int nlm_udp = 0, nlm_tcp = 0; + struct rlimit rlim; + int notify_sockfd; + char *env; +@@ -275,23 +305,8 @@ int main (int argc, char **argv) + /* Set hostname */ + MY_NAME = NULL; + +- conf_init_file(NFS_CONFFILE); +- xlog_from_conffile("statd"); +- out_port = conf_get_num("statd", "outgoing-port", out_port); +- port = conf_get_num("statd", "port", port); +- MY_NAME = conf_get_str("statd", "name"); +- if (MY_NAME) +- run_mode |= STATIC_HOSTNAME; +- s = conf_get_str("statd", "state-directory-path"); +- if (s && !nsm_setup_pathnames(argv[0], s)) +- exit(1); +- s = conf_get_str("statd", "ha-callout"); +- if (s) +- ha_callout_prog = s; +- +- nlm_tcp = conf_get_num("lockd", "port", nlm_tcp); +- /* udp defaults to the same as tcp ! */ +- nlm_udp = conf_get_num("lockd", "udp-port", nlm_tcp); ++ /* Read nfs.conf */ ++ read_nfsconf(argv); + + /* Process command line switches */ + while ((arg = getopt_long(argc, argv, "h?vVFNH:dn:p:o:P:LT:U:", longopts, NULL)) != EOF) { diff --git a/nfs-utils-2.3.3-systemd-exportfs-nofail.patch b/nfs-utils-2.3.3-systemd-exportfs-nofail.patch new file mode 100644 index 0000000..a41bdd7 --- /dev/null +++ b/nfs-utils-2.3.3-systemd-exportfs-nofail.patch @@ -0,0 +1,37 @@ +commit 003000d451833309c963054e58a48fa1df7e767b +Author: Steve Dickson +Date: Thu Dec 10 13:13:03 2020 -0500 + + exportfs: Ingnore export failures in nfs-server.serivce unit + + With some recent commits, exportfs will continue on trying to + export filesystems even when an entry is invalid or does + not exist, but will still have a non-zero exit to report + the error. + + This situation should not stop the nfs-server service + from comingup so nfs-server.service file should + ignore these types of failures + + Signed-off-by: Steve Dickson + +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index 06c1adb7..b432f910 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -21,13 +21,13 @@ After=rpc-gssd.service gssproxy.service rpc-svcgssd.service + [Service] + Type=oneshot + RemainAfterExit=yes +-ExecStartPre=/usr/sbin/exportfs -r ++ExecStartPre=-/usr/sbin/exportfs -r + ExecStart=/usr/sbin/rpc.nfsd + ExecStop=/usr/sbin/rpc.nfsd 0 + ExecStopPost=/usr/sbin/exportfs -au + ExecStopPost=/usr/sbin/exportfs -f + +-ExecReload=/usr/sbin/exportfs -r ++ExecReload=-/usr/sbin/exportfs -r + + [Install] + WantedBy=multi-user.target diff --git a/nfs-utils.spec b/nfs-utils.spec new file mode 100644 index 0000000..452baa6 --- /dev/null +++ b/nfs-utils.spec @@ -0,0 +1,2187 @@ +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: 56%{?dist} +Epoch: 1 + +# group all 32bit related archs +%define all_32bit_archs i386 i486 i586 i686 athlon ppc sparcv9 + +Source0: https://www.kernel.org/pub/linux/utils/nfs-utils/%{version}/%{name}-%{version}.tar.xz +Source1: id_resolver.conf +Source2: lockd.conf +Source3: 24-nfs-server.conf +Source4: nfsconvert.py +Source5: nfsconvert.sh +Source6: nfs-convert.service + +# +# RHEL 8.0 +# +Patch001: nfs-utils-2.3.3-nfsd-disable-v4.patch +Patch002: nfs-utils-2.3.3-remove-osd_login.patch +Patch003: nfs-utils-2.3.3-mount-sharecache.patch +Patch004: nfs-utils-2.3.3-gssd-usegssproxy.patch +Patch005: nfs-utils-2.3.3-nfsconf-usegssproxy.patch +Patch006: nfs-utils-2.3.3-man-tcpwrappers.patch +Patch007: nfs-utils-2.3.3-junction-update.patch + +# +# RHEL 8.1 +# +Patch008: nfs-utils-2.3.3-nfsconf-manage-gids.patch +Patch009: nfs-utils-2.3.3-statd-force.patch +Patch010: nfs-utils-2.3.3-statd-no-notify.patch +Patch011: nfs-utils-2.3.3-gssd-verbose.patch +Patch012: nfs-utils-2.3.3-nfsconf-inplace.patch +Patch013: nfs-utils-2.3.3-covscan-resource-leaks.patch +Patch014: nfs-utils-2.3.3-nfsman-typo.patch +Patch015: nfs-utils-2.3.3-mount-fallback.patch +Patch016: nfs-utils-2.3.3-mountd-memleak.patch +Patch017: nfs-utils-2.3.3-lseek-error-handling.patch +Patch018: nfs-utils-2.3.3-memleak-on-error.patch + +# +# RHEL 8.2 +# +Patch019: nfs-utils-2.3.3-nfsiostat-err-cnts.patch +Patch020: nfs-utils-2.3.3-gssd-man-verbose.patch +Patch021: nfs-utils-2.3.3-nfsconf-rdmaport.patch +Patch022: nfs-utils-2.3.3-gssd-early-daemon.patch +Patch023: nfs-utils-2.3.3-covscan-rm-deadcode-leaks.patch +Patch024: nfs-utils-2.3.3-gssd-memoryleak.patch + +# +# RHEL 8.3 +# +Patch025: nfs-utils-2.3.3-junction-err-fix.patch +Patch026: nfs-utils-2.3.3-nfsdcld-upstream-update.patch +Patch027: nfs-utils-2.3.3-nconnect-manpage.patch +Patch028: nfs-utils-2.3.3-nfsdclddb-rename.patch +Patch029: nfs-utils-2.3.3-nfsclnts-cmd.patch + +# +# RHEL 8.4 +# +Patch030: nfs-utils-2.3.3-exportfs-man-labels.patch +Patch031: nfs-utils-2.3.3-nfsiostat-div-zero.patch +Patch032: nfs-utils-2.3.3-nfsiostat-key-error.patch +Patch033: nfs-utils-2.3.3-nfsdclddb-manpage-rename.patch +Patch034: nfs-utils-2.3.3-systemd-exportfs-nofail.patch +Patch035: nfs-utils-2.3.3-exports-manpage-outdated.patch +Patch036: nfs-utils-2.3.3-gssd-multithread-updates.patch +Patch037: nfs-utils-2.3.3-mountd-pseudofs.patch + +# +# RHEL 8.5 +# +Patch038: nfs-utils-2.3.3-gssd-k5identity.patch +Patch039: nfs-utils-2.3.3-gssd-man-tflag.patch +Patch040: nfs-utils-2.3.3-exportfs-root.patch +Patch041: nfs-utils-2.3.3-mount-sloppy.patch +Patch042: nfs-utils-2.3.3-gssd-failed-thread.patch +Patch043: nfs-utils-2.3.3-gssd-timeout-thread.patch +Patch044: nfs-utils-2.3.3-gssd-debug-cleanup.patch +Patch045: nfs-utils-2.3.3-gssd-mutex-refcnt.patch + +# +# RHEL 8.6 +# +Patch046: nfs-utils-2.3.3-mountd-v4-logging.patch +Patch047: nfs-utils-2.3.3-gssd-printerr.patch +Patch048: nfs-utils-2.3.3-mount-ebusy.patch +Patch049: nfs-utils-2.3.3-nfsidmap-debug.patch + +# +# RHEL 8.7 +# +Patch050: nfs-utils-2.3.3-nfsman-softreval.patch +Patch051: nfs-utils-2.3.3-rpcctl.patch +Patch052: nfs-utils-2.3.3-nfsrahead.patch +Patch053: nfs-utils-2.3.3-rpcctl-subparser.patch +Patch054: nfs-utils-2.3.3-rpcctl-posixpath.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 +Patch103: nfs-utils-2.3.1-systemd-gssproxy-restart.patch +Patch104: nfs-utils-2.3.1-systemd-svcgssd-removed.patch + +Provides: exportfs = %{epoch}:%{version}-%{release} +Provides: nfsstat = %{epoch}:%{version}-%{release} +Provides: showmount = %{epoch}:%{version}-%{release} +Provides: rpcdebug = %{epoch}:%{version}-%{release} +Provides: rpc.idmapd = %{epoch}:%{version}-%{release} +Provides: rpc.mountd = %{epoch}:%{version}-%{release} +Provides: rpc.nfsd = %{epoch}:%{version}-%{release} +Provides: rpc.statd = %{epoch}:%{version}-%{release} +Provides: rpc.gssd = %{epoch}:%{version}-%{release} +Provides: mount.nfs = %{epoch}:%{version}-%{release} +Provides: mount.nfs4 = %{epoch}:%{version}-%{release} +Provides: umount.nfs = %{epoch}:%{version}-%{release} +Provides: umount.nfs4 = %{epoch}:%{version}-%{release} +Provides: sm-notify = %{epoch}:%{version}-%{release} +Provides: start-statd = %{epoch}:%{version}-%{release} + +License: MIT and GPLv2 and GPLv2+ and BSD +Requires: rpcbind, sed, gawk, grep +Requires: kmod, keyutils, quota, python3-pyyaml +BuildRequires: libevent-devel libcap-devel +BuildRequires: libtirpc-devel libblkid-devel +BuildRequires: krb5-libs >= 1.4 autoconf >= 2.57 openldap-devel >= 2.2 +BuildRequires: automake, libtool, gcc, device-mapper-devel +BuildRequires: krb5-devel, libmount-devel, libxml2-devel +BuildRequires: sqlite-devel +BuildRequires: python3-devel +BuildRequires: systemd +BuildRequires: rpcgen +Requires(pre): shadow-utils >= 4.0.3-25 +Requires(pre): util-linux +Requires(pre): coreutils +Requires(preun): coreutils +Requires: libnfsidmap libevent +Requires: libtirpc >= 0.2.3-1 libblkid libcap libmount +%{?systemd_requires} +Requires: gssproxy => 0.7.0-3 + +%package -n libnfsidmap +Summary: NFSv4 User and Group ID Mapping Library +Provides: libnfsidmap%{?_isa} = %{epoch}:%{version}-%{release} +License: BSD +BuildRequires: pkgconfig, openldap-devel +BuildRequires: automake, libtool +Requires(postun): /sbin/ldconfig +Requires(pre): /sbin/ldconfig +Requires: openldap + +%description -n libnfsidmap +Library that handles mapping between names and ids for NFSv4. + +%package -n libnfsidmap-devel +Summary: Development files for the libnfsidmap library +Requires: libnfsidmap%{?_isa} = %{epoch}:%{version}-%{release} +Requires: pkgconfig + +%description -n libnfsidmap-devel +This package includes header files and libraries necessary for +developing programs which use the libnfsidmap library. + + +%description +The nfs-utils package provides various utilities for use with NFS +clients and servers. + +%prep +%autosetup -p1 + +# Remove .orig files +find . -name "*.orig" | xargs rm -f + +# Change shebangs +find -name \*.py -exec sed -r -i '1s|^#!\s*/usr/bin.*python.*|#!%{__python3}|' {} \; + +%build +sh -x autogen.sh +%define _statdpath /var/lib/nfs/statd +%configure \ + CFLAGS="%{build_cflags} -D_FILE_OFFSET_BITS=64 `pkg-config --cflags libtirpc`" \ + LDFLAGS="%{build_ldflags} `pkg-config --libs libtirpc`" \ + --enable-mountconfig \ + --enable-ipv6 \ + --with-statdpath=%{_statdpath} \ + --enable-libmount-mount \ + --with-systemd \ + --without-tcp-wrappers \ + --with-pluginpath=%{_libdir}/libnfsidmap \ + --enable-junction + +%make_build all + +%install +%define _pkgdir %{_prefix}/lib/systemd + +rm -rf $RPM_BUILD_ROOT/* + +mkdir -p $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT%{_sbindir} +mkdir -p $RPM_BUILD_ROOT%{_libexecdir}/nfs-utils/ +mkdir -p $RPM_BUILD_ROOT%{_pkgdir}/system +mkdir -p $RPM_BUILD_ROOT%{_pkgdir}/system-generators +mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man8 +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/request-key.d +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/ +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/gssproxy + +%make_install + +install -s -m 755 tools/rpcdebug/rpcdebug $RPM_BUILD_ROOT%{_sbindir} +install -m 644 utils/mount/nfsmount.conf $RPM_BUILD_ROOT%{_sysconfdir} +install -m 644 nfs.conf $RPM_BUILD_ROOT%{_sysconfdir} +install -m 644 support/nfsidmap/idmapd.conf $RPM_BUILD_ROOT%{_sysconfdir} +install -m 644 %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/request-key.d + +mkdir -p $RPM_BUILD_ROOT/usr/lib/systemd/scripts +install -m 644 %{SOURCE2} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/lockd.conf +install -m 644 %{SOURCE3} $RPM_BUILD_ROOT%{_sysconfdir}/gssproxy +install -m 755 %{SOURCE4} $RPM_BUILD_ROOT%{_sbindir}/nfsconvert +install -m 755 %{SOURCE5} $RPM_BUILD_ROOT/%{_libexecdir}/nfs-utils/nfsconvert.sh +install -m 644 %{SOURCE6} $RPM_BUILD_ROOT%{_pkgdir}/system + + +rm -rf $RPM_BUILD_ROOT%{_libdir}/*.{a,la} +rm -rf $RPM_BUILD_ROOT%{_libdir}/libnfsidmap/*.{a,la} + +mkdir -p $RPM_BUILD_ROOT%{_sharedstatedir}/nfs/rpc_pipefs + +touch $RPM_BUILD_ROOT%{_sharedstatedir}/nfs/rmtab +mv $RPM_BUILD_ROOT%{_sbindir}/rpc.statd $RPM_BUILD_ROOT/sbin + +mkdir -p $RPM_BUILD_ROOT%{_sharedstatedir}/nfs/statd/sm +mkdir -p $RPM_BUILD_ROOT%{_sharedstatedir}/nfs/statd/sm.bak +mkdir -p $RPM_BUILD_ROOT%{_sharedstatedir}/nfs/v4recovery +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/exports.d + + +%pre +# move files so the running service will have this applied as well +for x in gssd idmapd ; do + if [ -f /var/lock/subsys/rpc.$x ]; then + mv /var/lock/subsys/rpc.$x /var/lock/subsys/rpc$x + fi +done + +%define rpcuser_uid 29 +# Create rpcuser gid as long as it does not already exist +cat /etc/group | cut -d':' -f 1 | grep --quiet rpcuser 2>/dev/null +if [ "$?" -eq 1 ]; then + /usr/sbin/groupadd -g %{rpcuser_uid} rpcuser >/dev/null 2>&1 || : +fi + +# Create rpcuser uid as long as it does not already exist. +cat /etc/passwd | cut -d':' -f 1 | grep --quiet rpcuser 2>/dev/null +if [ "$?" -eq 1 ]; then + /usr/sbin/useradd -l -c "RPC Service User" -r -g %{rpcuser_uid} \ + -s /sbin/nologin -u %{rpcuser_uid} -d /var/lib/nfs rpcuser >/dev/null 2>&1 || : +else + /usr/sbin/usermod -u %{rpcuser_uid} -g %{rpcuser_uid} rpcuser >/dev/null 2>&1 || : +fi + +# Using the 16-bit value of -2 for the nfsnobody uid and gid +%define nfsnobody_uid 65534 + +# Create nfsnobody gid as long as it does not already exist +cat /etc/group | cut -d':' -f 3 | grep --quiet %{nfsnobody_uid} 2>/dev/null +if [ "$?" -eq 1 ]; then + /usr/sbin/groupadd -g %{nfsnobody_uid} nfsnobody >/dev/null 2>&1 || : +fi + +# Create nfsnobody uid as long as it does not already exist. +cat /etc/passwd | cut -d':' -f 3 | grep --quiet %{nfsnobody_uid} 2>/dev/null +if [ $? -eq 1 ]; then + /usr/sbin/useradd -l -c "Anonymous NFS User" -r -g %{nfsnobody_uid} \ + -s /sbin/nologin -u %{nfsnobody_uid} -d /var/lib/nfs nfsnobody >/dev/null 2>&1 || : +fi + +%post +if [ $1 -eq 1 ] ; then + # Initial installation + /bin/systemctl enable nfs-client.target >/dev/null 2>&1 || : + /bin/systemctl start nfs-client.target >/dev/null 2>&1 || : +fi +%systemd_post nfs-server + +%preun +if [ $1 -eq 0 ]; then + %systemd_preun nfs-client.target + %systemd_preun nfs-server.server + +fi + +%postun +%systemd_postun_with_restart nfs-client.target +%systemd_postun_with_restart nfs-server + +/bin/systemctl --system daemon-reload >/dev/null 2>&1 || : + +if [ $1 -eq 0 ] ; then + rm -rf /var/lib/nfs/statd + rm -rf /var/lib/nfs/v4recovery +fi + +%triggerin -- nfs-utils > 1:2.1.1-3 +/bin/systemctl try-restart gssproxy || : + +%files +%config(noreplace) /etc/nfsmount.conf +%dir %{_sysconfdir}/exports.d +%dir %{_sharedstatedir}/nfs/v4recovery +%dir %attr(555, root, root) %{_sharedstatedir}/nfs/rpc_pipefs +%dir %{_sharedstatedir}/nfs +%dir %{_libexecdir}/nfs-utils +%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,root,root) %{_statdpath}/state +%config(noreplace) %{_sharedstatedir}/nfs/etab +%config(noreplace) %{_sharedstatedir}/nfs/rmtab +%config(noreplace) %{_sysconfdir}/request-key.d/id_resolver.conf +%config(noreplace) %{_sysconfdir}/modprobe.d/lockd.conf +%config(noreplace) %{_sysconfdir}/nfs.conf +%attr(0600,root,root) %config(noreplace) /%{_sysconfdir}/gssproxy/24-nfs-server.conf +%doc linux-nfs/ChangeLog linux-nfs/KNOWNBUGS linux-nfs/NEW linux-nfs/README +%doc linux-nfs/THANKS linux-nfs/TODO +/sbin/rpc.statd +/sbin/nfsdcltrack +%{_sbindir}/exportfs +%{_sbindir}/nfsstat +%{_sbindir}/rpcdebug +%{_sbindir}/rpc.mountd +%{_sbindir}/rpc.nfsd +%{_sbindir}/showmount +%{_sbindir}/rpc.idmapd +%{_sbindir}/rpc.gssd +%{_sbindir}/sm-notify +%{_sbindir}/start-statd +%{_sbindir}/mountstats +%{_sbindir}/nfsiostat +%{_sbindir}/nfsidmap +%{_sbindir}/blkmapd +%{_sbindir}/nfsconf +%{_sbindir}/nfsref +%{_sbindir}/nfsconvert +%{_sbindir}/nfsdclddb +%{_sbindir}/nfsdcld +%{_sbindir}/nfsdclnts +%{_sbindir}/rpcctl +%{_libexecdir}/nfsrahead +%{_udevrulesdir}/99-nfs.rules +%{_mandir}/*/* +%{_pkgdir}/*/* + +%attr(4755,root,root) /sbin/mount.nfs +%attr(755,root,root) %{_libexecdir}/nfs-utils/nfsconvert.sh + +/sbin/mount.nfs4 +/sbin/umount.nfs +/sbin/umount.nfs4 + +%files -n libnfsidmap +%doc support/nfsidmap/AUTHORS support/nfsidmap/README support/nfsidmap/COPYING +%config(noreplace) %{_sysconfdir}/idmapd.conf +%{_libdir}/libnfsidmap.so.* +%{_libdir}/libnfsidmap/*.so + +%files -n libnfsidmap-devel +%{_libdir}/pkgconfig/libnfsidmap.pc +%{_includedir}/nfsidmap.h +%{_includedir}/nfsidmap_plugin.h +%{_libdir}/libnfsidmap.so + +%changelog +* Thu Jun 30 2022 Steve Dickson 2.3.3-56 +- rpcctl: 'PosixPath' object has no attribute 'readlink' (bz 2087187) + +* Mon Jun 27 2022 Steve Dickson 2.3.3-55 +- rpcctl: Remove subparser required option as that was added in py3.7 (bz 2087187) + +* Tue Jun 14 2022 Steve Dickson 2.3.3-54 +- Create the nfsrahead command (bz 1946283) + +* Tue May 31 2022 Steve Dickson 2.3.3-53 +- rpcctl: Add a rpcctl.py tool (bz 2087187) + +* Tue May 24 2022 Steve Dickson 2.3.3-52 +- manpage: Add a description of the softreval/nosoftreval (bz 2073476) + +* Mon Mar 7 2022 Steve Dickson 2.3.3-51 +- libnfsidmap: Turn off default verbosity (bz 2057612) + +* Sat Feb 19 2022 Steve Dickson 2.3.3-50 +- mount.nfs: Fix Typo auto negotiating code. (bz 1946346) + +* Mon Feb 14 2022 Steve Dickson 2.3.3-49 +- mount.nfs Fix error reporting for already mounted shares (bz 1946346) + +* Thu Nov 4 2021 Steve Dickson 2.3.3-48 +- gssd: fix crash in debug message (bz 1988283) + +* Tue Nov 2 2021 Steve Dickson 2.3.3-47 +- Enable logging for NFSv4 mount requests (bz 2004151) + +* Wed Jul 28 2021 Steve Dickson 2.3.3-46 +- mount.nfs: Fix the sloppy option processing (bz 1967883) + +* Thu Jul 22 2021 Steve Dickson 2.3.3-45 +- gssd: use mutex to protect decrement of refcount (bz 1511706) + +* Mon Jul 19 2021 Steve Dickson 2.3.3-44 +- gssd: Deal with failed thread creation (bz 1981400) +- gssd: Add timeout for upcall threads (bz 1981403) +- gssd: Cleaned up debug messages (bz 1961056) +- spec: Updated description of the nfs-utils rpm (bz 1981419) + +* Sat Jul 10 2021 Steve Dickson 2.3.3-43 +- mount.nfs: insert 'sloppy' at beginning of the options (bz 1967883) + +* Mon May 10 2021 Steve Dickson 2.3.3-42 +- gssd: Add options to allow for the use of ~/.k5identity file (bz 1868087) +- man: Correct gssd(8) description of rpc-timeout and context-timeout (bz 1908232) +- exportfs: fix unexporting of '/' (bz 1944119) + +* Wed Jan 20 2021 Steve Dickson 2.3.3-41 +- mountd: never root squash on the pseudofs (bz 1804912) + +* Mon Dec 14 2020 Steve Dickson 2.3.3-40 +- gssd: upstream multithreaded updates (bz 1906792) + +* Fri Dec 11 2020 Steve Dickson 2.3.3-39 +- systemd: Ingnore export failures in nfs-server.serivce unit (bz 1894873) +- exports.man: Remove some outdated verbiage (bz 1769688) + +* Thu Dec 10 2020 Steve Dickson 2.3.3-38 +- exports man page: warn about subdirectory exports (bz 1652437) +- Don't modify /etc/group on upgrades (bz 1856881) +- nfs-iostat: divide by zero with fresh mount (bz 1861823) +- nfsiostat: Drop autofs entries before calling compare_iostats() (bz 1859130) +- nfsdclddb: clddb-tool was recently renamed to nfsdclddb (bz 1893599) + +* Thu Dec 10 2020 Alice Mitchell 2.3.3-37 +- Remove manual enabling of nfs-convert (bz 1683895) + +* Fri Oct 9 2020 Alice Mitchell 2.3.3-36 +- Fix uninstall warnings (bz 1733170) + +* Wed Jun 10 2020 Steve Dickson 2.3.3-35 +- Fix dependency problems with nfsdclnts (bz 1841502) + +* Tue Jun 9 2020 Steve Dickson 2.3.3-34 +- New nfsdclnts command added (bz 1841502) + +* Mon May 18 2020 Steve Dickson 2.3.3-33 +- manpage: Add a description of the 'nconnect' mount option (bz 1761352) +- nfsdclddb: Redname clddb-tool to nfsdclddb (bz 1836924) + +* Wed May 6 2020 Steve Dickson 2.3.3-32 +- junction: Fixed debug statement (bz 1831829) +- Userspace support for the latest nfsdcld daemon (bz 1817756) + +* Fri Mar 6 2020 Steve Dickson 2.3.3-31 +- gssd: Closed a memory leak in find_keytab_entry() (bz 1809277) + +* Fri Feb 7 2020 Steve Dickson 2.3.3-30 +- Removed dead code that was flagged by a covscan (bz 1746572) + +* Thu Jan 16 2020 Steve Dickson 2.3.3-29 +- statd: Fix permission denied error path (bz 1776096) + +* Tue Nov 26 2019 Steve Dickson 2.3.3-28 +- gssd: daemonize earlier (bz 1762847) + +* Mon Nov 11 2019 Steve Dickson 2.3.3-27 +- More coverity scans updates (bz 1746572) +- nfsd: Adjust nfs.conf setting/parsing of rdma port (bz 1710532) +- Add plain --rdma option to nfs.conf convertor (bz 1747295) +- mountstats: Add per-op error counts to iostat command (bz 1719983) +- gssd: add configure options verbosity to man page (bz 1749642) + +* Wed Sep 18 2019 Steve Dickson 2.3.3-26 +- Updated coverity scans patch to not do a double free (bz 1752326) + +* Mon Aug 19 2019 Steve Dickson 2.3.3-25 +- Change the owner/group of the state file (bz 1733445) + +* Mon Aug 12 2019 Steve Dickson 2.3.3-24 +- nfs.man: Fixed small typo in man page (bz 1732877) +- mount: Report correct error in the fall_back cases (bz 1709963) +- rpc.mountd: Fix e_hostname and e_uuid leaks (bz 1712202) +- spec: Remove redundant manpage files (bz 1718738) + +* Wed Jul 31 2019 Alice Mitchell 2.3.3-23 +- Fix memory leak on error (bz 1440524) +- Fix error handling on lseek (bz 1733887) + +* Thu Jul 18 2019 Alice Mitchell 2.3.3-22 +- Revert the forced chmod of nfs.conf as unneccessary (bz 1687496) + +* Mon Jul 15 2019 Steve Dickson 2.3.3-21 +- Gating tests: run tests from tests namespace (bz 1653927) + +* Tue Jul 9 2019 Steve Dickson 2.3.3-20 +- Gating tests: Fix _env data and source it in every test run (bz 1653927) + +* Fri May 3 2019 Steve Dickson 2.3.3-19 +- Removed resource leaks found by coverity scans (bz 1602633) + +* Thu Apr 25 2019 Steve Dickson 2.3.3-18 +- Modify nfs.conf in-place instead of replacing the file (bz 1687496) + +* Tue Mar 19 2019 Steve Dickson 2.3.3-17 +- Moved the gating tests out of a patch and into the top dir (bz 1653927) +- Move the mode corrections on /etc/nfs.conf to nfsconvert.py (bz 1655880) +- gssd: add verbosity options to the rpc.gssd man page (bz 1668026) + +* Fri Mar 8 2019 Steve Dickson 2.3.3-16 +- Add a conversion for new sm-notify force option in nfs.conf (bz 1677576) +- Correct the modes on /etc/nfs.conf after a conversion (bz 1655880) + +* Tue Mar 5 2019 Steve Dickson 2.3.3-15 +- nfs.conf: Fixed manage-gids option typo (bz 1672395) +- sm-notify: Added -f flag to nfs.conf parsing (bz 1677576) +- Add nfs.conf equivalent for the statd --no-notify cmdline option (bz 1683714) + +* Wed Feb 13 2019 Steve Dickson 2.3.3-14 +- Fix typo in checking for the 65534 uid/gid (bz 1655960) + +* Tue Feb 12 2019 Steve Dickson 2.3.3-13 +- Always have the nfs-convert service enabled (bz 1673685) + +* Sat Feb 9 2019 Steve Dickson 2.3.3-12 +- Change nfsconvert.sh not to set the immutable bit (bz 1673685) +- Change nfsconvert.py not to create the new dummy /etc/sysconfig/nfs (bz 1673685) + +* Sat Feb 9 2019 Steve Dickson 2.3.3-11 +- Do not install /etc/sysconfig/nfs (bz 1673685) + +* Fri Jan 25 2019 Steve Dickson 2.3.3-10 +- Only create nfsnobody when uid 65534 does not exist (bz 1655960) + +* Wed Jan 23 2019 Steve Dickson 2.3.3-9 +- Critical component nfs-utils requires tests for gating (bz 1653927) + +* Wed Dec 12 2018 Steve Dickson 2.3.3-8 +- Update junction code with latest upstream code (1543126) +- Make sure /etc/sysconfig/nfs is immutabl (1639432) + +* Tue Nov 6 2018 Steve Dickson 2.3.3-7 +- Use systemd scripts to convert NFS configurations (bz 1646626) + +* Fri Oct 26 2018 Steve Dickson 2.3.3-6 +- Changed /var/lib/nfs/rpc_pipefs to have 555 permissions (bz 1583489) +- Removed tcp wrappers support from man pages (bz 1642596) +- Reload not restart gssproxy in nfs-server.service (bz 1592660) + +* Thu Oct 25 2018 Steve Dickson 2.3.3-5 +- mount.nfs: Add braces around EBUSY code (bz 1629644) + +* Mon Oct 22 2018 Steve Dickson 2.3.3-4 +- Deprecated /etc/sysconfig/nfs (bz 1639432) + +* Sat Oct 20 2018 Steve Dickson 2.3.3-3 +- Ensure /var/lib/nfs/rpc_pipefs has the correct permissions (bz 1583489) +- mount.nfs: Only ignore EBUSY when a filesystem is already mount (bz 1629644) + +* Fri Oct 19 2018 Steve Dickson 2.3.3-2 +- Enable NFS basic junction support (bz 1543126) +- Removed osd_login (bz 1636434) + +* Fri Oct 5 2018 Steve Dickson 2.3.3-1 +- nfs.conf: fail to disable major NFS version 4 using "vers4=n" (bz 1624319) + +* Thu Sep 13 2018 Steve Dickson 2.3.3-0 +- Updated to the latest upstream release: nfs-utils-2-3-3 (bz 1543126) + +* Fri Sep 7 2018 Steve Dickson 2.3.1-8.rc1 +- Ensure /var/lib/nfs/rpc_pipefs has the correct permissions (bz 1583489) +- Remove rpc.svcgssd from systemd scripts (bz 1591700) + +* Thu May 3 2018 Steve Dickson 2.3.1-8.rc1 +- nfsd: Set default minor versions (bz 1570066) + +* Wed Apr 11 2018 Steve Dickson 2.3.1-7.rc1 +- mount.nfs: Fix auto protocol negotiation (bz 1565310) + +* Mon Apr 9 2018 Steve Dickson 2.3.1-6.rc1 +- Stop failing when systemctl try-restart gssproxy fails (bz 1552976) +- Use Fedora build flags (bz 1548679) + +* Fri Mar 2 2018 Steve Dickson 2.3.1-5.rc1 +- Updated to latest upstream RC release: nfs-utils-2-3-2-rc1 + +* Wed Feb 14 2018 Kevin Fenzi - 1:2.3.1-5 +- Rebuild for new libevent + +* Fri Feb 09 2018 Igor Gnatenko - 1:2.3.1-4 +- Escape macros in %%changelog + +* Thu Feb 08 2018 Fedora Release Engineering - 1:2.3.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Fri Jan 19 2018 Steve Dickson 2.3.1-2 +- Restart gssproxy (if running) when the NFS server is started (bz 1527653) + +* Tue Jan 9 2018 Steve Dickson 2.3.1-1 +- Added rpcgen dependency (bz 1531540) + +* Wed Dec 20 2017 Steve Dickson 2.3.1-0 +- Updated to latest upstream release: 2.3.1 +- Removed unnecessary chown rpcuser in %%post + +* Tue Dec 19 2017 Steve Dickson 2.2.1-3.rc2 +- Fix typo in nfs-utils_env.sh (bz 1516004) + +* Mon Dec 18 2017 Steve Dickson 2.2.1-2.rc2 +- Removed tcp_wrappers dependency (bz 1518769) + +* Fri Dec 15 2017 Steve Dickson 2.2.1-1.rc2 +- Updated to latest upstream RC release: nfs-utils-2-2-2-rc2 + +* Tue Nov 07 2017 Igor Gnatenko - 1:2.2.1-1.rc1.1 +- Remove old crufty coreutils requires + +* Mon Nov 6 2017 Steve Dickson 2.2.1-1.rc1 +- Restore ABI compat with pre-merge libnfsidmap (bz 1509063) +- Add a build-time dependency on python3-devel + +* Thu Nov 2 2017 Steve Dickson 2.2.1-0.rc1 +- Updated to latest upstream RC release: nfs-utils-2-2-2-rc1 + +* Mon Oct 30 2017 Steve Dickson 2.2.1-0 +- Updated to latest upstream release: nfs-utils-2-2-1 + +* Mon Oct 16 2017 Steve Dickson 2.1.1-8.rc6 +- Own the /usr/libexec/nfs-utils dir (bz 1484300) + +* Thu Oct 5 2017 Steve Dickson 2.1.1-7.rc6 +- Updated to the latest RC releease: nfs-utils-2-1-2-rc6 + +* Tue Sep 19 2017 Orion Poplawski 2.1.1-7.rc5 +- Fix URL + +* Thu Jul 27 2017 Steve Dickson 2.1.1-6.rc5 +- Updated to the latest RC releease: nfs-utils-2-1-2-rc5 + +* Wed Jul 26 2017 Fedora Release Engineering - 1:2.1.1-6.rc4.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Jul 19 2017 Steve Dickson 2.1.1-6.rc4 +- Removed build dependency on fedfs-utils-devel (bz 1427493) + +* Wed Jun 21 2017 Steve Dickson 2.1.1-5.rc4 +- Updated to the latest RC releease: nfs-utils-2-1-2-rc4 (bz 1462218) +- rpc.c: added include file so UINT16_MAX is defined + +* Thu Jun 1 2017 Steve Dickson 2.1.1-5.rc3 +- Updated to the latest RC releease: nfs-utils-2-1-2-rc3 (bz 1457921) + +* Wed Apr 26 2017 Steve Dickson 2.1.1-5.rc2 +- Conditionally restart gssproxy now that config file is installed (bz 1440885) +- systemd: Afters are also needed for the Wants=network-online.target (bz 1419351) +- nfsdcltrack: silence some expected errors (bz 1445863) +- Cleaned up fuzzy patches + +* Mon Apr 10 2017 Steve Dickson 2.1.1-4.rc2 +- Updated to the latest RC release: nfs-utils-2-1-2-rc2 (bz 1419351) +- Fixed typo in nfs.sysconfig (bz 1422249) + +* Tue Mar 28 2017 Steve Dickson 2.1.1-3.rc1 +- Added gssproxy server config file (bz 1431272) + +* Wed Feb 15 2017 Steve Dickson 2.1.1-2.rc1 +- Updated to the latest RC release: nfs-utils-2-1-2-rc1 + +* Fri Feb 10 2017 Fedora Release Engineering - 1:2.1.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Jan 19 2017 Steve Dickson 2.1.1-1 +- Added back the nfs-config service for backwards compatibility + +* Thu Jan 19 2017 Steve Dickson 2.1.1-0 +- Updated to latest upstream release: nfs-utils-2-1-1 (bz 1413232) + +* Mon Dec 19 2016 Miro Hronok - 1:1.3.4-1.rc3.1 +- Rebuild for Python 3.6 + +* Wed Nov 23 2016 Steve Dickson 1.3.4-1.rc3 +- Updated to the latest RC release: nfs-utils-1-3-5-rc3 + +* Thu Aug 25 2016 Steve Dickson 1.3.4-1.rc2 +- Updated to the latest RC release: nfs-utils-1-3-5-rc2 (bz 1369714) + +* Mon Aug 22 2016 Steve Dickson 1.3.4-1.rc1 +- Stop removing users and groups (bz 1364836) + +* Sat Aug 20 2016 Steve Dickson 1.3.4-0.rc1 +- Updated to the latest RC release: nfs-utils-1-3-5-rc1 +- Updated the Requires(pre) (bz 1319196) + +* Sat Aug 6 2016 Steve Dickson 1.3.4-0 +- Updated to latest upstream version 1.3.4 + +* Thu Jul 21 2016 Steve Dickson 1.3.3-9.rc6 +- Removed the rpc-svcgssd.service systemd file (bz 1334741) + +* Tue Jul 19 2016 Steve Dickson 1.3.3-8.rc6 +- Updated to the latest RC release: nfs-utils-1-3-4-rc6 + +* Mon May 2 2016 Steve Dickson 1.3.3-8.rc5 +- Updated to the latest RC release: nfs-utils-1-3-4-rc5 + +* Wed Mar 16 2016 Steve Dickson 1.3.3-7.rc4 +- Updated to the latest RC release: nfs-utils-1-3-4-rc4 (bz 1316701) +- Changed BuildRequires: glibc-headers ==> BuildRequires: gcc (bz 1230477) + +* Thu Feb 04 2016 Fedora Release Engineering - 1:1.3.3-6.rc3.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Sat Jan 16 2016 Steve Dickson 1.3.3-6.rc3 +- Updated to the latest RC release: nfs-utils-1-3-4-rc3 + +* Wed Dec 16 2015 Steve Dickson 1.3.3-6.rc2 +- mountd: fix netgroup lookup for short hostnames (bz 1284079) + +* Fri Dec 11 2015 Steve Dickson 1.3.3-5.rc2 +- Updated to the latest RC release: nfs-utils-1-3-4-rc2 + +* Tue Nov 17 2015 Steve Dickson 1.3.3-4.rc1 +- ghost-ed rpc.statd state file (bz 1158466) + +* Mon Nov 16 2015 Steve Dickson 1.3.3-3.rc1 +- Improving rpc.gssd's debugging (bz 1282600) + +* Mon Nov 16 2015 Steve Dickson 1.3.3-2.rc1 +- Decouple the starting of nfs-service and rpcbind (bz 1279526) + +* Tue Nov 10 2015 Fedora Release Engineering - 1:1.3.3-1.rc1.1 +- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5 + +* Wed Nov 4 2015 Steve Dickson 1.3.3-1.rc1 +- Updated to the latest RC release: nfs-utils-1-3-4-rc1 + +* Tue Nov 3 2015 Steve Dickson 1.3.3-1 +- Removed legacy triggers to remove rpm build errors + +* Mon Nov 2 2015 Steve Dickson 1.3.3-0 +- Updated to latest upstream version 1.3.3 + +* Fri Jul 31 2015 Steve Dickson 1.3.2-12 +- Fixed return value being overrun in gssd (bz 1249046) + +* Mon Jul 13 2015 Miro Hronok - 1:1.3.2-11 +- Replace Python scripts shebangs with %%{__python3} and require python3-devel to have that + +* Fri Jun 26 2015 Steve Dickson 1.3.2-10 +- Update to latest RC release: nfs-utils-1-3-3-rc5 (bz 1233005) + +* Wed Jun 17 2015 Fedora Release Engineering - 1:1.3.2-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed Jun 10 2015 Steve Dickson 1.3.2-8 +- Make systemd args backwards compatible (bz 1210751) +- Stop scribbling on stderr (bz 1211008) + +* Tue May 12 2015 Colin Walters - 1:1.3.2-7 +- Add patch to fix initial start on OSTree managed systems (bz 1219871) + +* Thu May 7 2015 Steve Dickson 1.3.2-4 +- Update to latest RC release: nfs-utils-1-3-3-rc4 + +* Mon Apr 6 2015 Steve Dickson 1.3.2-3 +- Update to latest RC release: nfs-utils-1-3-3-rc3 + +* Thu Apr 2 2015 Steve Dickson 1.3.2-2.1 +- Removed RPCRQUOTADOPTS from /etc/sysconfig/nfs (bz 1208516) + +* Thu Mar 19 2015 Steve Dickson 1.3.2-2.0 +- Update to latest RC release: nfs-utils-1-3-3-rc2 (bz 1088665) + +* Thu Feb 26 2015 Steve Dickson 1.3.2-1.0 +- Update to latest RC release: nfs-utils-1-3-3-rc1 + +* Mon Feb 9 2015 Steve Dickson 1.3.2-0.2 +- Change statd-notify.service to not wait for network to come up (bz 1183293) +- Added the rpcuser group before adding the rpcuser uid (bz 1165322) + +* Sun Feb 1 2015 Steve Dickson 1.3.2-0.1 +- statd: Fix test for foreground mode (bz 1188040) + +* Sat Jan 31 2015 Steve Dickson 1.3.2-0.0 +- Updated to latest upstream release: 1-3-2 + +* Mon Jan 19 2015 Steve Dickson 1.3.1-6.0 +- Set the GSS_USE_PROXY variable in nfs-utils_env.sh (bz 1183787) + +* Thu Jan 15 2015 Steve Dickson 1.3.1-5.0 +- Updated to latest upstream RC release: nfs-utils-1-3-2-rc5 (bz 1181708) + +* Wed Jan 14 2015 Steve Dickson 1.3.1-4.2 +- Added SMNOTIFYARGS to /etc/sysconf/nfs (bz 1182227) + +* Fri Jan 2 2015 Steve Dickson 1.3.1-4.1 +- Change if statments to string comparisons in nfs-utils_env.sh (bz 1170354) + +* Sat Dec 13 2014 Steve Dickson 1.3.1-4.0 +- Updated to latest upstream RC release: nfs-utils-1-3-2-rc4 (bz 1164477) +- Handle the rpcuser like other created users (bz 1165322) +- Restored lockd port and v4 grace/lease interface (bz 1115225) +- Make sure nfs-client target is enabled (bz 1173564) + +* Wed Dec 3 2014 Steve Dickson 1.3.1-2.4 +- Fixed typos in nfs-utils sysconfig files (bz 1170354) + +* Thu Nov 13 2014 Steve Dickson 1.3.1-2.3 +- Fixed a mount DOS (bz 1163886) + +* Thu Nov 6 2014 Richard W.M. Jones 1.3.1-2.2 +- Rebuild against new libnfsimap (bz 1160883) + +* Thu Nov 6 2014 Steve Dickson 1.3.1-2.1 +- Rebuild against new libnfsimap (bz 1160883) + +* Tue Nov 4 2014 Steve Dickson 1.3.1-2.0 +- Updated to latest upstream RC release: nfs-utils-1-3-2-rc2 (bz 1115179) + +* Tue Nov 4 2014 Steve Dickson 1.3.1-1.2 +- Rebuild with new glibc (bz 1158846) + +* Fri Oct 24 2014 Steve Dickson 1.3.1-1.1 +- Added fix to umount in the nfs-utils-1.3.2-rc1.patch + +* Wed Oct 22 2014 Steve Dickson 1.3.1-1.0 +- Updated to latest upstream RC release: nfs-utils-1-3-2-rc1 (bz 1142842) + +* Thu Sep 25 2014 Steve Dickson 1.3.1-0.0 +- Update to the latest upstream release: nfs-utils-1-3-1 +- Enable gssproxy to manage the GSSAPI creds on the server. + +* Fri Sep 12 2014 Steve Dickson 1.3.0-7.0 +- Updated to latest upstream RC release: nfs-utils-1-3-1-rc4 (bz 1108615) + +* Sun Aug 17 2014 Fedora Release Engineering - 1:1.3.0-6.0 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sun Jul 27 2014 Steve Dickson 1.3.0-5.0 +- Updated to latest upstream RC release: nfs-utils-1-3-1-rc3 +- Use _statdpath to define where statd's state lives + +* Tue Jul 01 2014 Jeff Layton - 1:1.3.0-4.0 +- clean up lockd configuration + +* Sat Jun 07 2014 Fedora Release Engineering - 1:1.3.0-3.0 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Sun Jun 1 2014 Steve Dickson 1.3.0-2.0 +- Updated to latest upstream RC release: nfs-utils-1-3-1-rc2 + +* Sat May 24 2014 Steve Dickson 1.3.0-1.3 +- Use systemd_post macro to enable services (bz 1087950) + +* Thu May 1 2014 Steve Dickson 1.3.0-1.2 +- mountd: fix segfault in add_name with newer gcc compilers + +* Thu May 1 2014 Steve Dickson 1.3.0-1.1 +- start-statd: rpc.statd's systemd unit changed names + +* Wed Apr 30 2014 Steve Dickson 1.3.0-1.0 +- Updated to latest upstream RC release: nfs-utils-1-3-1-rc1 + +* Fri Apr 25 2014 Steve Dickson 1.3.0-0.2 +- Fix PATH problem in start-statd (bz 1088226) + +* Mon Apr 14 2014 Steve Dickson 1.3.0-0.1 +- Incorporated new upstream systemd units + +* Tue Mar 25 2014 Steve Dickson 1.3.0-0.0 +- Updated to latest major release: nfs-utils-1-3-0 + +* Wed Jan 22 2014 Steve Dickson 1.2.9-3.0 +- Updated to latest upstream RC release: nfs-utils-1-2-10-rc3 + - gssd: Improve first attempt at acquiring GSS credentials (bz 1055077) +- gssd: set $HOME to prevent recursion (bz 1052902) + +* Fri Jan 10 2014 Steve Dickson 1.2.9-2.1 +- Fixed typo in nfs-service file. (bz 1047972) + +* Wed Jan 8 2014 Steve Dickson 1.2.9-2.0 +- Updated to latest upstream RC release: nfs-utils-1-2-10-rc2 +- Added Also=nfs.target to nfs-service file. (bz 1047972) + +* Wed Nov 20 2013 Steve Dickson 1.2.9-1.0 +- Updated to latest upstream RC release: nfs-utils-1-2-10-rc1 + +* Tue Nov 5 2013 Steve Dickson 1.2.9-0.0 +- Updated to latest upstream Release: nfs-utils-1-2-9 + +* Tue Sep 24 2013 Steve Dickson 1.2.8-6.0 +- Updated to latest upstream RC release: nfs-utils-1-2-9-rc6 + +* Wed Sep 18 2013 Steve Dickson 1.2.8-5.0 +- Updated to latest upstream RC release: nfs-utils-1-2-9-rc5 + +* Thu Aug 22 2013 Steve Dickson 1.2.8-4.1 +- nfs-utils: fix a number of specfile problems + +* Mon Aug 19 2013 Steve Dickson 1.2.8-4.0 +- Updated to latest upstream RC release: nfs-utils-1-2-9-rc4 + +* Sat Aug 03 2013 Fedora Release Engineering - 1:1.2.8-4.0 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jul 23 2013 Steve Dickson 1.2.8-3.0 +- Updated to latest upstream RC release: nfs-utils-1-2-9-rc3 + +* Tue Jul 23 2013 Steve Dickson 1.2.8-2.1 +- Make sure nfs.target is enabled (bz 970595) +- Fix nfs server reloads (bz 951247) + +* Fri May 31 2013 Steve Dickson 1.2.8-2.0 +- Update to latest upstream RC release: nfs-utils.1.2.9-rc1 +- Added GSS_USE_PROXY variable to nfs.sysconfig (bz 967112) + +* Tue May 7 2013 Steve Dickson 1.2.8-1.1 + systemd: nfs-server.service needs to be split up (bz 769879) + +* Tue May 7 2013 Steve Dickson 1.2.8-1 +- Updated to the latest upstream RC release: nfs-utils.1.2.9-rc1 + +* Tue Apr 23 2013 Steve Dickson 1.2.8-0 +- Updated to latest upstream release: 1.2.8 +- Removed the libgssglue dependency + +* Mon Apr 1 2013 Steve Dickson 1.2.7-6 +- Added v4.1 support rpc.nfsd (bz 947073) + +* Mon Mar 25 2013 Steve Dickson 1.2.7-5 +- Updated to latest upstream RC release: nfs-utils.1.2.8-rc4 +- Added nfs-lock.service to After line in nfs-server.service (bz 914792) + +* Thu Feb 14 2013 Fedora Release Engineering - 1:1.2.7-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Wed Jan 16 2013 Steve Dickson 1.2.7-3 +- Updated to latest upstream RC release: nfs-utils.1.2.8-rc3 +- Took ownership of /usr/lib/nfs-utils (bz 894535) + +* Mon Dec 17 2012 Steve Dickson 1.2.7-2 +- Update to latest upstream RC release: nfs-utils.1.2.8-rc2 + +* Wed Nov 28 2012 Steve Dickson 1.2.7-1 +- Update to latest upstream RC release: nfs-utils.1.2.8-rc1 + +* Fri Nov 9 2012 Steve Dickson 1.2.7-0 +- Updated to latest upstream release: nfs-utils.1.2.7 + +* Thu Nov 8 2012 Steve Dickson 1.2.6-14 +- Allow the service to start when RPCNFSDCOUNT is comment out. (bz 870143) +- Removed some old cruft from the spec file (bz 226198) + +* Mon Oct 15 2012 Steve Dickson 1.2.6-13 +- Added a Requires for the quota package (bz 866225) + +* Thu Aug 23 2012 Steve Dickson 1.2.6-12 +- Added FedFS support by added a BuildRequires for fedfs-utils-devel +- Introduce new systemd-rpm macros (bz 850227) +- Updated to latest upstream RC release: nfs-utils.1.2.7-rc5 (bz 833024) + +* Mon Aug 6 2012 Steve Dickson 1.2.6-11 +- Updated to latest upstream RC release: nfs-utils.1.2.7-rc4 + +* Thu Aug 2 2012 Steve Dickson 1.2.6-10 +- Removed modprobe.d/nfs.conf + +* Thu Jul 19 2012 Steve Dickson 1.2.6-9 +- Updated to latest upstream RC release: nfs-utils.1.2.7-rc3 + +* Thu Jul 5 2012 Steve Dickson 1.2.6-8 +- nfsidmap: default domain no being set (bz 829362) + +* Fri Jun 22 2012 Steve Dickson 1.2.6-7 +- Reworked how the legacy names are enabled in systemd +- Fixed typo in nfs-mountd.service + +* Tue Jun 12 2012 Steve Dickson 1.2.6-6 +- Updated to latest upstream RC release: nfs-utils.1.2.7-rc2 (bz 833555) + +* Tue Jun 12 2012 Steve Dickson 1.2.6-5 +- Reworked how the services are restarted. + +* Tue Jun 12 2012 Steve Dickson 1.2.6-4 +- Enable legacy service names. + +* Tue May 29 2012 Steve Dickson 1.2.6-3 +- Updated to latest upstream RC release: nfs-utils.1.2.7-rc1 + +* Tue May 29 2012 Steve Dickson 1.2.6-2 +* Fixed typo in the checking of nfsnobody (bz 816149) + +* Fri May 25 2012 Steve Dickson 1.2.6-1 +- Correctly search for the existence of nfsnobody (bz 816149) +- Correctly change the default group id for nfsnobody (bz 816149) + +* Tue May 15 2012 Steve Dickson 1.2.6-0 +- Update to the latest upstream release: nfs-utils-1.2.6 (bz 821673) +- Split out NFS server daemons into individual service files (bz 769879) +- Removed Wants= from nfs-lock.service (bz 817895) +- Only enable services if they are enabled on upgrades (bz 807020) + +* Thu May 3 2012 Steve Dickson 1.2.5-16 +- Update to the latest RC release: nfs-utils-1.2.6-rc7 + +* Thu Apr 26 2012 Josh Boyer 1.2.5-15 +- Add modprobe config file to alias 'nfs4' to 'nfs' (bz 806333) + +* Thu Mar 22 2012 Steve Dickson 1.2.5-14 +- gssd: Look for user creds in user defined directory (bz 786993) +- gssd: Don't link with libgssapi_krb5 (bz 784908) + +* Fri Mar 16 2012 Steve Dickson 1.2.5-13 +- Make sure statd is start before NFS mounts (bz 786050) +- rpc.idmap: Hide global symbols from libidmap plugins (bz 797332) +- nfsd: Bump up the default to 8 nprocs (bz 757452) + +* Wed Feb 08 2012 Harald Hoyer 1.2.5-12 +- require kmod instead of modutils (bz 788571) + +* Mon Jan 16 2012 Steve Dickson 1.2.5-11 +- Update to upstream RC release: nfs-utils-1.2.6-rc6 +- Reworked how the nfsd service requires the rpcbind service (bz 768550) + +* Mon Jan 9 2012 Steve Dickson 1.2.5-10 +- Added back the SUID bits on mount commands (bz 772396) +- Added a decency on keyutils (bz 769724) + +* Thu Jan 5 2012 Steve Dickson 1.2.5-9 +- Update to upstream RC release: nfs-utils-1.2.6-rc5 + +* Thu Dec 15 2011 Steve Dickson 1.2.5-8 +- Removed the nfs-idmap service. rpc.idmap is now part of + the nfs-server service + +* Tue Dec 13 2011 Steve Dickson 1.2.5-7 +- Enabled new idmaping by installing the id_resolver.conf file. +- Update to upstream RC release: nfs-utils-1.2.6-rc4 + +* Fri Nov 18 2011 Steve Dickson 1.2.5-6 +- Remove RQUOTAD_PORT and RQUOTAD from /etc/sysconfig/nfs (bz 754496) +- Ensured nfs-idmap service is started after the named is up (bz 748275) + +* Mon Nov 14 2011 Steve Dickson 1.2.5-5 +- Ensured nfs-idmap service is started after the network up (bz 748275) +- Update to upstream RC release: nfs-utils-1.2.6-rc3 (bz 746497) + +* Thu Oct 20 2011 Steve Dickson 1.2.5-4 +- Added pNFS debugging to rpcdebug. + +* Tue Oct 18 2011 Steve Dickson 1.2.5-3 +- Update to upstream RC release: nfs-utils-1.2.6-rc2 + +* Tue Oct 4 2011 Steve Dickson 1.2.5-2 +- Removed SUID bits on mount commands (bz 528498) +- Fixed a few typos in a couple man pages (bz 668124, 673818, 664330) +- Fixed a I/0 problem in rpc.idmapd (bz 684308) + +* Mon Oct 3 2011 Steve Dickson 1.2.5-1 +- Update to upstream RC release: nfs-utils-1.2.6-rc1 +- Added named.service to After list in nfs-server.service (bz 742746) + +* Tue Sep 27 2011 Steve Dickson 1.2.5-0 +- Update to upstream release: nfs-utils-1.2.5 (bz 717931) + +* Wed Sep 21 2011 Steve Dickson 1.2.4-11 +- Update to upstream RC release: nfs-utils-1.2.5-rc3 + +* Wed Sep 14 2011 Steve Dickson 1.2.4-10 +- Created /etc/exports.d to stop a warning (bz 697006) + +* Tue Aug 30 2011 Steve Dickson 1.2.4-9 +- Both the nfs.lock and nfs.idmap services should always + enabled on both installs and upgrades (bz 699040) +- Fixed the paths to the server scriptlets (bz 733531) + +* Mon Aug 29 2011 Steve Dickson 1.2.4-8 +- Update to upstream RC release: nfs-utils-1.2.5-rc2 + +* Wed Aug 24 2011 Steve Dickson 1.2.4-7 +- Added StandardError=syslog+console to all the service files + so startup errors will be logged. +- Changed exportfs to only log errors on existing /etc/export.d + directory, which eliminates a needless syslog entry. +- Automount /proc/fs/nfsd for rpc.nfsd + +* Wed Aug 10 2011 Steve Dickson 1.2.4-6 +- Fixed some bugs in the triggerun script as well in + the nfs-server scripts (bz 699040). + +* Wed Aug 3 2011 Steve Dickson 1.2.4-5 +- Cleaned up the .preconfig and .postconfig files per + code review request. + +* Wed Aug 3 2011 Steve Dickson 1.2.4-4 +- Converted init scrips to systemd services. (bz 699040) +- Made nfsnobody's uid/gid to always be a 16-bit value of -2 +- mount: fix for libmount from util-linux >= 2.20 + +* Thu Jul 21 2011 Steve Dickson 1.2.4-3 +- Updated to latest upstream release: nfs-utils-1-2-5-rc1 + +* Thu Jul 7 2011 Ville Skyttä - 1:1.2.4-2 +- Don't ship Makefiles or INSTALL in docs (#633934). + +* Mon Jul 4 2011 J. Bruce Fields 1.2.4-1 +- Rely on crypto module autoloading in init scripts +- initscripts: just try to mount rpc_pipefs always + +* Wed Jun 29 2011 Steve Dickson 1.2.4-0 +- Updated to latest upstream release: nfs-utils-1-2-4 + +* Wed Apr 20 2011 Steve Dickson 1.2.3-13 +- Updated to latest upstream release: nfs-utils-1-2-4-rc8 + +* Wed Apr 6 2011 Steve Dickson 1.2.3-12 +- Updated to latest upstream release: nfs-utils-1-2-4-rc7 +- Enabled the libmount code. + +* Mon Mar 7 2011 Steve Dickson 1.2.3-11 +- Updated to latest upstream release: nfs-utils-1-2-4-rc6 + +* Wed Feb 09 2011 Christopher Aillon - 1.2.3-10 +- Rebuild against newer libevent + +* Tue Feb 08 2011 Fedora Release Engineering - 1:1.2.3-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Wed Jan 26 2011 Steve Dickson 1.2.3-8 +- Fixed segfault in rpc.mountd (bz 669065) + +* Fri Jan 14 2011 Steve Dickson 1.2.3-7 +- Updated to latest upstream release: nfs-utils-1-2-4-rc5 +- Add initscripts changes needed for rpcbind to be running when nfs is started +- Initscripts changes needed to support NFS over RDMA +- Allow the setting of the NFSv4 grace period (bz 665387) + +* Mon Dec 13 2010 Steve Dickson 1.2.3-6 +- Updated to latest upstream release: nfs-utils-1-2-4-rc4 + +* Wed Dec 8 2010 Steve Dickson 1.2.3-5 +- Replace the nfs-utils-lib dependency with a libnfsidmap + dependency + +* Wed Dec 1 2010 Steve Dickson 1.2.3-4 +- The nfs service is not stopped on reboot or halt (bz 652786) +- Removed obsolete configuration values (bz 653765) + +* Mon Nov 29 2010 Steve Dickson 1.2.3-3 +- Updated to latest upstream release: nfs-utils-1-2-4-rc3 + +* Fri Oct 15 2010 Steve Dickson 1.2.3-2 +- Initscripts do not conform to LSB specification (bz 621562) +- sm-notify needs to call res_init() before each try (bz 625531) +- exports(5) man page duplicated paragraphs (bz 590921) + +* Thu Oct 14 2010 Steve Dickson 1.2.3-1 +- Updated to latest upstream release: nfs-utils-1-2-4-rc1 + +* Mon Oct 4 2010 Steve Dickson 1.2.3-0.1 +- Fixed a regession with -p arguemnt to rpc.mountd + +* Thu Sep 30 2010 Steve Dickson 1.2.3-0 +- Updated to latest upstream release: nfs-utils-1-2-3 + +* Thu Sep 16 2010 Steve Dickson 1.2.2-8 +- Update to upstream RC release: nfs-utils-1-2-3-rc6 + +* Thu Sep 9 2010 Steve Dickson 1.2.2-7 +- Update to upstream RC release: nfs-utils-1-2-3-rc5 + +* Tue Jun 22 2010 Steve Dickson 1.2.2-6 +- Update to upstream RC release: nfs-utils-1-2-3-rc4 + +* Thu May 6 2010 Steve Dickson 1.2.2-4 +- Update to upstream RC release: nfs-utils-1-2-3-rc3 + +* Fri Apr 16 2010 Steve Dickson 1.2.2-3 +- Update to upstream RC release: nfs-utils-1-2-3-rc2 + +* Mon Mar 22 2010 Steve Dickson 1.2.2-2 +- Update to upstream RC release: nfs-utils-1-2-3-rc1 + +* Thu Feb 18 2010 Steve Dickson 1.2.2-1 +- Updated to latest upstream version: 1.2.2 + +* Thu Jan 28 2010 Steve Dickson 1.2.1-17 +- Backed out the "Don't fail mounts when /etc/netconfig is + nonexistent" patch + +* Wed Jan 27 2010 Steve Dickson 1.2.1-16 +- mount.nfs: Don't fail mounts when /etc/netconfig is nonexistent + +* Mon Jan 25 2010 Steve Dickson 1.2.1-15 +- statd: Teach nfs_compare_sockaddr() to handle NULL + arguments + +* Fri Jan 22 2010 Steve Dickson 1.2.1-14 +- Update to upstream RC release: nfs-utils-1-2-2-rc9 + +* Thu Jan 21 2010 Steve Dickson 1.2.1-13 +- mount.nfs: Configuration file parser ignoring options +- mount.nfs: Set the default family for lookups based on + defaultproto= setting +- Enabled ipv6 + +* Sun Jan 17 2010 Steve Dickson 1.2.1-12 +- Updated to latest upstream RC release: nfs-utils-1-2-2-rc7 + which includes Ipv6 support for tcpwrapper (disabled by default). + +* Sat Jan 16 2010 Steve Dickson 1.2.1-11 +- Updated to latest upstream RC release: nfs-utils-1-2-2-rc7 + which includes Ipv6 support for statd (disabled by default). + +* Thu Jan 14 2010 Steve Dickson 1.2.1-10 +- Updated to the latest pseudo root release (rel10) which + containts the upstream pseudo root release + +* Tue Jan 12 2010 Steve Dickson 1.2.1-9 +- Updated to latest upstream RC release: nfs-utils-1-2-2-rc5 + +* Mon Jan 4 2010 Steve Dickson 1.2.1-8 +- mount.nfs: don't use IPv6 unless IPV6_SUPPORTED is set + +* Mon Dec 14 2009 Steve Dickson 1.2.1-7 +- Updated to latest upstream RC release: nfs-utils-1-2-2-rc3 + +* Thu Dec 10 2009 Steve Dickson 1.2.1-6 +- Update the pseudo root to handle security flavors better. + +* Mon Dec 7 2009 Steve Dickson 1.2.1-5 +- mount.nfs: Retry v4 mounts with v3 on ENOENT errors + +* Mon Dec 7 2009 Steve Dickson 1.2.1-4 +- Updated to the latest pseudo root release (rel9) (bz 538609). + +* Thu Nov 12 2009 Steve Dickson 1.2.1-3 +- Stop rpc.nfsd from failing to startup when the network + is down (bz 532270) + +* Wed Nov 11 2009 Steve Dickson 1.2.1-2 +- Updated to the latest pseudo root release (rel8). + +* Wed Nov 4 2009 Steve Dickson 1.2.1-1 +- Updated to latest upstream release: 1.2.0 + +* Tue Nov 3 2009 Steve Dickson 1.2.0-18 +- Reworked and remove some of the Default-Start/Stop stanzas + in the init scripts (bz 531425) + +* Mon Nov 2 2009 Steve Dickson 1.2.0-17 +- Updated to the latest pseudo root release (rel7). +- Added upstream 1.2.1-rc7 patch which fixes: + - Stop ignoring the -o v4 option (bz 529407) + - Allow network protocol roll backs when proto is set + in the config file (bz 529864) +- v4 mounts will roll back to v3 mounts when the mount + fails with ENOENT. + +* Mon Oct 5 2009 Steve Dickson 1.2.0-16 +- Fixed a whole where '-o v4' was not overriding the + version in the conf file. + +* Wed Sep 30 2009 Steve Dickson 1.2.0-15 +- Change the nfsmount.conf file to define v3 as the default + protocol version. +- Make sure versions set on the command line override version + set in nfsmount.conf +- Make version rollbacks still work when versions are set in + nfsmount.conf + +* Tue Sep 29 2009 Steve Dickson 1.2.0-13 +- Added upstream 1.2.1-rc5 patch + - mount.nfs: Support negotiation between v4, v3, and v2 + - mount.nfs: Keep server's address in nfsmount_info + - mount.nfs: Sandbox each mount attempt + - mount.nfs: Support negotiation between v4, v3, and v2 + +* Wed Sep 23 2009 Steve Dickson 1.2.0-12 +- Updated to the latest pseudo root release (rel6). + +* Tue Sep 15 2009 Steve Dickson 1.2.0-11 +- Added upstream 1.2.1-rc5 patch + - Added --sort --list functionality to nfs-iostat.py + - Fixed event handler in idmapd + - Added -o v4 support + - Disabled IPv6 support in nfsd + - Don't give client an empty flavor list + - Fixed gssed so it does not blindly caches machine credentials + +* Mon Aug 17 2009 Steve Dickson 1.2.0-10 +- Added upstream 1.2.1-rc4 patch + - Fix bug when both crossmnt + - nfs(5): Add description of lookupcache mount option + - nfs(5): Remove trailing blanks + - Added nfs41 support to nfssat + - Added support for mount to us a configuration file. + +* Fri Aug 14 2009 Steve Dickson 1.2.0-9 +- Added upstream 1.2.1-rc3 patch + - Add IPv6 support to nfsd + - Allow nfssvc_setfds to properly deal with AF_INET6 + - Convert nfssvc_setfds to use getaddrinfo + - Move check for active knfsd to helper function + - Declare a static common buffer for nfssvc.c routine + - Convert rpc.nfsd to use xlog() and add --debug and --syslog options + +* Tue Jul 28 2009 Steve Dickson 1.2.0-8 +- Fixed 4.1 versioning problem (bz 512377) + +* Wed Jul 15 2009 Steve Dickson 1.2.0-7 +- Added upstream 1.2.1-rc2 patch + - A large number of mount command changes. + +* Mon Jul 13 2009 Steve Dickson 1.2.0-6 +- Added NFSD v4 dynamic pseudo root patch which allows + NFS v3 exports to be mounted by v4 clients. + +* Mon Jun 29 2009 Steve Dickson 1.2.0-5 +- Stopped rpc.idmapd from spinning (bz 508221) + +* Mon Jun 22 2009 Steve Dickson 1.2.0-4 +- Added upstream 1.2.1-rc1 patch + - Fix to check in closeall() + - Make --enable-tirpc the default + - Set all verbose types in gssd daemons + - Retry exports if getfh() fails + +* Wed Jun 10 2009 Steve Dickson 1.2.0-3 +- Updated init scripts to add dependencies + on other system facilities (bz 475133) + +* Wed Jun 10 2009 Steve Dickson 1.2.0-2 +- nfsnobody gid is wrong (bz 485379) + +* Tue Jun 2 2009 Steve Dickson 1.2.0-1 +- Updated to latest upstream release: 1.2.0 + +* Tue May 19 2009 Tom "spot" Callaway 1.1.6-4 +- Replace the Sun RPC license with the BSD license, with the explicit permission of Sun Microsystems + +* Mon May 18 2009 Steve Dickson 1.1.6-3 +- Added upstream 1.1.7-rc1 patch + - utils/nfsd: add support for minorvers4 + - sm-notify: Don't orphan addrinfo structs + - sm-notify: Failed DNS lookups should be retried + - mount: remove legacy version of nfs_name_to_address() + - compiling error in rpcgen + - nfs-utils: Fix IPv6 support in support/nfs/rpc_socket.c + - umount.nfs: Harden umount.nfs error reportin + +* Mon Apr 27 2009 Steve Dickson 1.1.6-2 +- nfslock.init: options not correctly parsed (bz 459591) + +* Mon Apr 20 2009 Steve Dickson 1.1.6-1 +- Updated to latest upstream release: 1.1.6 + +* Mon Mar 23 2009 Steve Dickson 1.1.5-4 +- Added upstream rc3 patch + - gssd: initialize fakeseed in prepare_krb5_rfc1964_buffer + - gssd: NULL-terminate buffer after read in read_service_info (try #2) + - gssd: free buffer allocated by gssd_k5_err_msg + - gssd: fix potential double-frees in gssd + - Removed a number of warn_unused_result warnings + +* Mon Mar 16 2009 Steve Dickson 1.1.5-3 +- Added upstream rc2 patch + +* Fri Mar 6 2009 Steve Dickson 1.1.5-2 +- Fixed lockd not using settings in sysconfig/nfs (bz 461043) +- Fixed some lost externs in the tcpwrapper code + +* Thu Mar 5 2009 Steve Dickson 1.1.5-1 +- Updated to latest upstream version: 1.1.5 + +* Wed Mar 4 2009 Steve Dickson 1.1.4-21 +- configure: fix AC_CACHE_VAL warnings + +* Wed Feb 25 2009 Fedora Release Engineering - 1:1.1.4-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Wed Feb 18 2009 Steve Dickson 1.1.4-19 +- Exportfs and rpc.mountd optimalization (bz 76643) + +* Tue Feb 17 2009 Steve Dickson 1.1.4-18 +- umount.nfs command: Add an AF_INET6-capable version of nfs_call_unmount() +- umount.nfs command: Support AF_INET6 server addresses +- umount command: remove do_nfs_umount23 function + +* Tue Feb 17 2009 Steve Dickson 1.1.4-17 +- Integrated the upstream fix for bz 483375 +- mount: segmentation faults on UDP mounts (bz 485448) + +* Sat Jan 31 2009 Steve Dickson 1.1.4-16 +- Fixed typo in -mount-textbased.patch (bz 483375) + +* Sat Jan 31 2009 Steve Dickson 1.1.4-15 +- Reworked tcp wrapper code to correctly use API (bz 480223) +- General clean up of tcp wrapper code. + +* Tue Jan 27 2009 Steve Dickson 1.1.4-14 +- text-based mount command: make po_rightmost() work for N options +- text-based mount command: Function to stuff "struct pmap" from mount options +- text-based mount options: Use new pmap stuffer when rewriting mount options +- text-based mount command: fix mount option rewriting logic +- text-based mount command: support AF_INET6 in rewrite_mount_options() + +* Tue Jan 20 2009 Steve Dickson 1.1.4-13 +- mountd: Don't do tcp wrapper check when there are no rules (bz 448898) + +* Wed Jan 7 2009 Steve Dickson 1.1.4-12 +- configure: Remove inet_ntop(3) check from configure.ac +- configure: Add new build option "--enable-tirpc" +- showmount command: Quiesce warning when TI-RPC is disabled + +* Sat Jan 3 2009 Steve Dickson 1.1.4-11 +- Added warnings to tcp wrapper code when mounts are + denied due to misconfigured DNS configurations. +- gssd: By default, don't spam syslog when users' credentials expire +- mount: revert recent fix for build problems on old systems +- mount: use gethostbyname(3) when building on old systems +- mount: getport: don't use getaddrinfo(3) on old systems +- mount: Random clean up +- configure: use "--disable-uuid" instead of "--without-uuid" + +* Fri Dec 19 2008 Steve Dickson 1.1.4-10 +- Re-enabled and fixed/enhanced tcp wrappers. + +* Wed Dec 17 2008 Steve Dickson 1.1.4-9 +- text-based mount command: add function to parse numeric mount options +- text-based mount command: use po_get_numeric() for handling retry +- sm-notify command: fix a use-after-free bug +- statd: not unlinking host files + +* Thu Dec 11 2008 Steve Dickson 1.1.4-8 +- mount command: AF_INET6 support for probe_bothports() +- mount command: support AF_INET6 in probe_nfsport() and probe_mntport() +- mount command: full support for AF_INET6 addresses in probe_port() +- gssd/svcgssd: add support to retrieve actual context expiration +- svcgssd: use the actual context expiration for cache + +* Sat Dec 6 2008 Steve Dickson 1.1.4-7 +- sm-notify: always exiting without any notification. + +* Tue Dec 2 2008 Steve Dickson 1.1.4-6 +- mount command: remove local getport() implementation +- mount command: Replace clnt_ping() and getport() calls in probe_port() +- mount command: Use nfs_error() instead of perror() +- mount command: Use nfs_pmap_getport() in probe_statd() + +* Mon Dec 1 2008 Steve Dickson 1.1.4-5 +- Make sure /proc/fs/nfsd exists when the nfs init script + does the exports (bz 473396) +- Fixed typo in nfs init script that caused rpc.rquotad daemons + to be started but not stoppped (bz 473929) + +* Wed Nov 26 2008 Steve Dickson 1.1.4-4 +- gssd: unblock DNOTIFY_SIGNAL in case it was blocked +- Ensure statd gets started if required when non-root + user mounts an NFS filesystem + +* Tue Nov 25 2008 Steve Dickson 1.1.4-3 +- Give showmount support for querying via rpcbindv3/v4 + +* Tue Nov 18 2008 Steve Dickson 1.1.4-2 +- Add AF_INET6-capable API to acquire an RPC CLIENT +- Introduce rpcbind client utility functions + +* Sat Oct 18 2008 Steve Dickson 1.1.4-1 +- Updated to latest upstream version: 1.1.4 + +* Tue Oct 14 2008 Steve Dickson 1.1.3-6 +- sm-notify exists when there are no hosts to notify + +* Thu Sep 18 2008 Steve Dickson 1.1.3-5 +- Reworked init scripts so service will be able to + stop when some of the checks fail. (bz 462508) +- Pre-load nfsd when args to rpc.nfsd are given (bz 441983) + +* Thu Aug 28 2008 Steve Dickson 1.1.3-4 +- Added in a number of up upstream patches (101 thru 110). + +* Mon Aug 11 2008 Tom "spot" Callaway 1.1.3-3 +- fix license tag + +* Thu Jul 31 2008 Steve Dickson 1.1.3-2 +- Mount command did not compile against older glibc versions. + +* Mon Jul 28 2008 Steve Dickson 1.1.3-1 +- Updated to latest upstream version: 1.1.3 + +* Wed Jul 2 2008 Steve Dickson 1.1.2-12 +- Changed the default directories for sm-notify (bz 435480) +- Added 'condstop' to init scripts so service are not + started when nfs-utils is removed. + +* Mon Jun 30 2008 Dennis Gilmore 1.1.2-11 +- add sparc arch handling + +* Mon Jun 30 2008 Steve Dickson 1.1.2-10 +- Rebuild for the updated libevent lib. + +* Fri Jun 27 2008 Steve Dickson 1.1.2-9 +- Removed the nfslock service start/stop from %%post section + (bz 453046) + +* Wed Jun 25 2008 Steve Dickson 1.1.2-8 +- FQDNs in the rmtab causes exportfs to seg fault (bz 444275) + +* Mon Jun 23 2008 Steve Dickson 1.1.2-7 +- Added -D_FILE_OFFSET_BITS=64 to CFLAGS +- make nfsstat read and print stats as unsigned integers +- Added (but not installed) the mountstats and nfs-iostat + python scripts. + +* Fri Jun 6 2008 Steve Dickson 1.1.2-6 +- Added 5 (111 thru 115) upstream patches that fixed + things mostly in the text mounting code. + +* Thu May 8 2008 Steve Dickson 1.1.2-5 +- Added 10 (101 thru 110) upstream patches that fixed + things mostly in the mount and gssd code. + +* Wed May 7 2008 Steve Dickson 1.1.2-4 +- Added ppc arch to the all_32bit_archs list (bz 442847) + +* Wed Apr 23 2008 Steve Dickson 1.1.2-3 +- Documented how to turn off/on protocol support for + rpc.nfsd in /etc/sysconfig/nfs (bz443625) +- Corrected the nfslock initscript 'status' return code (bz 441605) +- Removed obsolete code from the nfslock initscript (bz 441604) + +* Mon Apr 14 2008 Steve Dickson 1.1.2-2 +- Make EACCES a non fatal error (bz 439807) + +* Tue Mar 25 2008 Steve Dickson 1.1.2-1 +- Upgrade to nfs-utils-1.1.2 + +* Mon Mar 3 2008 Steve Dickson 1.1.1-5 +- Stopped mountd from incorrectly logging an error + (commit 9dd9b68c4c44f0d9102eb85ee2fa36a8b7f638e3) +- Stop gssd from ignoring the machine credential caches + (commit 46d439b17f22216ce8f9257a982c6ade5d1c5931) +- Fixed typo in the nfsstat command line arugments. + (commit acf95d32a44fd8357c24e8a04ec53fc6900bfc58) +- Added test to stop buffer overflow in idmapd + (commit bcd0fcaf0966c546da5043be700587f73174ae25) + +* Sat Feb 9 2008 Steve Dickson 1.1.1-4 +- Cleaned up some typos that were found in the various + places in the mountd code + +* Thu Jan 24 2008 Steve Dickson 1.1.1-3 +- Added in relatime mount option so mount.nfs stays + compatible with the mount command in util-linux-ng (bz 274301) + +* Tue Jan 22 2008 Steve Dickson 1.1.1-2 +- Added -S/--since to the nfsstat(1) manpage +- The wording in the exportfs man page can be a bit confusing, implying + that "exportfs -u :/foo" will unexport /foo from all hosts, which it won't +- Removed nfsprog option since the kernel no longer supports it. +- Removed mountprog option since the kernel no longer supports it. +- Stop segfaults on amd64 during warnings messages. +- Fix bug when both crossmnt and fsid are set. + +* Sat Jan 5 2008 Steve Dickson 1.1.1-1 +- Updated to latest upstream release, nfs-utils-1.1.1 +- Added the removal of sm-notify.pid to nfslock init script. +- Changed spec file to use condrestart instead of condstop + when calling init scripts. +- Fixed typo in rpc.mountd man page +- Turn on 'nohide' automatically for all refer exports (bz 313561) + +* Tue Dec 04 2007 Release Engineering - 1.1.0-7 + - Rebuild for openldap bump + +* Wed Oct 17 2007 Steve Dickson 1.1.0-6 +- Switch the libgssapi dependency to libgssglue + +* Fri Sep 14 2007 Steve Dickson 1.1.0-5 +- Changed the default paths in sm-notify to + /var/lib/nfs/statd (bz 258461) +- Updated exportfs manpage (bz 262861) + +* Wed Aug 15 2007 Steve Dickson 1.1.0-4 +- Make sure the open() system calling in exportfs uses + mode bits when creating the etab file (bz 252440). + +* Mon Aug 13 2007 Steve Dickson 1.1.0-3 +- Added nosharecache mount option which re-enables + rw/ro mounts to the same server (bz 243913). + +* Thu Aug 2 2007 Steve Dickson 1.1.0-2 +- Make sure the gss and idmap daemons remove thier lock + files when they are stopped. + +* Sat Jul 28 2007 Steve Dickson 1.1.0-1 +- Upgraded to the latest upstream version (nfs-utils-1.1.0) + +* Thu May 24 2007 Steve Dickson 1.0.10-7 +- Fixed typo in mount.nfs4 that causes a segfault during + error processing (bz 241190) + +* Tue May 22 2007 Steve Dickson 1.0.10-6 +- Make sure the condrestarts exit with a zero value (bz 240225) +- Stopped /etc/sysconfig/nfs from being overwritten on updates (bz 234543) +- Added -o nordirplus mount option to disable READDIRPLUS (bz 240357) +- Disabled the FSCache patch, for now... + +* Thu May 10 2007 Steve Dickson 1.0.12-5 +- Fix mount.nfs4 to display correct error message (bz 227212) +- Updated mountd and showmount reverse lookup flags (bz 220772) +- Eliminate timeout on nfsd shutdowns (bz 222001) +- Eliminate memory leak in mountd (bz 239536) +- Make sure statd uses correct uid/gid by chowning + the /var/lib/nfs/statd with the rpcuser id. (bz 235216) +- Correct some sanity checking in rpc.nfsd. (bz 220887) +- Added missing unlock_mtab() call in moutnd +- Have mountd hold open etab file to force inode number to change (bz 236823) +- Create a /etc/sysconfig/nfs with all the possible init script + variables (bz 234543) +- Changed nfs initscript to exit with correct value (bz 221874) + +* Tue Apr 3 2007 Steve Dickson 1.0.12-4 +- Replace portmap dependency with an rpcbind dependency (bz 228894) + +* Mon Mar 12 2007 Steve Dickson 1.0.12-3 +- Incorporated Merge Review comments (bz 226198) + +* Fri Mar 9 2007 Steve Dickson 1.0.12-2 +- Added condstop to all the initscripts (bz 196934) +- Made no_subtree_check a default export option (bz 212218) + +* Tue Mar 6 2007 Steve Dickson 1.0.12-1 +- Upgraded to 1.0.12 +- Fixed typo in Summary. + +* Thu Mar 1 2007 Karel Zak 1.0.11-2 +- Fixed mount.nfs -f (fake) option (bz 227988) + +* Thu Feb 22 2007 Steve Dickson 1.0.11-1 +- Upgraded to 1.0.11 + +* Wed Feb 21 2007 Steve Dickson 1.0.10-7 +- Added FS_Location support + +* Mon Dec 18 2006 Karel Zak 1.0.10-6 +- add support for mount options that contain commas (bz 219645) + +* Wed Dec 13 2006 Steve Dickson 1.0.10-5 +- Stopped v4 umounts from ping rpc.mountd (bz 215553) + +* Tue Nov 28 2006 Steve Dickson 1.0.10-4 +- Doing a connect on UDP sockets causes the linux network + stack to reject UDP patches from multi-home server with + nic on the same subnet. (bz 212471) + +* Wed Nov 15 2006 Steve Dickson 1.0.10-3 +- Removed some old mounting versioning code that was + stopping tcp mount from working (bz 212471) + +* Tue Oct 31 2006 Steve Dickson 1.0.10-2 +- Fixed -o remount (bz 210346) +- fix memory leak in rpc.idmapd (bz 212547) +- fix use after free bug in dirscancb (bz 212547) +- Made no_subtree_check a default export option (bz 212218) + +* Wed Oct 25 2006 Steve Dickson 1.0.10-1 +- Upgraded to 1.0.10 + +* Mon Oct 16 2006 Steve Dickson 1.0.9-10 +- Fixed typo in nfs man page (bz 210864). + +* Fri Oct 13 2006 Steve Dickson 1.0.9-9 +- Unable to mount NFS V3 share where sec=none is specified (bz 210644) + +* Tue Sep 26 2006 Steve Dickson 1.0.9-8 +- mount.nfs was not returning a non-zero exit value + on failed mounts (bz 206705) + +* Wed Sep 20 2006 Karel Zak 1.0.9-7 +- Added support for the mount -s (sloppy) option (#205038) +- Added nfs.5 man page from util-linux +- Added info about [u]mount.nfs to the package description + +* Mon Sep 11 2006 1.0.9-6 +- Removed the compiling of getiversion and getkversion since + UTS_RELEASE is no longer defined and these binary are + not installed. + +* Fri Aug 18 2006 1.0.9-5 +- Changed gssd daemons to cache things in memory + instead of /tmp which makes selinux much happier. + (bz 203078) + +* Wed Aug 16 2006 1.0.9-4 +- Allow variable for HA callout program in /etc/init.d/nfslock + (bz 202790) + +* Wed Aug 02 2006 1.0.9-3 +- add epoch (#196359) + +* Fri Jul 28 2006 1.0.9-2 +- Enabled the creating of mount.nfs and umount.nfs binaries +- Added mount option fixes suggested by upstream. +- Fix lazy umounts (bz 169299) +- Added -o fsc mount option. + +* Mon Jul 24 2006 1.0.9-1 +- Updated to 1.0.9 release + +* Wed Jul 12 2006 Jesse Keating - 1:1.0.8-5.1 +- rebuild + +* Sun Jul 2 2006 1:1.0.8-5 +- Introduce epoch to fix upgrade path + +* Sat Jul 1 2006 1.0.8-3 +- Fixed typos in /etc/rc.d/init.d/nfs file (bz 184486) + +* Fri Jun 30 2006 1.0.8-3 +- Split the controlling of nfs version, ports, and protocol + into two different patches +- Fixed and added debugging statements to rpc.mountd. +- Fixed -p arg to work with priviledged ports (bz 156655) +- Changed nfslock initscript to set LOCKD_TCPPORT and + LOCKD_UDPPORT (bz 162133) +- Added MOUNTD_NFS_V1 variable to version 1 of the + mount protocol can be turned off. (bz 175729) +- Fixed gssd to handel mixed case characters in + the domainname. (bz 186069) + +* Wed Jun 21 2006 1.0.8-2 +- Updated to nfs-utils-1.0.8 + +* Thu Jun 8 2006 1.0.8.rc4-1 +- Upgraded to the upstream 1.0.8.rc4 version + +* Fri Feb 10 2006 Jesse Keating - 1.0.8.rc2-4.FC5.2 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Jesse Keating - 1.0.8.rc2-4.FC5.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Fri Jan 20 2006 Steve Dickson 1.0.8.rc2-4.FC5 +- Added new libnfsidmap call, nfs4_set_debug(), to rpc.idmapd + which turns on debugging in the libarary. + +* Mon Jan 16 2006 Steve Dickson 1.0.8.rc2-3.FC5 +- Added innetgr patch that changes configure scripts to + check for the innetgr function. (bz 177899) + +* Wed Jan 11 2006 Peter Jones 1.0.8.rc2-2.FC5 +- Fix lockfile naming in the initscripts so they're stopped correctly. + +* Mon Jan 9 2006 Steve Dickson 1.0.8.rc2-1.FC5 +- Updated to 1.0.8-rc2 release +- Broke out libgssapi into its own rpm +- Move librpcsecgss and libnfsidmap in the new nfs-utils-lib rpm +- Removed libevent code; Required to be installed. + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Sun Oct 23 2005 Steve Dickson 1.0.7-19 +- Updated to latest code in SourceForge CVS +- Updated to latest CITI patches (1.0.7-4) +- Fix bug in nfsdreopen by compiling in server defaults + +* Thu Sep 22 2005 Steve Dickson 1.0.7-18 +- Updated libnfsidmap to 0.11 +- Updated libgssapi to 0.5 +- Made sure the gss daemons and new libs are + all using the same include files. +- Removed code from the tree that is no longer used. +- Add ctlbits patch that introduced the -N -T and -U + command line flags to rpc.nfsd. + +* Sun Sep 18 2005 Steve Dickson 1.0.7-17 +- Updated to latest nfs-utils code in upstream CVS tree +- Updated libevent from 1.0b to 1.1a +- Added libgssapi-0.4 and librpcsecgss-0.6 libs from CITI + +* Thu Sep 8 2005 Steve Dickson 1.0.7-16 +- Reworked the nfslock init script so if lockd is running + it will be killed which is what the HA community needs. (bz 162446) +- Stopped rpcidmapd.init from doing extra echoing when + condstart-ed. + +* Wed Aug 24 2005 Peter Jones - 1.0.7-15 +- don't strip during "make install", so debuginfo packages are generated right + +* Thu Aug 18 2005 Florian La Roche +- no need to still keep a requirement for kernel-2.2 or newer + +* Tue Aug 16 2005 Steve Dickson 1.0.7-13 +- Changed mountd to use stat64() (bz 165062) + +* Tue Aug 2 2005 Steve Dickson 1.0.7-12 +- Changed useradd to use new -l flag (bz149407) +- 64bit fix in gssd code (bz 163139) +- updated broken dependencies +- updated rquotad to compile with latest + quota version. + +* Thu May 26 2005 Steve Dickson 1.0.7-8 +- Fixed subscripting problem in idmapd (bz 158188) + +* Thu May 19 2005 Steve Dickson 1.0.7-7 +- Fixed buffer overflow in rpc.svcgssd (bz 114288) + +* Wed Apr 13 2005 Steve Dickson 1.0.7-6 +- Fixed misformated output from nfslock script (bz 154648) + +* Tue Mar 29 2005 Steve Dickson 1.0.7-4 +- Fixed a compile error on x86_64 machines in the gss code. +- Updated the statd-notify-hostname.patch to eliminate + a segmentation fault in rpc.statd when an network + interface was down. (bz 151828) + +* Sat Mar 19 2005 Steve Dickson 1.0.7-3 +- Changed xlog to use LOG_INFO instead of LOG_DEBUG + so debug messages will appear w/out any config + changes to syslog.conf. +- Reworked how /etc/exports is setup (bz 151389) + +* Wed Mar 2 2005 Steve Dickson 1.0.7-2 +- Tied the rpcsecgss debugging in with gssd and + svcgssd debugging + +* Mon Feb 14 2005 Steve Dickson +- Added support to rpcgssd.init and rpcsvcgssd.init scripts + to insmod security modules. +- Changed the nfs.init script to bring rpc.svcgssd up and down, + since rpc.svcgssd is only needed with the NFS server is running. + +* Tue Dec 14 2004 Steve Dickson +- Fix problem in idmapd that was causing "xdr error 10008" + errors (bz 142813) +- make sure the correct hostname is used in the SM_NOTIFY + message that is sent from a rebooted server which has + multiple network interfaces. (bz 139101) + +- Changed nfslock to send lockd a -KILL signal + when coming down. (bz 125257) + +* Thu Nov 11 2004 Steve Dickson +- Replaced a memcopy with explicit assignments + in getquotainfo() of rquotad to fix potential overflow + that can occur on 64bit machines. (bz 138068) + +* Mon Nov 8 2004 Steve Dickson +- Updated to latest sourceforge code +- Updated to latest CITIT nfs4 patches + +* Sun Oct 17 2004 Steve Dickson +- Changed nfs.init to bring down rquotad correctly + (bz# 136041) + +* Thu Oct 14 2004 Steve Dickson +- Added "$RQUOTAD_PORT" variable to nfs.init which + allows the rpc.rquotad to use a predefined port + (bz# 124676) + +* Fri Oct 1 2004 +- Make sure the uid/gid of nfsnobody is the + correct value for all archs (bz# 123900) +- Fixed some security issues found by SGI (bz# 133556) + +* Mon Aug 30 2004 Steve Dickson +- Major clean up. +- Removed all unused/old patches +- Rename and condensed a number of patches +- Updated to CITI's nfs-utils-1.0.6-13 patches + +* Tue Aug 10 2004 Bill Nottingham +- move if..fi condrestart stanza to %%postun (#127914, #128601) + +* Wed Jun 16 2004 +- nfslock stop is now done on package removals +- Eliminate 3 syslog messages that are logged for + successful events. + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Mon Jun 14 2004 +- Fixed syntax error in nfs initscripts when + NETWORKING is not defined +- Removed sync warning on readonly exports. +- Changed run levels in rpc initscripts. +- Replaced modinfo with lsmod when checking + for loaded modules. + +* Tue Jun 1 2004 +- Changed the rpcgssd init script to ensure the + rpcsec_gss_krb5 module is loaded + +* Tue May 18 2004 +- Removed the auto option from MOUNTD_NFS_V2 and + MOUNTD_NFS_V3 variables. Since v2 and v3 are on + by default, there only needs to be away of + turning them off. + +* Mon May 10 2004 +- Rebuilt + +* Thu Apr 15 2004 +- Changed the permission on idmapd.conf to 644 +- Added mydaemon code to svcgssd +- Updated the add_gssd.patch from upstream + +* Wed Apr 14 2004 +- Created a pipe between the parent and child so + the parent process can report the correct exit + status to the init scripts +- Added SIGHUP processing to rpc.idmapd and the + rpcidmapd init script. + +* Mon Mar 22 2004 +- Make sure check_new_cache() is looking in the right place + +* Wed Mar 17 2004 +- Changed the v4 initscripts to use $prog for the + arugment to daemon + +* Tue Mar 16 2004 +- Made the nfs4 daemons initscripts work better when + sunrpc is not a module +- added more checks to see if modules are being used. + +* Mon Mar 15 2004 +- Add patch that sets up gssapi_mech.conf correctly + +* Fri Mar 12 2004 +- Added the shutting down of the rpc v4 daemons. +- Updated the Red Hat only patch with some init script changes. + +* Thu Mar 11 2004 Bill Nottingham +- rpc_pipefs mounting and aliases are now in modutils; require that + +* Thu Mar 11 2004 +- Updated the gssd patch. + +* Sun Mar 7 2004 +- Added the addition and deletion of rpc_pipefs to /etc/fstab +- Added the addition and deletion of module aliases to /etc/modules.conf + +* Mon Mar 1 2004 +- Removed gssd tarball and old nfsv4 patch. +- Added new nfsv4 patches that include both the + gssd and idmapd daemons +- Added redhat-only v4 patch that reduces the + static librpc.a to only contain gss rpc related + routines (I would rather have gssd use the glibc + rpc routines) +-Changed the gssd svcgssd init scripts to only + start up if SECURE_NFS is set to 'yes' in + /etc/sysconfig/nfs + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Thu Feb 12 2004 Thomas Woerner +- make rpc.lockd, rpc.statd, rpc.mountd and rpc.nfsd pie + +* Wed Jan 28 2004 Steve Dickson +- Added the NFSv4 bits + +* Mon Dec 29 2003 Steve Dickson +- Added the -z flag to nfsstat + +* Wed Dec 24 2003 Steve Dickson +- Fixed lockd port setting in nfs.int script + +* Wed Oct 22 2003 Steve Dickson +- Upgrated to 1.0.6 +- Commented out the acl path for fedora + +* Wed Aug 27 2003 Steve Dickson +- Added the setting of lockd ports via sysclt interface +- Removed queue setting code since its no longer needed + +* Thu Aug 7 2003 Steve Dickson +- Added back the acl patch Taroon b2 + +* Wed Jul 23 2003 Steve Dickson +- Commented out the acl patch (for now) + +* Mon Jul 21 2003 Steve Dickson +- Upgrated to 1.0.5 + +* Wed Jun 18 2003 Steve Dickson +- Added security update +- Fixed the drop-privs.patch which means the chroot +patch could be removed. + +* Mon Jun 9 2003 Steve Dickson +- Defined the differ kinds of debugging avaliable for mountd in +the mountd man page. + +* Wed Jun 04 2003 Elliot Lee +- rebuilt + +* Tue Jun 3 2003 Steve Dickson +- Upgraded to 1.0.3 +- Fixed numerous bugs in init scrips +- Added nfsstat overflow patch + +* Thu Jan 23 2003 Tim Powers 1.0.1-2.9 +- rebuild + +* Fri Dec 13 2002 Daniel J Walsh +- change init script to not start rpc.lock if already running + +* Wed Dec 11 2002 Daniel J Walsh +- Moved access code to be after dropping privs + +* Mon Nov 18 2002 Stephen C. Tweedie +- Build with %%configure +- Add nhfsgraph, nhfsnums and nhfsrun to the files list + +* Mon Nov 11 2002 Stephen C. Tweedie +- Don't drop privs until we've bound the notification socket + +* Thu Nov 7 2002 Stephen C. Tweedie +- Ignore SIGPIPE in rpc.mountd + +* Thu Aug 1 2002 Bob Matthews +- Add Sean O'Connell's nfs control tweaks +- to nfs init script. + +* Mon Jul 22 2002 Bob Matthews +- Move to nfs-utils-1.0.1 + +* Mon Feb 18 2002 Bob Matthews +- "service nfs restart" should start services even if currently +- not running (#59469) +- bump version to 0.3.3-4 + +* Wed Oct 3 2001 Bob Matthews +- Move to nfs-utils-0.3.3 +- Make nfsnobody a system account (#54221) + +* Tue Aug 21 2001 Bob Matthews +- if UID 65534 is unassigned, add user nfsnobody (#22685) + +* Mon Aug 20 2001 Bob Matthews +- fix typo in nfs init script which prevented MOUNTD_PORT from working (#52113) + +* Tue Aug 7 2001 Bob Matthews +- nfs init script shouldn't fail if /etc/exports doesn't exist (#46432) + +* Fri Jul 13 2001 Bob Matthews +- Make %%pre useradd consistent with other Red Hat packages. + +* Tue Jul 03 2001 Michael K. Johnson +- Added sh-utils dependency for uname -r in nfs init script + +* Tue Jun 12 2001 Bob Matthews +- make non RH kernel release strings scan correctly in +- nfslock init script (#44186) + +* Mon Jun 11 2001 Bob Matthews +- don't install any rquota pages in _mandir: (#39707, #44119) +- don't try to manipulate rpc.rquotad in init scripts +- unless said program actually exists: (#43340) + +* Tue Apr 10 2001 Preston Brown +- don't translate initscripts for 6.x + +* Tue Apr 10 2001 Michael K. Johnson +- do not start lockd on kernel 2.2.18 or higher (done automatically) + +* Fri Mar 30 2001 Preston Brown +- don't use rquotad from here now; quota package contains a version that + works with 2.4 (#33738) + +* Mon Mar 12 2001 Bob Matthews +- Statd logs at LOG_DAEMON rather than LOG_LOCAL5 +- s/nfs/\$0/ where appropriate in init scripts + +* Tue Mar 6 2001 Jeff Johnson +- Move to nfs-utils-0.3.1 + +* Wed Feb 14 2001 Bob Matthews +- #include patch + +* Mon Feb 12 2001 Bob Matthews +- Really enable netgroups + +* Mon Feb 5 2001 Bernhard Rosenkraenzer +- i18nize initscripts + +* Fri Jan 19 2001 Bob Matthews +- Increased {s,r}blen in rpcmisc.c:makesock to accommodate eepro100 + +* Tue Jan 16 2001 Bob Matthews +- Hackish fix in build section to enable netgroups + +* Wed Jan 3 2001 Bob Matthews +- Fix incorrect file specifications in statd manpage. +- Require gawk 'cause it's used in nfslock init script. + +* Wed Dec 13 2000 Bob Matthews +- Require sed because it's used in nfs init script + +* Tue Dec 12 2000 Bob Matthews +- Don't do a chroot(2) after dropping privs, in statd. + +* Mon Dec 11 2000 Bob Matthews +- NFSv3 if kernel >= 2.2.18, detected in init script + +* Thu Nov 23 2000 Florian La Roche +- update to 0.2.1 + +* Tue Nov 14 2000 Bill Nottingham +- don't start lockd on 2.4 kernels; it's unnecessary + +* Tue Sep 5 2000 Florian La Roche +- more portable fix for mandir + +* Sun Sep 3 2000 Florian La Roche +- update to 0.2-release + +* Fri Sep 1 2000 Florian La Roche +- fix reload script + +* Thu Aug 31 2000 Florian La Roche +- update to 0.2 from CVS +- adjust statd-drop-privs patch +- disable tcp_wrapper support + +* Wed Aug 2 2000 Bill Nottingham +- fix stop priority of nfslock + +* Tue Aug 1 2000 Bill Nottingham +- um, actually *include and apply* the statd-drop-privs patch + +* Mon Jul 24 2000 Bill Nottingham +- fix init script ordering (#14502) + +* Sat Jul 22 2000 Bill Nottingham +- run statd chrooted and as non-root +- add prereqs + +* Tue Jul 18 2000 Trond Eivind Glomsrød +- use "License", not "Copyright" +- use %%{_tmppath} and %%{_mandir} + +* Mon Jul 17 2000 Matt Wilson +- built for next release + +* Mon Jul 17 2000 Matt Wilson +- 0.1.9.1 +- remove patch0, has been integrated upstream + +* Wed Feb 9 2000 Bill Nottingham +- the wonderful thing about triggers, is triggers are wonderful things... + +* Thu Feb 03 2000 Cristian Gafton +- switch to nfs-utils as the base tree +- fix the statfs patch for the new code base +- single package that obsoletes everything we had before (if I am to keep + some traces of my sanity with me...) + +* Mon Jan 17 2000 Preston Brown +- use statfs syscall instead of stat to determinal optimal blksize diff --git a/nfsconvert.py b/nfsconvert.py new file mode 100644 index 0000000..9aee57f --- /dev/null +++ b/nfsconvert.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 +""" +Read in the deprecated /etc/sysconfig/nfs file and +set the corresponding values in nfs.conf +""" + +from __future__ import print_function +import os +import sys +import getopt +import subprocess +import configparser + +CONF_NFS = '/etc/nfs.conf' +CONF_IDMAP = '/etc/idmapd.conf' +SYSCONF_NFS = '/etc/sysconfig/nfs' +SYSCONF_BACKUP = ".rpmsave" +CONF_TOOL = '/usr/sbin/nfsconf' + +# options for nfsd found in RPCNFSDARGS +OPTS_NFSD = 'dH:p:rR:N:V:stTuUG:L:' +LONG_NFSD = ['debug', 'host=', 'port=', 'rdma=', 'nfs-version=', 'no-nfs-version=', + 'tcp', 'no-tcp', 'udp', 'no-udp', 'grace-time=', 'lease-time='] +CONV_NFSD = {'-d': (CONF_NFS, 'nfsd', 'debug', 'all'), + '-H': (CONF_NFS, 'nfsd', 'host', ','), + '-p': (CONF_NFS, 'nfsd', 'port', '$1'), + '-r': (CONF_NFS, 'nfsd', 'rdma', 'nfsrdma'), + '-R': (CONF_NFS, 'nfsd', 'rdma', '$1'), + '-N': (CONF_NFS, 'nfsd', 'vers$1', 'n'), + '-V': (CONF_NFS, 'nfsd', 'vers$1', 'y'), + '-t': (CONF_NFS, 'nfsd', 'tcp', '1'), + '-T': (CONF_NFS, 'nfsd', 'tcp', '0'), + '-u': (CONF_NFS, 'nfsd', 'udp', '1'), + '-U': (CONF_NFS, 'nfsd', 'udp', '0'), + '-G': (CONF_NFS, 'nfsd', 'grace-time', '$1'), + '-L': (CONF_NFS, 'nfsd', 'lease-time', '$1'), + '$1': (CONF_NFS, 'nfsd', 'threads', '$1'), + '--debug': (CONF_NFS, 'nfsd', 'debug', 'all'), + '--host': (CONF_NFS, 'nfsd', 'host', ','), + '--port': (CONF_NFS, 'nfsd', 'port', '$1'), + '--rdma': (CONF_NFS, 'nfsd', 'rdma', '$1'), + '--no-nfs-version': (CONF_NFS, 'nfsd', 'vers$1', 'n'), + '--nfs-version': (CONF_NFS, 'nfsd', 'vers$1', 'y'), + '--tcp': (CONF_NFS, 'nfsd', 'tcp', '1'), + '--no-tcp': (CONF_NFS, 'nfsd', 'tcp', '0'), + '--udp': (CONF_NFS, 'nfsd', 'udp', '1'), + '--no-udp': (CONF_NFS, 'nfsd', 'udp', '0'), + '--grace-time': (CONF_NFS, 'nfsd', 'grace-time', '$1'), + '--lease-time': (CONF_NFS, 'nfsd', 'lease-time', '$1'), + } + +# options for mountd found in RPCMOUNTDOPTS +OPTS_MOUNTD = 'go:d:H:p:N:nrs:t:V:' +LONG_MOUNTD = ['descriptors=', 'debug=', 'nfs-version=', 'no-nfs-version=', + 'port=', 'no-tcp', 'ha-callout=', 'state-directory-path=', + 'num-threads=', 'reverse-lookup', 'manage-gids', 'no-udp'] + +CONV_MOUNTD = {'-g': (CONF_NFS, 'mountd', 'manage-gids', '1'), + '-o': (CONF_NFS, 'mountd', 'descriptors', '$1'), + '-d': (CONF_NFS, 'mountd', 'debug', '$1'), + '-H': (CONF_NFS, 'mountd', 'ha-callout', '$1'), + '-p': (CONF_NFS, 'mountd', 'port', '$1'), + '-N': (CONF_NFS, 'nfsd', 'vers$1', 'n'), + '-V': (CONF_NFS, 'nfsd', 'vers$1', 'y'), + '-n': (CONF_NFS, 'nfsd', 'tcp', '0'), + '-s': (CONF_NFS, 'mountd', 'stat-directory-path', '$1'), + '-t': (CONF_NFS, 'mountd', 'threads', '$1'), + '-r': (CONF_NFS, 'mountd', 'reverse-lookup', '1'), + '-u': (CONF_NFS, 'nfsd', 'udp', '0'), + '--manage-gids': (CONF_NFS, 'mountd', 'manage-gids', '1'), + '--descriptors': (CONF_NFS, 'mountd', 'descriptors', '$1'), + '--debug': (CONF_NFS, 'mountd', 'debug', '$1'), + '--ha-callout': (CONF_NFS, 'mountd', 'ha-callout', '$1'), + '--port': (CONF_NFS, 'mountd', 'port', '$1'), + '--nfs-version': (CONF_NFS, 'nfsd', 'vers$1', 'y'), + '--no-nfs-version': (CONF_NFS, 'nfsd', 'vers$1', 'n'), + '--no-tcp': (CONF_NFS, 'nfsd', 'tcp', '0'), + '--state-directory-path': (CONF_NFS, 'mountd', 'state-directory-path', '$1'), + '--num-threads': (CONF_NFS, 'mountd', 'threads', '$1'), + '--reverse-lookup': (CONF_NFS, 'mountd', 'reverse-lookup', '1'), + '--no-udp': (CONF_NFS, 'nfsd', 'udp', '0'), + } + +# options for statd found in STATDARG +OPTS_STATD = 'o:p:T:U:n:P:H:L' +LONG_STATD = ['outgoing-port=', 'port=', 'name=', 'state-directory-path=', + 'ha-callout=', 'nlm-port=', 'nlm-udp-port=', 'no-notify'] +CONV_STATD = {'-o': (CONF_NFS, 'statd', 'outgoing-port', '$1'), + '-p': (CONF_NFS, 'statd', 'port', '$1'), + '-T': (CONF_NFS, 'lockd', 'port', '$1'), + '-U': (CONF_NFS, 'lockd', 'udp-port', '$1'), + '-n': (CONF_NFS, 'statd', 'name', '$1'), + '-P': (CONF_NFS, 'statd', 'state-directory-path', '$1'), + '-H': (CONF_NFS, 'statd', 'ha-callout', '$1'), + '-L': (CONF_NFS, 'statd', 'no-notify', '1'), + '--outgoing-port': (CONF_NFS, 'statd', 'outgoing-port', '$1'), + '--port': (CONF_NFS, 'statd', 'port', '$1'), + '--name': (CONF_NFS, 'statd', 'name', '$1'), + '--state-directory-path': (CONF_NFS, 'statd', 'state-directory-path', '$1'), + '--ha-callout': (CONF_NFS, 'statd', 'ha-callout', '$1'), + '--nlm-port': (CONF_NFS, 'lockd', 'port', '$1'), + '--nlm-udp-port': (CONF_NFS, 'lockd', 'udp-port', '$1'), + '--no-notify': (CONF_NFS, 'statd', 'no-notify', '1'), + } + +# options for sm-notify found in SMNOTIFYARGS +OPTS_SMNOTIFY = 'dm:np:v:P:f' +CONV_SMNOTIFY = {'-d': (CONF_NFS, 'sm-notify', 'debug', 'all'), + '-m': (CONF_NFS, 'sm-notify', 'retry-time', '$1'), + '-n': (CONF_NFS, 'sm-notify', 'update-state', '1'), + '-p': (CONF_NFS, 'sm-notify', 'outgoing-port', '$1'), + '-v': (CONF_NFS, 'sm-notify', 'outgoing-addr', '$1'), + '-f': (CONF_NFS, 'sm-notify', 'force', '1'), + '-P': (CONF_NFS, 'statd', 'state-directory-path', '$1'), + } + +# options for idmapd found in RPCIDMAPDARGS +OPTS_IDMAPD = 'vp:CS' +CONV_IDMAPD = {'-v': (CONF_IDMAP, 'general', 'verbosity', '+'), + '-p': (CONF_NFS, 'general', 'pipefs-directory', '$1'), + '-C': (CONF_IDMAP, 'general', 'client-only', '1'), + '-S': (CONF_IDMAP, 'general', 'server-only', '1'), + } + +# options for gssd found in RPCGSSDARGS +OPTS_GSSD = 'Mnvrp:k:d:t:T:R:lD' +CONV_GSSD = {'-M': (CONF_NFS, 'gssd', 'use-memcache', '1'), + '-n': (CONF_NFS, 'gssd', 'root_uses_machine_creds', '0'), + '-v': (CONF_NFS, 'gssd', 'verbosity', '+'), + '-r': (CONF_NFS, 'gssd', 'rpc-verbosity', '+'), + '-p': (CONF_NFS, 'general', 'pipefs-directory', '$1'), + '-k': (CONF_NFS, 'gssd', 'keytab-file', '$1'), + '-d': (CONF_NFS, 'gssd', 'cred-cache-directory', '$1'), + '-t': (CONF_NFS, 'gssd', 'context-timeout', '$1'), + '-T': (CONF_NFS, 'gssd', 'rpc-timeout', '$1'), + '-R': (CONF_NFS, 'gssd', 'preferred-realm', '$1'), + '-l': (CONF_NFS, 'gssd', 'limit-to-legacy-enctypes', '0'), + '-D': (CONF_NFS, 'gssd', 'avoid-dns', '0'), + } + +# options for blkmapd found in BLKMAPDARGS +OPTS_BLKMAPD = '' +CONV_BLKMAPD = {} + +# meta list of all the getopt lists +GETOPT_MAPS = [('RPCNFSDARGS', OPTS_NFSD, LONG_NFSD, CONV_NFSD), + ('RPCMOUNTDOPTS', OPTS_MOUNTD, LONG_MOUNTD, CONV_MOUNTD), + ('STATDARG', OPTS_STATD, LONG_STATD, CONV_STATD), + ('STATDARGS', OPTS_STATD, LONG_STATD, CONV_STATD), + ('SMNOTIFYARGS', OPTS_SMNOTIFY, [], CONV_SMNOTIFY), + ('RPCIDMAPDARGS', OPTS_IDMAPD, [], CONV_IDMAPD), + ('RPCGSSDARGS', OPTS_GSSD, [], CONV_GSSD), + ('BLKMAPDARGS', OPTS_BLKMAPD, [], CONV_BLKMAPD), + ] + +# any fixups we need to apply first +GETOPT_FIXUP = {'RPCNFSDARGS': ('--rdma', '--rdma=nfsrdma'), + } + +# map for all of the single option values +VALUE_MAPS = {'LOCKD_TCPPORT': (CONF_NFS, 'lockd', 'port', '$1'), + 'LOCKD_UDPPORT': (CONF_NFS, 'lockd', 'udp-port', '$1'), + 'RPCNFSDCOUNT': (CONF_NFS, 'nfsd', 'threads', '$1'), + 'NFSD_V4_GRACE': (CONF_NFS, 'nfsd', 'grace-time', '$1'), + 'NFSD_V4_LEASE': (CONF_NFS, 'nfsd', 'lease-time', '$1'), + 'MOUNTD_PORT': (CONF_NFS, 'mountd', 'port', '$1'), + 'STATD_PORT': (CONF_NFS, 'statd', 'port', '$1'), + 'STATD_OUTGOING_PORT': (CONF_NFS, 'statd', 'outgoing-port', '$1'), + 'STATD_HA_CALLOUT': (CONF_NFS, 'statd', 'ha-callout', '$1'), + 'GSS_USE_PROXY': (CONF_NFS, 'gssd', 'use-gss-proxy', '$1') + } + +def eprint(*args, **kwargs): + """ Print error to stderr """ + print(*args, file=sys.stderr, **kwargs) + +def makesub(param, value): + """ Variable substitution """ + return param.replace('$1', value) + +def set_value(value, entry): + """ Set a configuration value by running nfsconf tool""" + cfile, section, tag, param = entry + + tag = makesub(tag, value) + param = makesub(param, value) + if param == '+': + param = value + if param == ',': + param = value + args = [CONF_TOOL, "--file", cfile, "--set", section, tag, param] + + try: + subprocess.check_output(args, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print("Error running nfs-conf tool:\n %s" % (e.output.decode())) + print("Args: %s\n" % args) + raise Exception + +def convert_getopt(optname, options, optstring, longopts, conversions): + """ Parse option string into seperate config items + + Take a getopt string and a table of conversions + parse it all and spit out the converted config + + Keyword arguments: + options -- the argv string to convert + optstring -- getopt format option list + conversions -- table of translations + """ + optcount = 0 + try: + args = options.strip('\"').split() + if optname in GETOPT_FIXUP: + (k, v) = GETOPT_FIXUP[optname] + for i, opt in enumerate(args): + if opt == k: + args[i] = v + elif opt == '--': + break + optlist, optargs = getopt.gnu_getopt(args, optstring, longopts=longopts) + except getopt.GetoptError as err: + eprint(err) + raise Exception + + setlist = {} + for (k, v) in optlist: + if k in conversions: + # it's already been set once + param = conversions[k][3] + tag = k + makesub(conversions[k][2], v) + if tag in setlist: + value = setlist[tag][0] + # is it a cummulative entry + if param == '+': + value = str(int(value) + 1) + if param == ',': + value += "," + v + else: + if param == '+': + value = "1" + elif param == ',': + value = v + else: + value = v + setlist[tag] = (value, conversions[k]) + else: + if v: + eprint("Ignoring unrecognised option %s=%s in %s" % (k, v, optname)) + else: + eprint("Ignoring unrecognised option %s in %s" % (k, optname)) + + + for v, c in setlist.values(): + try: + set_value(v, c) + optcount += 1 + except Exception: + raise + + i = 1 + for o in optargs: + opname = '$' + str(i) + if opname in conversions: + try: + set_value(o, conversions[opname]) + optcount += 1 + except Exception: + raise + else: + eprint("Unrecognised trailing arguments") + raise Exception + i += 1 + + return optcount + +def map_values(): + """ Main function """ + mapcount = 0 + + # Lets load the old config + with open(SYSCONF_NFS) as cfile: + file_content = '[sysconf]\n' + cfile.read() + sysconfig = configparser.RawConfigParser() + sysconfig.read_string(file_content) + + # Map all the getopt option lists + for (name, opts, lopts, conv) in GETOPT_MAPS: + if name in sysconfig['sysconf']: + try: + mapcount += convert_getopt(name, sysconfig['sysconf'][name], opts, + lopts, conv) + except Exception: + eprint("Error whilst converting %s to nfsconf options." % (name)) + raise + + # Map the single value options + for name, opts in VALUE_MAPS.items(): + if name in sysconfig['sysconf']: + try: + value = sysconfig['sysconf'][name] + set_value(value.strip('\"'), opts) + mapcount += 1 + except Exception: + raise + + # All went well, move aside the old file + # but dont bother if there were no changes and + # an old config file already exists + backupfile = SYSCONF_NFS + SYSCONF_BACKUP + if mapcount > 0 or not os.path.exists(backupfile): + try: + os.replace(SYSCONF_NFS, backupfile) + except OSError as err: + eprint("Error moving old config %s: %s" % (SYSCONF_NFS, err)) + raise + +# Main routine +try: + map_values() +except Exception as e: + eprint(e) + eprint("Conversion failed. Please correct the error and try again.") + exit(1) diff --git a/nfsconvert.sh b/nfsconvert.sh new file mode 100644 index 0000000..ce28562 --- /dev/null +++ b/nfsconvert.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# +# Convert /etc/sysconfig/nfs values in /etc/nfs.conf valuse +# + +# +# No file no conversion +# +if [ ! -f /etc/sysconfig/nfs ]; then + exit 0 +fi + +# +# See if the conversion happen already +# +grep "nfs.conf" /etc/sysconfig/nfs > /dev/null +if [ $? -eq 0 ]; then + exit 0 +fi + +if [ -f /etc/nfs.conf.rpmnew ]; then + # See if it is the we want to use + grep tag1234 /etc/nfs.conf.rpmnew > /dev/null + if [ $? -eq 0 ]; then + cp /etc/nfs.conf /etc/nfs.conf.rpmsave + cat /etc/nfs.conf.rpmnew | sed '/tag123/d' > /etc/nfs.conf + rm /etc/nfs.conf.rpmnew + fi +else + cp /etc/nfs.conf /etc/nfs.conf.rpmsave +fi + +# +# Do the conversion +# +/usr/sbin/nfsconvert + diff --git a/sources b/sources new file mode 100644 index 0000000..73bb3aa --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA512 (nfs-utils-2.3.3.tar.xz) = 5025ccd7699ac1a0fdbd8b18ed8b33ea89230158320d809ec51e73f831100db75dceaddde481d911eeca9059caa521d155c2d14d014d75f091f432aad92a9716