diff --git a/SOURCES/lockd.conf b/SOURCES/lockd.conf new file mode 100644 index 0000000..759f31d --- /dev/null +++ b/SOURCES/lockd.conf @@ -0,0 +1,22 @@ +# +# 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/SOURCES/nfs-utils-1.2.5-idmap-errmsg.patch b/SOURCES/nfs-utils-1.2.5-idmap-errmsg.patch index 8e00ed7..f4657e3 100644 --- a/SOURCES/nfs-utils-1.2.5-idmap-errmsg.patch +++ b/SOURCES/nfs-utils-1.2.5-idmap-errmsg.patch @@ -1,12 +1,12 @@ -diff -up nfs-utils-1.2.5/utils/nfsidmap/nfsidmap.c.orig nfs-utils-1.2.5/utils/nfsidmap/nfsidmap.c ---- nfs-utils-1.2.5/utils/nfsidmap/nfsidmap.c.orig 2011-12-13 15:01:26.311660000 -0500 -+++ nfs-utils-1.2.5/utils/nfsidmap/nfsidmap.c 2011-12-13 15:32:48.167354000 -0500 -@@ -261,7 +261,7 @@ int main(int argc, char **argv) +diff -up nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c.orig nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c +--- nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c.orig 2016-04-28 14:17:28.704643000 -0400 ++++ nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c 2016-04-28 14:19:20.004449000 -0400 +@@ -416,7 +416,7 @@ int main(int argc, char **argv) - xlog_stderr(0); + xlog_stderr(verbose); if ((argc - optind) != 2) { -- xlog_err("Bad arg count. Check /etc/request-key.conf"); -+ xlog_err("Bad arg count. Check /etc/request-key.d/id_resolver.conf"); +- 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 1; + return EXIT_FAILURE; } diff --git a/SOURCES/nfs-utils-1.2.9-exportfs-badentries.patch b/SOURCES/nfs-utils-1.2.9-exportfs-badentries.patch new file mode 100644 index 0000000..897a96f --- /dev/null +++ b/SOURCES/nfs-utils-1.2.9-exportfs-badentries.patch @@ -0,0 +1,94 @@ +diff --git a/support/export/client.c b/support/export/client.c +index dbf47b9..0f7b4fe 100644 +--- a/support/export/client.c ++++ b/support/export/client.c +@@ -277,7 +277,7 @@ client_lookup(char *hname, int canonical) + if (htype == MCL_FQDN && !canonical) { + ai = host_addrinfo(hname); + if (!ai) { +- xlog(L_ERROR, "Failed to resolve %s", hname); ++ xlog(L_WARNING, "Failed to resolve %s", hname); + goto out; + } + hname = ai->ai_canonname; +diff --git a/support/export/export.c b/support/export/export.c +index 6b1d045..ce714d4 100644 +--- a/support/export/export.c ++++ b/support/export/export.c +@@ -76,15 +76,22 @@ export_read(char *fname) + struct exportent *eep; + nfs_export *exp; + ++ int volumes = 0; ++ + setexportent(fname, "r"); + while ((eep = getexportent(0,1)) != NULL) { + exp = export_lookup(eep->e_hostname, eep->e_path, 0); +- if (!exp) +- export_create(eep, 0); ++ if (!exp) { ++ exp = export_create(eep, 0); ++ if (exp) ++ volumes++; ++ } + else + warn_duplicated_exports(exp, eep); + } + endexportent(); ++ if (volumes == 0) ++ xlog(L_ERROR, "No file systems exported!"); + } + + /** +diff --git a/support/export/hostname.c b/support/export/hostname.c +index 5f31aee..cdf9e76 100644 +--- a/support/export/hostname.c ++++ b/support/export/hostname.c +@@ -137,11 +137,11 @@ host_pton(const char *paddr) + case EAI_NONAME: + break; + case EAI_SYSTEM: +- xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m", ++ xlog(L_WARNING, "%s: failed to convert %s: (%d) %m", + __func__, paddr, errno); + break; + default: +- xlog(D_GENERAL, "%s: failed to convert %s: %s", ++ xlog(L_WARNING, "%s: failed to convert %s: %s", + __func__, paddr, gai_strerror(error)); + break; + } +@@ -179,11 +179,11 @@ host_addrinfo(const char *hostname) + case 0: + return ai; + case EAI_SYSTEM: +- xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m", ++ xlog(D_PARSE, "%s: failed to resolve %s: (%d) %m", + __func__, hostname, errno); + break; + default: +- xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ xlog(D_PARSE, "%s: failed to resolve %s: %s", + __func__, hostname, gai_strerror(error)); + break; + } +diff --git a/systemd/nfs-server.service b/systemd/nfs-server.service +index f0e456a..7f60f39 100644 +--- a/systemd/nfs-server.service ++++ b/systemd/nfs-server.service +@@ -23,13 +23,13 @@ EnvironmentFile=-/run/sysconfig/nfs-utils + + Type=oneshot + RemainAfterExit=yes +-ExecStartPre=/usr/sbin/exportfs -r ++ExecStartPre=-/usr/sbin/exportfs -r + ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS + 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/SOURCES/nfs-utils-1.3.0-blkmapd-usage.patch b/SOURCES/nfs-utils-1.3.0-blkmapd-usage.patch new file mode 100644 index 0000000..61fa7d6 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-blkmapd-usage.patch @@ -0,0 +1,60 @@ +diff -up nfs-utils-1.3.0/utils/blkmapd/blkmapd.man.save nfs-utils-1.3.0/utils/blkmapd/blkmapd.man +--- nfs-utils-1.3.0/utils/blkmapd/blkmapd.man.save 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/blkmapd/blkmapd.man 2016-05-17 14:12:08.000000000 -0400 +@@ -9,7 +9,7 @@ + .SH NAME + blkmapd \- pNFS block layout mapping daemon + .SH SYNOPSIS +-.B "blkmapd [-d] [-f]" ++.B "blkmapd [-h] [-d] [-f]" + .SH DESCRIPTION + The + .B blkmapd +@@ -33,6 +33,9 @@ reflect the server topology, and passes + by the pNFS block layout client. + .SH OPTIONS + .TP ++.B -h ++Display usage message. ++.TP + .B -d + Performs device discovery only then exits. + .TP +diff -up nfs-utils-1.3.0/utils/blkmapd/device-discovery.c.save nfs-utils-1.3.0/utils/blkmapd/device-discovery.c +--- nfs-utils-1.3.0/utils/blkmapd/device-discovery.c.save 2016-05-17 14:11:36.000000000 -0400 ++++ nfs-utils-1.3.0/utils/blkmapd/device-discovery.c 2016-05-17 14:11:48.000000000 -0400 +@@ -427,7 +427,10 @@ void sig_die(int signal) + BL_LOG_ERR("exit on signal(%d)\n", signal); + exit(1); + } +- ++static void usage(void) ++{ ++ fprintf(stderr, "Usage: blkmapd [-hdf]\n" ); ++} + /* Daemon */ + int main(int argc, char **argv) + { +@@ -435,7 +438,7 @@ int main(int argc, char **argv) + struct stat statbuf; + char pidbuf[64]; + +- while ((opt = getopt(argc, argv, "df")) != -1) { ++ while ((opt = getopt(argc, argv, "hdf")) != -1) { + switch (opt) { + case 'd': + dflag = 1; +@@ -443,6 +446,13 @@ int main(int argc, char **argv) + case 'f': + fg = 1; + break; ++ case 'h': ++ usage(); ++ exit(0); ++ default: ++ usage(); ++ exit(1); ++ + } + } + diff --git a/SOURCES/nfs-utils-1.3.0-exportfs-bufsiz.patch b/SOURCES/nfs-utils-1.3.0-exportfs-bufsiz.patch new file mode 100644 index 0000000..a82e8c8 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-exportfs-bufsiz.patch @@ -0,0 +1,47 @@ +diff -up nfs-utils-1.3.0/utils/exportfs/exportfs.c.orig nfs-utils-1.3.0/utils/exportfs/exportfs.c +--- nfs-utils-1.3.0/utils/exportfs/exportfs.c.orig 2016-05-03 11:06:55.925043000 -0400 ++++ nfs-utils-1.3.0/utils/exportfs/exportfs.c 2016-05-03 11:11:58.680677000 -0400 +@@ -508,9 +508,10 @@ unexportfs(char *arg, int verbose) + + static int can_test(void) + { +- char buf[1024]; ++ char buf[1024] = { 0 }; + int fd; + int n; ++ size_t bufsiz = sizeof(buf); + + fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY); + if (fd < 0) +@@ -523,9 +524,9 @@ static int can_test(void) + * commit 2f74f972 (sunrpc: prepare NFS for 2038). + */ + if (time(NULL) > INT_TO_LONG_THRESHOLD_SECS) +- sprintf(buf, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX); ++ snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %ld -test-client-\n", LONG_MAX); + else +- sprintf(buf, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX); ++ snprintf(buf, bufsiz-1, "nfsd 0.0.0.0 %d -test-client-\n", INT_MAX); + + n = write(fd, buf, strlen(buf)); + close(fd); +@@ -541,7 +542,8 @@ static int can_test(void) + + static int test_export(char *path, int with_fsid) + { +- char buf[1024]; ++ /* beside max path, buf size should take protocol str into account */ ++ char buf[NFS_MAXPATHLEN+1+64] = { 0 }; + char *bp = buf; + int len = sizeof(buf); + int fd, n; +@@ -766,7 +768,8 @@ dumpopt(char c, char *fmt, ...) + static void + dump(int verbose, int export_format) + { +- char buf[1024]; ++ /* buf[] size should >= sizeof(struct exportent->e_path) */ ++ char buf[NFS_MAXPATHLEN+1] = { 0 }; + char *bp; + int len; + nfs_export *exp; diff --git a/SOURCES/nfs-utils-1.3.0-exportfs-empty-exports.patch b/SOURCES/nfs-utils-1.3.0-exportfs-empty-exports.patch new file mode 100644 index 0000000..9d422a4 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-exportfs-empty-exports.patch @@ -0,0 +1,119 @@ +diff --git a/support/export/export.c b/support/export/export.c +index ce714d4..e1bebce 100644 +--- a/support/export/export.c ++++ b/support/export/export.c +@@ -69,8 +69,9 @@ static void warn_duplicated_exports(nfs_export *exp, struct exportent *eep) + * export_read - read entries from /etc/exports + * @fname: name of file to read from + * ++ * Returns number of read entries. + */ +-void ++int + export_read(char *fname) + { + struct exportent *eep; +@@ -82,16 +83,16 @@ export_read(char *fname) + while ((eep = getexportent(0,1)) != NULL) { + exp = export_lookup(eep->e_hostname, eep->e_path, 0); + if (!exp) { +- exp = export_create(eep, 0); +- if (exp) ++ if (export_create(eep, 0)) ++ /* possible complaints already logged */ + volumes++; + } + else + warn_duplicated_exports(exp, eep); + } + endexportent(); +- if (volumes == 0) +- xlog(L_ERROR, "No file systems exported!"); ++ ++ return volumes; + } + + /** +diff --git a/support/include/exportfs.h b/support/include/exportfs.h +index 97b2327..faa9f0b 100644 +--- a/support/include/exportfs.h ++++ b/support/include/exportfs.h +@@ -133,7 +133,7 @@ struct addrinfo * client_resolve(const struct sockaddr *sap); + int client_member(const char *client, + const char *name); + +-void export_read(char *fname); ++int export_read(char *fname); + void export_reset(nfs_export *); + nfs_export * export_lookup(char *hname, char *path, int caconical); + nfs_export * export_find(const struct addrinfo *ai, +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index c06f2aa..b7d8578 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -47,7 +47,7 @@ static void error(nfs_export *exp, int err); + static void usage(const char *progname, int n); + static void validate_export(nfs_export *exp); + static int matchhostname(const char *hostname1, const char *hostname2); +-static void export_d_read(const char *dname); ++static int export_d_read(const char *dname); + static void grab_lockfile(void); + static void release_lockfile(void); + +@@ -185,8 +185,11 @@ main(int argc, char **argv) + atexit(release_lockfile); + + if (f_export && ! f_ignore) { +- export_read(_PATH_EXPORTS); +- export_d_read(_PATH_EXPORTS_D); ++ if (! (export_read(_PATH_EXPORTS) + ++ export_d_read(_PATH_EXPORTS_D))) { ++ if (f_verbose) ++ xlog(L_WARNING, "No file systems exported!"); ++ } + } + if (f_export) { + if (f_all) +@@ -699,21 +702,22 @@ out: + + /* Based on mnt_table_parse_dir() in + util-linux-ng/shlibs/mount/src/tab_parse.c */ +-static void ++static int + export_d_read(const char *dname) + { + int n = 0, i; + struct dirent **namelist = NULL; ++ int volumes = 0; + + + n = scandir(dname, &namelist, NULL, versionsort); + if (n < 0) { + if (errno == ENOENT) + /* Silently return */ +- return; ++ return volumes; + xlog(L_NOTICE, "scandir %s: %s", dname, strerror(errno)); + } else if (n == 0) +- return; ++ return volumes; + + for (i = 0; i < n; i++) { + struct dirent *d = namelist[i]; +@@ -743,14 +747,14 @@ export_d_read(const char *dname) + continue; + } + +- export_read(fname); ++ volumes += export_read(fname); + } + + for (i = 0; i < n; i++) + free(namelist[i]); + free(namelist); + +- return; ++ return volumes; + } + + static char diff --git a/SOURCES/nfs-utils-1.3.0-exportfs-hostnames.patch b/SOURCES/nfs-utils-1.3.0-exportfs-hostnames.patch new file mode 100644 index 0000000..9dce081 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-exportfs-hostnames.patch @@ -0,0 +1,55 @@ +diff --git a/support/export/hostname.c b/support/export/hostname.c +index cdf9e76..816b098 100644 +--- a/support/export/hostname.c ++++ b/support/export/hostname.c +@@ -230,7 +230,7 @@ host_canonname(const struct sockaddr *sap) + default: + (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), + NULL, 0, NI_NUMERICHOST); +- xlog(D_GENERAL, "%s: failed to resolve %s: %s", ++ xlog(D_PARSE, "%s: failed to resolve %s: %s", + __func__, buf, gai_strerror(error)); + return NULL; + } +diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c +index 614c3dc..bc3f00f 100644 +--- a/utils/exportfs/exportfs.c ++++ b/utils/exportfs/exportfs.c +@@ -108,11 +108,14 @@ main(int argc, char **argv) + xlog_stderr(1); + xlog_syslog(0); + +- while ((c = getopt(argc, argv, "afhio:ruvs")) != EOF) { ++ while ((c = getopt(argc, argv, "ad:fhio:ruvs")) != EOF) { + switch(c) { + case 'a': + f_all = 1; + break; ++ case 'd': ++ xlog_sconfig(optarg, 1); ++ break; + case 'f': + force_flush = 1; + break; +@@ -869,6 +872,6 @@ error(nfs_export *exp, int err) + static void + usage(const char *progname, int n) + { +- fprintf(stderr, "usage: %s [-afhioruvs] [host:/path]\n", progname); ++ fprintf(stderr, "usage: %s [-adfhioruvs] [host:/path]\n", progname); + exit(n); + } +diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man +index 75d952a..fdf9260 100644 +--- a/utils/exportfs/exportfs.man ++++ b/utils/exportfs/exportfs.man +@@ -88,6 +88,9 @@ appropriate export entry for the host given in + to be added to the kernel's export table. + .SH OPTIONS + .TP ++.B \-d kind " or " \-\-debug kind ++Turn on debugging. Valid kinds are: all, auth, call, general and parse. ++.TP + .B -a + Export or unexport all directories. + .TP diff --git a/SOURCES/nfs-utils-1.3.0-exportfs-slashes.patch b/SOURCES/nfs-utils-1.3.0-exportfs-slashes.patch new file mode 100644 index 0000000..0c6c6f0 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-exportfs-slashes.patch @@ -0,0 +1,22 @@ +diff -up nfs-utils-1.3.0/utils/exportfs/exportfs.c.orig nfs-utils-1.3.0/utils/exportfs/exportfs.c +--- nfs-utils-1.3.0/utils/exportfs/exportfs.c.orig 2016-05-03 10:50:17.226864000 -0400 ++++ nfs-utils-1.3.0/utils/exportfs/exportfs.c 2016-05-03 10:51:49.848199000 -0400 +@@ -402,8 +402,17 @@ unexportfs_parsed(char *hname, char *pat + hname = ai->ai_canonname; + } + ++ /* ++ * It's possible the specified path ends with a '/'. But ++ * the entry from exportlist won't has the trailing '/', ++ * so need to deal with it. ++ */ ++ size_t nlen = strlen(path); ++ while (path[nlen - 1] == '/') ++ nlen--; ++ + for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) { +- if (path && strcmp(path, exp->m_export.e_path)) ++ if (path && strncmp(path, exp->m_export.e_path, nlen)) + continue; + if (htype != exp->m_client->m_type) + continue; diff --git a/SOURCES/nfs-utils-1.3.0-mount-nfs-types.patch b/SOURCES/nfs-utils-1.3.0-mount-nfs-types.patch new file mode 100644 index 0000000..f2d199c --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mount-nfs-types.patch @@ -0,0 +1,43 @@ +commit d1f6583a5ff32711a1da1d4a13a29a5700f63504 +Author: Ben Hutchings +Date: Thu Apr 2 11:43:33 2015 -0400 + + mount.nfs.man, nfs.man: Update distinction between fstypes + + From: Ben Hutchings + + this is a resync of the man page updates in the Debian + package with mainline nfs-utils. + + Acked-By: J. Bruce Fields + Signed-off-by: Steve Dickson + +diff --git a/utils/mount/mount.nfs.man b/utils/mount/mount.nfs.man +index 1a4561b..15a82d5 100644 +--- a/utils/mount/mount.nfs.man ++++ b/utils/mount/mount.nfs.man +@@ -15,16 +15,20 @@ is meant to be used by the + .BR mount (8) + command for mounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality. + +-.BR mount.nfs4 +-is used for mounting NFSv4 file system, while +-.BR mount.nfs +-is used to mount NFS file systems versions 3 or 2. + .I remotetarget + is a server share usually in the form of + .BR servername:/path/to/share. + .I dir + is the directory on which the file system is to be mounted. + ++Under Linux 2.6.32 and later kernel versions, ++.BR mount.nfs ++can mount all NFS file system versions. Under earlier Linux kernel versions, ++.BR mount.nfs4 ++must be used for mounting NFSv4 file systems while ++.BR mount.nfs ++must be used for NFSv3 and v2. ++ + .SH OPTIONS + .TP + .BI "\-r" diff --git a/SOURCES/nfs-utils-1.3.0-mount-remount.patch b/SOURCES/nfs-utils-1.3.0-mount-remount.patch new file mode 100644 index 0000000..da8b190 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mount-remount.patch @@ -0,0 +1,48 @@ +diff -up nfs-utils-1.3.0/utils/mount/stropts.c.orig nfs-utils-1.3.0/utils/mount/stropts.c +--- nfs-utils-1.3.0/utils/mount/stropts.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/mount/stropts.c 2016-04-28 11:18:25.876793000 -0400 +@@ -352,13 +352,26 @@ static int nfs_validate_options(struct n + if (!nfs_nfs_proto_family(mi->options, &family)) + return 0; + +- hint.ai_family = (int)family; +- error = getaddrinfo(mi->hostname, NULL, &hint, &mi->address); +- if (error != 0) { +- nfs_error(_("%s: Failed to resolve server %s: %s"), +- progname, mi->hostname, gai_strerror(error)); +- mi->address = NULL; +- return 0; ++ /* ++ * A remount is not going to be able to change the server's address, ++ * nor should we try to resolve another address for the server as we ++ * may end up with a different address. ++ */ ++ if (mi->flags & MS_REMOUNT) { ++ po_remove_all(mi->options, "addr"); ++ } else { ++ hint.ai_family = (int)family; ++ error = getaddrinfo(mi->hostname, NULL, &hint, &mi->address); ++ if (error != 0) { ++ nfs_error(_("%s: Failed to resolve server %s: %s"), ++ progname, mi->hostname, gai_strerror(error)); ++ mi->address = NULL; ++ return 0; ++ } ++ ++ if (!nfs_append_addr_option(mi->address->ai_addr, ++ mi->address->ai_addrlen, mi->options)) ++ return 0; + } + + if (!nfs_set_version(mi)) +@@ -367,10 +380,6 @@ static int nfs_validate_options(struct n + if (!nfs_append_sloppy_option(mi->options)) + return 0; + +- if (!nfs_append_addr_option(mi->address->ai_addr, +- mi->address->ai_addrlen, mi->options)) +- return 0; +- + return 1; + } + diff --git a/SOURCES/nfs-utils-1.3.0-mount-usage.patch b/SOURCES/nfs-utils-1.3.0-mount-usage.patch new file mode 100644 index 0000000..4240fb5 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mount-usage.patch @@ -0,0 +1,19 @@ +diff -up nfs-utils-1.3.0/utils/mount/mount_libmount.c.orig nfs-utils-1.3.0/utils/mount/mount_libmount.c +--- nfs-utils-1.3.0/utils/mount/mount_libmount.c.orig 2016-06-08 09:48:19.446035537 -0400 ++++ nfs-utils-1.3.0/utils/mount/mount_libmount.c 2016-06-08 09:57:58.389518986 -0400 +@@ -207,6 +207,7 @@ static int umount_main(struct libmnt_con + + if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) { + nfs_error(_("%s: no mount point provided"), progname); ++ umount_usage(); + return EX_USAGE; + } + +@@ -328,6 +329,7 @@ static int mount_main(struct libmnt_cont + + if (!mount_point) { + nfs_error(_("%s: no mount point provided"), progname); ++ mount_usage(); + goto err; + } + if (!spec) { diff --git a/SOURCES/nfs-utils-1.3.0-mountd-manpage-P.patch b/SOURCES/nfs-utils-1.3.0-mountd-manpage-P.patch new file mode 100644 index 0000000..8e56689 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountd-manpage-P.patch @@ -0,0 +1,27 @@ +commit d0f9df9761d5fc63327fcaa0bc4515e739ca6f0e +Author: Yongcheng Yang +Date: Wed Mar 16 12:15:32 2016 -0400 + + mountd.man: Update to change -P option as an alias for -p + + From: Yongcheng Yang + + Signed-off-by: Yongcheng Yang + Signed-off-by: Steve Dickson + +diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man +index 7c5bfbe..66e3bba 100644 +--- a/utils/mountd/mountd.man ++++ b/utils/mountd/mountd.man +@@ -115,10 +115,7 @@ must be invoked with the option + .B \-n " or " \-\-no-tcp + Don't advertise TCP for mount. + .TP +-.B \-P +-Ignored (compatibility with unfsd??). +-.TP +-.B \-p num " or " \-\-port num ++.B \-p num " or " \-P num " or " \-\-port num + Specifies the port number used for RPC listener sockets. + If this option is not specified, + .B rpc.mountd diff --git a/SOURCES/nfs-utils-1.3.0-mountd-netgroups.patch b/SOURCES/nfs-utils-1.3.0-mountd-netgroups.patch new file mode 100644 index 0000000..fbab37c --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountd-netgroups.patch @@ -0,0 +1,31 @@ +diff -up nfs-utils-1.3.0/support/export/client.c.save nfs-utils-1.3.0/support/export/client.c +--- nfs-utils-1.3.0/support/export/client.c.save 2016-06-07 14:02:11.244677000 -0400 ++++ nfs-utils-1.3.0/support/export/client.c 2016-06-07 14:03:11.380193000 -0400 +@@ -635,7 +635,7 @@ check_netgroup(const nfs_client *clp, co + const char *netgroup = clp->m_hostname + 1; + struct addrinfo *tmp = NULL; + struct hostent *hp; +- char *dot, *hname; ++ char *dot, *hname, *ip; + int i, match; + + match = 0; +@@ -682,6 +682,18 @@ check_netgroup(const nfs_client *clp, co + } + } + ++ /* check whether the IP itself is in the netgroup */ ++ ip = calloc(INET6_ADDRSTRLEN, 1); ++ if (inet_ntop(ai->ai_family, &(((struct sockaddr_in *)ai->ai_addr)->sin_addr), ip, INET6_ADDRSTRLEN) == ip) { ++ if (innetgr(netgroup, ip, NULL, NULL)) { ++ free(hname); ++ hname = ip; ++ match = 1; ++ goto out; ++ } ++ } ++ free(ip); ++ + /* Okay, strip off the domain (if we have one) */ + dot = strchr(hname, '.'); + if (dot == NULL) diff --git a/SOURCES/nfs-utils-1.3.0-mountd-root.patch b/SOURCES/nfs-utils-1.3.0-mountd-root.patch new file mode 100644 index 0000000..eae1f14 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountd-root.patch @@ -0,0 +1,157 @@ +commit 710a492fda68092a02d6360d7a185f6a4dcaea85 +Author: NeilBrown +Date: Thu Feb 26 14:10:35 2015 -0500 + + exports.man: improve documentation of 'nohide' and 'crossmnt' + + - note that 'nohide' is irrelevant for NFSv4 + - note that children on a 'crossmnt' filesystem cannot be unexported + - note that 'nocrossmnt' is a valid option, but probably not useful. + + Signed-off-by: NeilBrown + Signed-off-by: Steve Dickson + +diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man +index 59358e6..9309246 100644 +--- a/utils/exportfs/exports.man ++++ b/utils/exportfs/exports.man +@@ -218,16 +218,46 @@ This option can be very useful in some situations, but it should be + used with due care, and only after confirming that the client system + copes with the situation effectively. + +-The option can be explicitly disabled with ++The option can be explicitly disabled for NFSv2 and NFSv3 with + .IR hide . ++ ++This option is not relevant when NFSv4 is use. NFSv4 never hides ++subordinate filesystems. Any filesystem that is exported will be ++visible where expected when using NFSv4. + .TP +-.IR crossmnt ++.I crossmnt + This option is similar to + .I nohide +-but it makes it possible for clients to move from the filesystem marked +-with crossmnt to exported filesystems mounted on it. Thus when a child +-filesystem "B" is mounted on a parent "A", setting crossmnt on "A" has +-the same effect as setting "nohide" on B. ++but it makes it possible for clients to access all filesystems mounted ++on a filesystem marked with ++.IR crossmnt . ++Thus when a child filesystem "B" is mounted on a parent "A", setting ++crossmnt on "A" has a similar effect to setting "nohide" on B. ++ ++With ++.I nohide ++the child filesystem needs to be explicitly exported. With ++.I crossmnt ++it need not. If a child of a ++.I crossmnt ++file is not explicitly exported, then it will be implicitly exported ++with the same export options as the parent, except for ++.IR fsid= . ++This makes it impossible to ++.B not ++export a child of a ++.I crossmnt ++filesystem. If some but not all subordinate filesystems of a parent ++are to be exported, then they must be explicitly exported and the ++parent should not have ++.I crossmnt ++set. ++ ++The ++.I nocrossmnt ++option can explictly disable ++.I crossmnt ++if it was previously set. This is rarely useful. + .TP + .IR no_subtree_check + This option disables subtree checking, which has mild security + +commit b7341b19d62481504f1820414159009535d37809 +Author: NeilBrown +Date: Wed Feb 25 16:47:56 2015 -0500 + + mountd: fix next_mnt handling for "/" + + If the (exported) path passed to next_mnt() is simply "/", next_mnt() + will not report any children, as none start with "/" followed by a '/'. + So make a special case for strlen(p)==1. In that case, return all + children. + + This gives correct handling if only "/" is exported. + + Signed-off-by: NeilBrown + Signed-off-by: Steve Dickson + +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index 1430aee..23af4a9 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -365,7 +365,7 @@ static char *next_mnt(void **v, char *p) + *v = f; + } else + f = *v; +- while ((me = getmntent(f)) != NULL && ++ while ((me = getmntent(f)) != NULL && l > 1 && + (strncmp(me->mnt_dir, p, l) != 0 || + me->mnt_dir[l] != '/')) + ; + +commit 7e27d4a542bf97e0ddc1036010e1b2d218a01c2b +Author: Vivek Trivedi +Date: Wed Sep 16 11:14:03 2015 -0400 + + mountd: fix mount issue due to comparison with uninitialized uuid + + Fix mount issue due to comparison of uninitialized variable + u(uuid) with parsed->fhuuid when uuid_by_path return 0. + + /tmp/usb + 192.168.1.0/16(ro,no_root_squash,no_subtree_check,fsid=0) + /tmp/usb/sda1 192.168.1.0/16(ro,no_root_squash,no_subtree_check) + /tmp/usb/sdb1 192.168.1.0/16(ro,no_root_squash,no_subtree_check) + + mount -t nfs -o nolock,nfsvers=3 192.168.1.2:/tmp/usb/sda1 /tmp/sda1 + mount -t nfs -o nolock,nfsvers=3 192.168.1.2:/tmp/usb/sdb1 /tmp/sdb1 + + results in below mountd error: + mountd: /tmp/usb and /tmp/usb/sdb1 have same filehandle for + 192.168.1.0/16, using first + + when uuid_by_path returned 0, by chance, garbage value of u was same as + parsed->fhuuid(of sdb1), and comparison of these resulted in above + error. + + Signed-off-by: Vivek Trivedi + Reviewed-by: Amit Sahrawat + Signed-off-by: Steve Dickson + +diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c +index 9a1bb27..1430aee 100644 +--- a/utils/mountd/cache.c ++++ b/utils/mountd/cache.c +@@ -547,18 +547,17 @@ static bool match_fsid(struct parsed_fsid *parsed, nfs_export *exp, char *path) + if (!is_mountpoint(path)) + return false; + check_uuid: +- if (exp->m_export.e_uuid) ++ if (exp->m_export.e_uuid) { + get_uuid(exp->m_export.e_uuid, parsed->uuidlen, u); ++ if (memcmp(u, parsed->fhuuid, parsed->uuidlen) == 0) ++ return true; ++ } + else + for (type = 0; + uuid_by_path(path, type, parsed->uuidlen, u); + type++) + if (memcmp(u, parsed->fhuuid, parsed->uuidlen) == 0) + return true; +- +- if (memcmp(u, parsed->fhuuid, parsed->uuidlen) != 0) +- return false; +- return true; + } + /* Well, unreachable, actually: */ + return false; diff --git a/SOURCES/nfs-utils-1.3.0-mountd-usage-error.patch b/SOURCES/nfs-utils-1.3.0-mountd-usage-error.patch new file mode 100644 index 0000000..d127a5c --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountd-usage-error.patch @@ -0,0 +1,25 @@ +commit 6ec0d58fbff7c1a814a21cf8c7fe0ef5106a04c1 +Author: Steve Dickson +Date: Wed Jan 20 14:16:08 2016 -0500 + + mountd: print an error message when no versions are specified + + Signed-off-by: Steve Dickson + +diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c +index 8aca181..b584afc 100644 +--- a/utils/mountd/mountd.c ++++ b/utils/mountd/mountd.c +@@ -794,9 +794,10 @@ main(int argc, char **argv) + } + + /* No more arguments allowed. */ +- if (optind != argc || !version_any()) ++ if (optind != argc || !version_any()) { ++ fprintf(stderr, "%s: No protocol versions specified!\n", progname); + usage(progname, 1); +- ++ } + if (chdir(state_dir)) { + fprintf(stderr, "%s: chdir(%s) failed: %s\n", + progname, state_dir, strerror(errno)); diff --git a/SOURCES/nfs-utils-1.3.0-mountd-usage.patch b/SOURCES/nfs-utils-1.3.0-mountd-usage.patch new file mode 100644 index 0000000..2d674ca --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountd-usage.patch @@ -0,0 +1,14 @@ +diff -up nfs-utils-1.3.0/utils/mountd/mountd.c.orig nfs-utils-1.3.0/utils/mountd/mountd.c +--- nfs-utils-1.3.0/utils/mountd/mountd.c.orig 2016-02-11 09:57:51.376317000 -0500 ++++ nfs-utils-1.3.0/utils/mountd/mountd.c 2016-02-11 10:02:54.377956000 -0500 +@@ -911,7 +911,8 @@ usage(const char *prog, int n) + " [-o num|--descriptors num] [-f exports-file|--exports-file=file]\n" + " [-p|--port port] [-V version|--nfs-version version]\n" + " [-N version|--no-nfs-version version] [-n|--no-tcp]\n" +-" [-H ha-callout-prog] [-s|--state-directory-path path]\n" +-" [-g|--manage-gids] [-t num|--num-threads=num] [-u|--no-udp]\n", prog); ++" [-H prog |--ha-callout prog] [-r |--reverse-lookup]\n" ++" [-s|--state-directory-path path] [-g|--manage-gids]\n" ++" [-t num|--num-threads=num] [-u|--no-udp]\n", prog); + exit(n); + } diff --git a/SOURCES/nfs-utils-1.3.0-mountstats-manpage-fix.patch b/SOURCES/nfs-utils-1.3.0-mountstats-manpage-fix.patch new file mode 100644 index 0000000..fc2fbb8 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountstats-manpage-fix.patch @@ -0,0 +1,36 @@ +commit f2efa15ef8c083926791195ed1037fd47a5e4e41 +Author: Scott Mayhew +Date: Wed Sep 16 11:19:41 2015 -0400 + + mountstats.man: Remove a few bogus .R macros + + These have no effect on the rendering of the man page, but they do cause + the following error if you try to pipe or redirect the output: + + `R' is a string (producing the registered sign), not a macro. + + Signed-off-by: Scott Mayhew + Signed-off-by: Steve Dickson + +diff --git a/tools/mountstats/mountstats.man b/tools/mountstats/mountstats.man +index bfc4c46..a55130b 100644 +--- a/tools/mountstats/mountstats.man ++++ b/tools/mountstats/mountstats.man +@@ -13,13 +13,13 @@ mountstats \- Displays various NFS client per-mount statistics + .RB [ \-S | \-\-since + .IR sincefile ] + .\" .RB [ \-n | \-\-nfs | \-r | \-\-rpc | \-R | \-\-raw ] +-.R [ ++[ + .RB [ \-n | \-\-nfs ] +-.R | ++| + .RB [ \-r | \-\-rpc ] +-.R | ++| + .RB [ \-R | \-\-raw ] +-.R ] ++] + .RI [ mountpoint ] ... + .P + .B mountstats iostat diff --git a/SOURCES/nfs-utils-1.3.0-mountstats-manpage-fix2.patch b/SOURCES/nfs-utils-1.3.0-mountstats-manpage-fix2.patch new file mode 100644 index 0000000..bc4fa4a --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-mountstats-manpage-fix2.patch @@ -0,0 +1,21 @@ +commit 8c7d7fe5577b059130d472d4145a09cf9d4a7ea2 +Author: Steve Dickson +Date: Mon Aug 10 09:58:01 2015 -0400 + + mountstats.man: fixed typo in man page + + Signed-off-by: Steve Dickson + +diff --git a/tools/mountstats/mountstats.man b/tools/mountstats/mountstats.man +index a9df1e4..bfc4c46 100644 +--- a/tools/mountstats/mountstats.man ++++ b/tools/mountstats/mountstats.man +@@ -115,7 +115,7 @@ options. + Specifies the amount of time in seconds between each report. The first report contains statistics for the time since each file system was mounted. Each subsequent report contains statistics collected during the interval since the previous report. This may not be used with the + .BR \-f | \-\-file + or +-.BR \-s | \-\-since ++.BR \-S | \-\-since + options. + .P + .IP "\fIcount\fP" diff --git a/SOURCES/nfs-utils-1.3.0-nfs_connect_nb-eintr.patch b/SOURCES/nfs-utils-1.3.0-nfs_connect_nb-eintr.patch new file mode 100644 index 0000000..1f1f49a --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-nfs_connect_nb-eintr.patch @@ -0,0 +1,33 @@ +diff -up nfs-utils-1.3.0/support/nfs/rpc_socket.c.orig nfs-utils-1.3.0/support/nfs/rpc_socket.c +--- nfs-utils-1.3.0/support/nfs/rpc_socket.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/support/nfs/rpc_socket.c 2016-04-28 11:37:53.694236000 -0400 +@@ -215,7 +215,7 @@ static int nfs_connect_nb(const int fd, + * use it later. + */ + ret = connect(fd, sap, salen); +- if (ret < 0 && errno != EINPROGRESS) { ++ if (ret < 0 && errno != EINPROGRESS && errno != EINTR) { + ret = -1; + goto done; + } +@@ -227,10 +227,16 @@ static int nfs_connect_nb(const int fd, + FD_ZERO(&rset); + FD_SET(fd, &rset); + +- ret = select(fd + 1, NULL, &rset, NULL, timeout); +- if (ret <= 0) { +- if (ret == 0) +- errno = ETIMEDOUT; ++ while ((ret = select(fd + 1, NULL, &rset, NULL, timeout)) < 0) { ++ if (errno != EINTR) { ++ ret = -1; ++ goto done; ++ } else { ++ continue; ++ } ++ } ++ if (ret == 0) { ++ errno = ETIMEDOUT; + ret = -1; + goto done; + } diff --git a/SOURCES/nfs-utils-1.3.0-nfsd-rdma.patch b/SOURCES/nfs-utils-1.3.0-nfsd-rdma.patch new file mode 100644 index 0000000..f9b5827 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-nfsd-rdma.patch @@ -0,0 +1,12 @@ +diff -up nfs-utils-1.3.0/utils/nfsd/nfssvc.c.orig nfs-utils-1.3.0/utils/nfsd/nfssvc.c +--- nfs-utils-1.3.0/utils/nfsd/nfssvc.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/nfsd/nfssvc.c 2016-05-17 10:42:05.000000000 -0400 +@@ -278,7 +278,7 @@ nfssvc_set_rdmaport(const char *port) + int fd; + + if (sv) +- nport = sv->s_port; ++ nport = ntohs(sv->s_port); + else { + char *ep; + nport = strtol(port, &ep, 10); diff --git a/SOURCES/nfs-utils-1.3.0-nfsd-warnings.patch b/SOURCES/nfs-utils-1.3.0-nfsd-warnings.patch new file mode 100644 index 0000000..71879bc --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-nfsd-warnings.patch @@ -0,0 +1,26 @@ +commit a2e431fdd114f2c2466573471dafef9024392f2d +Author: Steve Dickson +Date: Wed Apr 30 12:26:06 2014 -0400 + + nfsd: Remove some warnings nfsd.c + + nfsd.c:347:15: warning: comparison between signed and unsigned integer + expressions [-Wsign-compare] + nfsd.c:385:13: warning: comparison between signed and unsigned integer + expressions [-Wsign-compare] + + Signed-off-by: Steve Dickson + +diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c +index 73d6a92..03e3c81 100644 +--- a/utils/nfsd/nfsd.c ++++ b/utils/nfsd/nfsd.c +@@ -101,7 +101,7 @@ main(int argc, char **argv) + int count = NFSD_NPROC, c, i, error = 0, portnum = 0, fd, found_one; + char *p, *progname, *port, *rdma_port = NULL; + char **haddr = NULL; +- unsigned int hcounter = 0; ++ int hcounter = 0; + int socket_up = 0; + unsigned int minorvers = 0; + unsigned int minorversset = 0; diff --git a/SOURCES/nfs-utils-1.3.0-nfsidmap-h-opt.patch b/SOURCES/nfs-utils-1.3.0-nfsidmap-h-opt.patch new file mode 100644 index 0000000..646f4c0 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-nfsidmap-h-opt.patch @@ -0,0 +1,55 @@ +diff -up nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c.orig nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c +--- nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c.orig 2016-06-08 09:59:09.920690710 -0400 ++++ nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c 2016-06-08 11:22:01.104971559 -0400 +@@ -16,7 +16,7 @@ + #include "conffile.h" + + int verbose = 0; +-char *usage = "Usage: %s [-v] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]"; ++char *usage = "Usage: %s [-vh] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]"; + + #define MAX_ID_LEN 11 + #define IDMAP_NAMESZ 128 +@@ -355,7 +355,7 @@ int main(int argc, char **argv) + + xlog_open(progname); + +- while ((opt = getopt(argc, argv, "du:g:r:ct:vl")) != -1) { ++ while ((opt = getopt(argc, argv, "hdu:g:r:ct:vl")) != -1) { + switch (opt) { + case 'd': + display++; +@@ -384,9 +384,10 @@ int main(int argc, char **argv) + case 't': + timeout = atoi(optarg); + break; ++ case 'h': + default: + xlog_warn(usage, progname); +- break; ++ exit(opt == 'h' ? 0 : 1); + } + } + +diff -up nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.man.orig nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.man +--- nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.man.orig 2016-06-08 09:59:09.899690366 -0400 ++++ nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.man 2016-06-08 11:22:01.104971559 -0400 +@@ -15,6 +15,8 @@ nfsidmap \- The NFS idmapper upcall prog + .B "nfsidmap -d" + .br + .B "nfsidmap -l" ++.br ++.B "nfsidmap -h" + .SH DESCRIPTION + The NFSv4 protocol represents the local system's UID and GID values + on the wire as strings of the form +@@ -71,6 +73,9 @@ Display the system's effective NFSv4 dom + .B -g user + Revoke the gid key of the given user. + .TP ++.B -h ++Display usage message. ++.TP + .B -l + Display on + .I stdout diff --git a/SOURCES/nfs-utils-1.3.0-nfsidmap-timeout.patch b/SOURCES/nfs-utils-1.3.0-nfsidmap-timeout.patch new file mode 100644 index 0000000..ddae35b --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-nfsidmap-timeout.patch @@ -0,0 +1,22 @@ +diff -up nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c.save nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c +--- nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c.save 2015-11-24 09:29:53.332040370 -0500 ++++ nfs-utils-1.3.0/utils/nfsidmap/nfsidmap.c 2015-11-24 09:30:06.579249849 -0500 +@@ -24,7 +24,7 @@ char *usage="Usage: %s [-v] [-c || [-u|- + + #define PROCKEYS "/proc/keys" + #ifndef DEFAULT_KEYRING +-#define DEFAULT_KEYRING "id_resolver" ++#define DEFAULT_KEYRING ".id_resolver" + #endif + + #ifndef PATH_IDMAPDCONF +@@ -315,6 +315,9 @@ int main(int argc, char **argv) + key, type, value, timeout); + } + ++ /* Become a possesor of the to-be-instantiated key to set the key's timeout */ ++ request_key("keyring", DEFAULT_KEYRING, NULL, KEY_SPEC_THREAD_KEYRING); ++ + if (strcmp(type, "uid") == 0) + rc = id_lookup(value, key, USER); + else if (strcmp(type, "gid") == 0) diff --git a/SOURCES/nfs-utils-1.3.0-nfsidmap-update.patch b/SOURCES/nfs-utils-1.3.0-nfsidmap-update.patch new file mode 100644 index 0000000..e6d1867 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-nfsidmap-update.patch @@ -0,0 +1,574 @@ +diff --git a/aclocal/keyutils.m4 b/aclocal/keyutils.m4 +index a392c0e..16b225d 100644 +--- a/aclocal/keyutils.m4 ++++ b/aclocal/keyutils.m4 +@@ -8,4 +8,8 @@ AC_DEFUN([AC_KEYUTILS], [ + + AC_CHECK_HEADERS([keyutils.h]) + ++ AC_CHECK_LIB([keyutils], [find_key_by_type_and_desc], ++ [AC_DEFINE([HAVE_FIND_KEY_BY_TYPE_AND_DESC], [1], ++ [Define to 1 if you have the `find_key_by_type_and_desc' function.])],) ++ + ])dnl +diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c +index 10f69f9..9c49d42 100644 +--- a/utils/nfsidmap/nfsidmap.c ++++ b/utils/nfsidmap/nfsidmap.c +@@ -1,3 +1,4 @@ ++#include "config.h" + + #include + #include +@@ -15,7 +16,7 @@ + #include "conffile.h" + + int verbose = 0; +-char *usage="Usage: %s [-v] [-c || [-u|-g|-r key] || [-t timeout] key desc]"; ++char *usage = "Usage: %s [-v] [-c || [-u|-g|-r key] || -d || -l || [-t timeout] key desc]"; + + #define MAX_ID_LEN 11 + #define IDMAP_NAMESZ 128 +@@ -31,15 +32,163 @@ char *usage="Usage: %s [-v] [-c || [-u|-g|-r key] || [-t timeout] key desc]"; + #define PATH_IDMAPDCONF "/etc/idmapd.conf" + #endif + +-static int keyring_clear(char *keyring); +- + #define UIDKEYS 0x1 + #define GIDKEYS 0x2 + ++#ifndef HAVE_FIND_KEY_BY_TYPE_AND_DESC ++static key_serial_t find_key_by_type_and_desc(const char *type, ++ const char *desc, key_serial_t destringid) ++{ ++ char buf[BUFSIZ]; ++ key_serial_t key; ++ FILE *fp; ++ ++ if ((fp = fopen(PROCKEYS, "r")) == NULL) { ++ xlog_err("fopen(%s) failed: %m", PROCKEYS); ++ return -1; ++ } ++ ++ key = -1; ++ while(fgets(buf, BUFSIZ, fp) != NULL) { ++ unsigned int id; ++ ++ if (strstr(buf, type) == NULL) ++ continue; ++ if (strstr(buf, desc) == NULL) ++ continue; ++ if (sscanf(buf, "%x %*s", &id) != 1) { ++ xlog_err("Unparsable keyring entry in %s", PROCKEYS); ++ continue; ++ } ++ ++ key = (key_serial_t)id; ++ break; ++ } ++ ++ fclose(fp); ++ return key; ++} ++#endif ++ ++/* ++ * Clear all the keys on the given keyring ++ */ ++static int keyring_clear(const char *keyring) ++{ ++ key_serial_t key; ++ ++ key = find_key_by_type_and_desc("keyring", keyring, 0); ++ if (key == -1) { ++ if (verbose) ++ xlog_warn("'%s' keyring was not found.", keyring); ++ return EXIT_SUCCESS; ++ } ++ ++ if (keyctl_clear(key) < 0) { ++ xlog_err("keyctl_clear(0x%x) failed: %m", ++ (unsigned int)key); ++ return EXIT_FAILURE; ++ } ++ ++ if (verbose) ++ xlog_warn("'%s' cleared", keyring); ++ return EXIT_SUCCESS; ++} ++ ++static int display_default_domain(void) ++{ ++ char domain[NFS4_MAX_DOMAIN_LEN]; ++ int rc; ++ ++ rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN); ++ if (rc) { ++ xlog_errno(rc, "nfs4_get_default_domain failed: %m"); ++ return EXIT_FAILURE; ++ } ++ ++ printf("%s\n", domain); ++ return EXIT_SUCCESS; ++} ++ ++static void list_key(key_serial_t key) ++{ ++ char *buffer, *c; ++ int rc; ++ ++ rc = keyctl_describe_alloc(key, &buffer); ++ if (rc < 0) { ++ switch (errno) { ++ case EKEYEXPIRED: ++ printf("Expired key not displayed\n"); ++ break; ++ default: ++ xlog_err("Failed to describe key: %m"); ++ } ++ return; ++ } ++ ++ c = strrchr(buffer, ';'); ++ if (!c) { ++ xlog_err("Unparsable key not displayed\n"); ++ goto out_free; ++ } ++ printf(" %s\n", ++c); ++ ++out_free: ++ free(buffer); ++} ++ ++static void list_keys(const char *ring_name, key_serial_t ring_id) ++{ ++ key_serial_t *key; ++ void *keylist; ++ int count; ++ ++ count = keyctl_read_alloc(ring_id, &keylist); ++ if (count < 0) { ++ xlog_err("Failed to read keyring %s: %m", ring_name); ++ return; ++ } ++ count /= (int)sizeof(*key); ++ ++ switch (count) { ++ case 0: ++ printf("No %s keys found.\n", ring_name); ++ break; ++ case 1: ++ printf("1 %s key found:\n", ring_name); ++ break; ++ default: ++ printf("%u %s keys found:\n", count, ring_name); ++ } ++ ++ for (key = keylist; count--; key++) ++ list_key(*key); ++ ++ free(keylist); ++} ++ ++/* ++ * List all keys on a keyring ++ */ ++static int list_keyring(const char *keyring) ++{ ++ key_serial_t key; ++ ++ key = find_key_by_type_and_desc("keyring", keyring, 0); ++ if (key == -1) { ++ xlog_err("'%s' keyring was not found.", keyring); ++ return EXIT_FAILURE; ++ } ++ ++ list_keys(keyring, key); ++ return EXIT_SUCCESS; ++} ++ + /* + * Find either a user or group id based on the name@domain string + */ +-int id_lookup(char *name_at_domain, key_serial_t key, int type) ++static int id_lookup(char *name_at_domain, key_serial_t key, int type) + { + char id[MAX_ID_LEN]; + uid_t uid = 0; +@@ -53,30 +202,33 @@ int id_lookup(char *name_at_domain, key_serial_t key, int type) + rc = nfs4_group_owner_to_gid(name_at_domain, &gid); + sprintf(id, "%u", gid); + } +- if (rc < 0) ++ if (rc < 0) { + xlog_errno(rc, "id_lookup: %s: failed: %m", + (type == USER ? "nfs4_owner_to_uid" : "nfs4_group_owner_to_gid")); ++ return EXIT_FAILURE; ++ } + +- if (rc == 0) { +- rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); +- if (rc < 0) { +- switch(rc) { +- case -EDQUOT: +- case -ENFILE: +- case -ENOMEM: +- /* +- * The keyring is full. Clear the keyring and try again +- */ +- rc = keyring_clear(DEFAULT_KEYRING); +- if (rc == 0) +- rc = keyctl_instantiate(key, id, strlen(id) + 1, 0); +- break; +- default: ++ rc = EXIT_SUCCESS; ++ if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) { ++ switch (errno) { ++ case EDQUOT: ++ case ENFILE: ++ case ENOMEM: ++ /* ++ * The keyring is full. Clear the keyring and try again ++ */ ++ rc = keyring_clear(DEFAULT_KEYRING); ++ if (rc) + break; ++ if (keyctl_instantiate(key, id, strlen(id) + 1, 0)) { ++ rc = EXIT_FAILURE; ++ xlog_err("id_lookup: keyctl_instantiate failed: %m"); + } ++ break; ++ default: ++ rc = EXIT_FAILURE; ++ break; + } +- if (rc < 0) +- xlog_err("id_lookup: keyctl_instantiate failed: %m"); + } + + return rc; +@@ -85,7 +237,7 @@ int id_lookup(char *name_at_domain, key_serial_t key, int type) + /* + * Find the name@domain string from either a user or group id + */ +-int name_lookup(char *id, key_serial_t key, int type) ++static int name_lookup(char *id, key_serial_t key, int type) + { + char name[IDMAP_NAMESZ]; + char domain[NFS4_MAX_DOMAIN_LEN]; +@@ -94,11 +246,10 @@ int name_lookup(char *id, key_serial_t key, int type) + int rc; + + rc = nfs4_get_default_domain(NULL, domain, NFS4_MAX_DOMAIN_LEN); +- if (rc != 0) { ++ if (rc) { + xlog_errno(rc, + "name_lookup: nfs4_get_default_domain failed: %m"); +- rc = -1; +- goto out; ++ return EXIT_FAILURE; + } + + if (type == USER) { +@@ -108,61 +259,21 @@ int name_lookup(char *id, key_serial_t key, int type) + gid = atoi(id); + rc = nfs4_gid_to_name(gid, domain, name, IDMAP_NAMESZ); + } +- if (rc < 0) ++ if (rc) { + xlog_errno(rc, "name_lookup: %s: failed: %m", + (type == USER ? "nfs4_uid_to_name" : "nfs4_gid_to_name")); +- +- if (rc == 0) { +- rc = keyctl_instantiate(key, &name, strlen(name), 0); +- if (rc < 0) +- xlog_err("name_lookup: keyctl_instantiate failed: %m"); ++ return EXIT_FAILURE; + } +-out: +- return rc; +-} +-/* +- * Clear all the keys on the given keyring +- */ +-static int keyring_clear(char *keyring) +-{ +- FILE *fp; +- char buf[BUFSIZ]; +- key_serial_t key; + +- if (keyring == NULL) +- keyring = DEFAULT_KEYRING; +- +- if ((fp = fopen(PROCKEYS, "r")) == NULL) { +- xlog_err("fopen(%s) failed: %m", PROCKEYS); +- return 1; ++ rc = EXIT_SUCCESS; ++ if (keyctl_instantiate(key, &name, strlen(name), 0)) { ++ rc = EXIT_FAILURE; ++ xlog_err("name_lookup: keyctl_instantiate failed: %m"); + } + +- while(fgets(buf, BUFSIZ, fp) != NULL) { +- if (strstr(buf, "keyring") == NULL) +- continue; +- if (strstr(buf, keyring) == NULL) +- continue; +- if (verbose) { +- *(strchr(buf, '\n')) = '\0'; +- xlog_warn("clearing '%s'", buf); +- } +- /* +- * The key is the first arugment in the string +- */ +- *(strchr(buf, ' ')) = '\0'; +- sscanf(buf, "%x", &key); +- if (keyctl_clear(key) < 0) { +- xlog_err("keyctl_clear(0x%x) failed: %m", key); +- fclose(fp); +- return 1; +- } +- fclose(fp); +- return 0; +- } +- xlog_err("'%s' keyring was not found.", keyring); +- fclose(fp); +- return 1; ++ return rc; + } ++ + /* + * Revoke a key + */ +@@ -177,7 +288,7 @@ static int key_invalidate(char *keystr, int keymask) + + if ((fp = fopen(PROCKEYS, "r")) == NULL) { + xlog_err("fopen(%s) failed: %m", PROCKEYS); +- return 1; ++ return EXIT_FAILURE; + } + + while(fgets(buf, BUFSIZ, fp) != NULL) { +@@ -211,18 +322,18 @@ static int key_invalidate(char *keystr, int keymask) + if (keyctl_invalidate(key) < 0) { + xlog_err("keyctl_invalidate(0x%x) failed: %m", key); + fclose(fp); +- return 1; ++ return EXIT_FAILURE; + } + + keymask &= ~mask; + if (keymask == 0) { + fclose(fp); +- return 0; ++ return EXIT_SUCCESS; + } + } + xlog_err("'%s' key was not found.", keystr); + fclose(fp); +- return 1; ++ return EXIT_FAILURE; + } + + int main(int argc, char **argv) +@@ -234,7 +345,7 @@ int main(int argc, char **argv) + int timeout = 600; + key_serial_t key; + char *progname, *keystr = NULL; +- int clearing = 0, keymask = 0; ++ int clearing = 0, keymask = 0, display = 0, list = 0; + + /* Set the basename */ + if ((progname = strrchr(argv[0], '/')) != NULL) +@@ -244,8 +355,14 @@ int main(int argc, char **argv) + + xlog_open(progname); + +- while ((opt = getopt(argc, argv, "u:g:r:ct:v")) != -1) { ++ while ((opt = getopt(argc, argv, "du:g:r:ct:vl")) != -1) { + switch (opt) { ++ case 'd': ++ display++; ++ break; ++ case 'l': ++ list++; ++ break; + case 'u': + keymask = UIDKEYS; + keystr = strdup(optarg); +@@ -273,28 +390,35 @@ int main(int argc, char **argv) + } + } + ++ if (geteuid() != 0) { ++ xlog_err("Must be run as root."); ++ return EXIT_FAILURE; ++ } ++ + if ((rc = nfs4_init_name_mapping(PATH_IDMAPDCONF))) { + xlog_errno(rc, "Unable to create name to user id mappings."); +- return 1; ++ return EXIT_FAILURE; + } + if (!verbose) + verbose = conf_get_num("General", "Verbosity", 0); + ++ if (display) ++ return display_default_domain(); ++ if (list) ++ return list_keyring(DEFAULT_KEYRING); + if (keystr) { +- rc = key_invalidate(keystr, keymask); +- return rc; ++ return key_invalidate(keystr, keymask); + } + if (clearing) { + xlog_syslog(0); +- rc = keyring_clear(DEFAULT_KEYRING); +- return rc; ++ return keyring_clear(DEFAULT_KEYRING); + } + +- xlog_stderr(0); ++ xlog_stderr(verbose); + if ((argc - optind) != 2) { +- xlog_err("Bad arg count. Check /etc/request-key.conf"); ++ xlog_warn("Bad arg count. Check /etc/request-key.conf"); + xlog_warn(usage, progname); +- return 1; ++ return EXIT_FAILURE; + } + + if (verbose) +@@ -305,11 +429,15 @@ int main(int argc, char **argv) + arg = strdup(argv[optind]); + if (arg == NULL) { + xlog_err("strdup failed: %m"); +- return 1; ++ return EXIT_FAILURE; + } + type = strtok(arg, ":"); + value = strtok(NULL, ":"); +- ++ if (value == NULL) { ++ free(arg); ++ xlog_err("Error: Null uid/gid value."); ++ return EXIT_FAILURE; ++ } + if (verbose) { + xlog_warn("key: 0x%lx type: %s value: %s timeout %ld", + key, type, value, timeout); +@@ -328,7 +456,7 @@ int main(int argc, char **argv) + rc = name_lookup(value, key, GROUP); + + /* Set timeout to 10 (600 seconds) minutes */ +- if (rc == 0) ++ if (rc == EXIT_SUCCESS) + keyctl_set_timeout(key, timeout); + + free(arg); +diff --git a/utils/nfsidmap/nfsidmap.man b/utils/nfsidmap/nfsidmap.man +index 3a3a523..0275bdf 100644 +--- a/utils/nfsidmap/nfsidmap.man ++++ b/utils/nfsidmap/nfsidmap.man +@@ -11,30 +11,72 @@ nfsidmap \- The NFS idmapper upcall program + .B "nfsidmap [-v] [-c]" + .br + .B "nfsidmap [-v] [-u|-g|-r user]" ++.br ++.B "nfsidmap -d" ++.br ++.B "nfsidmap -l" + .SH DESCRIPTION +-The file ++The NFSv4 protocol represents the local system's UID and GID values ++on the wire as strings of the form ++.IR user@domain . ++The process of translating from UID to string and string to UID is ++referred to as "ID mapping." ++.PP ++The system derives the ++.I user ++part of the string by performing a password or group lookup. ++The lookup mechanism is configured in ++.IR /etc/idmapd.conf . ++.PP ++By default, the ++.I domain ++part of the string is the system's DNS domain name. ++It can also be specified in ++.I /etc/idmapd.conf ++if the system is multi-homed, ++or if the system's DNS domain name does ++not match the name of the system's Kerberos realm. ++.PP ++The + .I /usr/sbin/nfsidmap +-is used by the NFS idmapper to translate user and group ids into names, and to +-translate user and group names into ids. Idmapper uses request-key to perform +-the upcall and cache the result. ++program performs translations on behalf of the kernel. ++The kernel uses the request-key mechanism to perform ++an upcall. + .I /usr/sbin/nfsidmap +-is called by /sbin/request-key, and will perform the translation and +-initialize a key with the resulting information. ++is invoked by /sbin/request-key, performs the translation, ++and initializes a key with the resulting information. ++The kernel then caches the translation results in the key. + .PP + .I nfsidmap +-can also used to clear the keyring of all the keys or +-revoke one particular key. +-This is useful when the id mappings have failed to due +-to a lookup error resulting in all the cached uids/gids to be set +-to the user id nobody. ++can also clear cached ID map results in the kernel, ++or revoke one particular key. ++An incorrect cached key can result in file and directory ownership ++reverting to "nobody" on NFSv4 mount points. ++.PP ++In addition, the ++.B -d ++and ++.B -l ++options are available to help diagnose misconfigurations. ++They have no effect on the keyring containing ID mapping results. + .SH OPTIONS + .TP + .B -c + Clear the keyring of all the keys. + .TP ++.B -d ++Display the system's effective NFSv4 domain name on ++.IR stdout . ++.TP + .B -g user + Revoke the gid key of the given user. + .TP ++.B -l ++Display on ++.I stdout ++all keys currently in the keyring used to cache ID mapping results. ++These keys are visible only to the superuser. ++.TP + .B -r user + Revoke both the uid and gid key of the given user. + .TP +@@ -89,5 +131,15 @@ Notice that the new line was added above the line for the generic program. + request-key will find the first matching line and run the corresponding program. + In this case, /some/other/program will handle all uid lookups, and + /usr/sbin/nfsidmap will handle gid, user, and group lookups. ++.SH FILES ++.TP ++.I /etc/idmapd.conf ++ID mapping configuration file ++.TP ++.I /etc/request-key.conf ++Request key configuration file ++.SH "SEE ALSO" ++.BR idmapd.conf (5), ++.BR request-key (8) + .SH AUTHOR + Bryan Schumaker, diff --git a/SOURCES/nfs-utils-1.3.0-rpcgssd-debug.patch b/SOURCES/nfs-utils-1.3.0-rpcgssd-debug.patch new file mode 100644 index 0000000..656854b --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-rpcgssd-debug.patch @@ -0,0 +1,2564 @@ +diff -up nfs-utils-1.3.0/aclocal/libtirpc.m4.orig nfs-utils-1.3.0/aclocal/libtirpc.m4 +--- nfs-utils-1.3.0/aclocal/libtirpc.m4.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/aclocal/libtirpc.m4 2016-04-15 11:42:49.532156526 -0400 +@@ -2,61 +2,61 @@ dnl Checks for TI-RPC library and header + dnl + AC_DEFUN([AC_LIBTIRPC], [ + ++ AS_IF( ++ [test "$enable_tirpc" != "no"], ++ [PKG_CHECK_MODULES([TIRPC], [libtirpc], ++ [LIBTIRPC="${TIRPC_LIBS}" ++ AM_CPPFLAGS="${AM_CPPFLAGS} ${TIRPC_CFLAGS}" ++ AC_DEFINE([HAVE_LIBTIRPC], [1], ++ [Define to 1 if you have and wish to use libtirpc.])], ++ [AC_LIBTIRPC_OLD ++ AS_IF([test "$enable_tirpc" = "yes" -a -z "${LIBTIRPC}"], ++ [AC_MSG_ERROR([libtirpc not found.])])])]) ++ ++ AS_IF([test -n "${LIBTIRPC}"], ++ [AC_CHECK_LIB([tirpc], [authgss_free_private_data], ++ [AC_DEFINE([HAVE_AUTHGSS_FREE_PRIVATE_DATA], [1], ++ [Define to 1 if your rpcsec library provides authgss_free_private_data])],, ++ [${LIBS}])]) ++ ++ AS_IF([test -n "${LIBTIRPC}"], ++ [AC_CHECK_LIB([tirpc], [libtirpc_set_debug], ++ [AC_DEFINE([HAVE_LIBTIRPC_SET_DEBUG], [1], ++ [Define to 1 if your tirpc library provides libtirpc_set_debug])],, ++ [${LIBS}])]) ++ ++ AC_SUBST([AM_CPPFLAGS]) ++ AC_SUBST(LIBTIRPC) ++ ++])dnl ++ ++dnl Old way of checking libtirpc without pkg-config ++dnl This can go away when virtually all libtirpc provide a .pc file ++dnl ++AC_DEFUN([AC_LIBTIRPC_OLD], [ ++ + AC_ARG_WITH([tirpcinclude], + [AC_HELP_STRING([--with-tirpcinclude=DIR], + [use TI-RPC headers in DIR])], + [tirpc_header_dir=$withval], + [tirpc_header_dir=/usr/include/tirpc]) + +- dnl if --enable-tirpc was specifed, the following components +- dnl must be present, and we set up HAVE_ macros for them. +- +- if test "$enable_tirpc" != "no"; then +- +- dnl look for the library +- AC_CHECK_LIB([tirpc], [clnt_tli_create], [:], +- [if test "$enable_tirpc" = "yes"; then +- AC_MSG_ERROR([libtirpc not found.]) +- else +- AC_MSG_WARN([libtirpc not found. TIRPC disabled!]) +- enable_tirpc="no" +- fi]) +- fi +- +- if test "$enable_tirpc" != "no"; then +- +- dnl Check if library contains authgss_free_private_data +- AC_CHECK_LIB([tirpc], [authgss_free_private_data], [have_free_private_data=yes], +- [have_free_private_data=no]) +- fi +- +- if test "$enable_tirpc" != "no"; then +- dnl also must have the headers installed where we expect +- dnl look for headers; add -I compiler option if found +- AC_CHECK_HEADERS([${tirpc_header_dir}/netconfig.h], +- AC_SUBST([AM_CPPFLAGS], ["-I${tirpc_header_dir}"]), +- [if test "$enable_tirpc" = "yes"; then +- AC_MSG_ERROR([libtirpc headers not found.]) +- else +- AC_MSG_WARN([libtirpc headers not found. TIRPC disabled!]) +- enable_tirpc="no" +- fi]) +- +- fi +- +- dnl now set $LIBTIRPC accordingly +- if test "$enable_tirpc" != "no"; then +- AC_DEFINE([HAVE_LIBTIRPC], 1, +- [Define to 1 if you have and wish to use libtirpc.]) +- LIBTIRPC="-ltirpc" +- if test "$have_free_private_data" = "yes"; then +- AC_DEFINE([HAVE_AUTHGSS_FREE_PRIVATE_DATA], 1, +- [Define to 1 if your rpcsec library provides authgss_free_private_data,]) +- fi +- else +- LIBTIRPC="" +- fi +- +- AC_SUBST(LIBTIRPC) ++ dnl Look for the library ++ AC_CHECK_LIB([tirpc], [clnt_tli_create], ++ [has_libtirpc="yes"], ++ [has_libtirpc="no"]) ++ ++ dnl Also must have the headers installed where we expect ++ dnl to look for headers; add -I compiler option if found ++ AS_IF([test "$has_libtirpc" = "yes"], ++ [AC_CHECK_HEADERS([${tirpc_header_dir}/netconfig.h], ++ [AC_SUBST([AM_CPPFLAGS], ["-I${tirpc_header_dir}"])], ++ [has_libtirpc="no"])]) ++ ++ dnl Now set $LIBTIRPC accordingly ++ AS_IF([test "$has_libtirpc" = "yes"], ++ [AC_DEFINE([HAVE_LIBTIRPC], [1], ++ [Define to 1 if you have and wish to use libtirpc.]) ++ LIBTIRPC="-ltirpc"]) + + ])dnl +diff -up nfs-utils-1.3.0/support/include/nfslib.h.orig nfs-utils-1.3.0/support/include/nfslib.h +--- nfs-utils-1.3.0/support/include/nfslib.h.orig 2016-04-15 11:42:13.930460892 -0400 ++++ nfs-utils-1.3.0/support/include/nfslib.h 2016-04-15 11:42:38.365938345 -0400 +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -129,8 +130,8 @@ void fendrmtabent(FILE *fp); + void frewindrmtabent(FILE *fp); + + /* mydaemon */ +-void mydaemon(int nochdir, int noclose, int *pipefds); +-void release_parent(int *pipefds); ++void daemon_init(bool fg); ++void daemon_ready(void); + + /* + * wildmat borrowed from INN +@@ -182,6 +183,9 @@ size_t strlcpy(char *, const char *, si + ssize_t atomicio(ssize_t (*f) (int, void*, size_t), + int, void *, size_t); + ++#ifdef HAVE_LIBTIRPC_SET_DEBUG ++void libtirpc_set_debug(char *name, int level, int use_stderr); ++#endif + + #define UNUSED(x) UNUSED_ ## x __attribute__((unused)) + +diff -up nfs-utils-1.3.0/support/nfs/mydaemon.c.orig nfs-utils-1.3.0/support/nfs/mydaemon.c +--- nfs-utils-1.3.0/support/nfs/mydaemon.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/support/nfs/mydaemon.c 2016-04-15 11:42:38.366938365 -0400 +@@ -46,56 +46,61 @@ + #include + #include + #include ++#include + #include + #include + #include + ++#include "nfslib.h" ++ ++static int pipefds[2] = { -1, -1}; ++ + /** +- * mydaemon - daemonize, but have parent wait to exit +- * @nochdir: skip chdir()'ing the child to / after forking if true +- * @noclose: skip closing stdin/stdout/stderr if true +- * @pipefds: pointer to 2 element array of pipefds ++ * daemon_init - initial daemon setup ++ * @fg: whether to run in the foreground + * + * This function is like daemon(), but with our own special sauce to delay + * the exit of the parent until the child is set up properly. A pipe is created + * between parent and child. The parent process will wait to exit until the +- * child dies or writes a '1' on the pipe signaling that it started +- * successfully. ++ * child dies or writes an int on the pipe signaling its status. + */ + void +-mydaemon(int nochdir, int noclose, int *pipefds) ++daemon_init(bool fg) + { + int pid, status, tempfd; + ++ if (fg) ++ return; ++ + if (pipe(pipefds) < 0) { + xlog_err("mydaemon: pipe() failed: errno %d (%s)\n", + errno, strerror(errno)); +- exit(1); ++ exit(EXIT_FAILURE); + } +- if ((pid = fork ()) < 0) { ++ ++ pid = fork(); ++ if (pid < 0) { + xlog_err("mydaemon: fork() failed: errno %d (%s)\n", + errno, strerror(errno)); +- exit(1); ++ exit(EXIT_FAILURE); + } + +- if (pid != 0) { +- /* +- * Parent. Wait for status from child. +- */ ++ if (pid > 0) { ++ /* Parent */ + close(pipefds[1]); +- if (read(pipefds[0], &status, 1) != 1) +- exit(1); +- exit (0); ++ if (read(pipefds[0], &status, sizeof(status)) != sizeof(status)) ++ exit(EXIT_FAILURE); ++ exit(status); + } +- /* Child. */ ++ ++ /* Child */ + close(pipefds[0]); + setsid (); +- if (nochdir == 0) { +- if (chdir ("/") == -1) { +- xlog_err("mydaemon: chdir() failed: errno %d (%s)\n", +- errno, strerror(errno)); +- exit(1); +- } ++ ++ if (chdir ("/")) { ++ xlog_err("mydaemon: chdir() failed: errno %d (%s)\n", ++ errno, strerror(errno)); ++ exit(EXIT_FAILURE); + } + + while (pipefds[1] <= 2) { +@@ -103,41 +108,39 @@ mydaemon(int nochdir, int noclose, int * + if (pipefds[1] < 0) { + xlog_err("mydaemon: dup() failed: errno %d (%s)\n", + errno, strerror(errno)); +- exit(1); ++ exit(EXIT_FAILURE); + } + } + +- if (noclose == 0) { +- tempfd = open("/dev/null", O_RDWR); +- if (tempfd >= 0) { +- dup2(tempfd, 0); +- dup2(tempfd, 1); +- dup2(tempfd, 2); +- close(tempfd); +- } else { +- xlog_err("mydaemon: can't open /dev/null: errno %d " +- "(%s)\n", errno, strerror(errno)); +- exit(1); +- } ++ tempfd = open("/dev/null", O_RDWR); ++ if (tempfd < 0) { ++ xlog_err("mydaemon: can't open /dev/null: errno %d " ++ "(%s)\n", errno, strerror(errno)); ++ exit(EXIT_FAILURE); + } + +- return; ++ dup2(tempfd, 0); ++ dup2(tempfd, 1); ++ dup2(tempfd, 2); ++ closelog(); ++ dup2(pipefds[1], 3); ++ pipefds[1] = 3; ++ closeall(4); + } + + /** +- * release_parent - tell the parent that it can exit now +- * @pipefds: pipefd array that was previously passed to mydaemon() ++ * daemon_ready - tell interested parties that the daemon is ready + * +- * This function tells the parent process of mydaemon() that it's now clear +- * to exit(0). ++ * This function tells e.g. the parent process that the daemon is up ++ * and running. + */ + void +-release_parent(int *pipefds) ++daemon_ready(void) + { +- int status; ++ int status = 0; + + if (pipefds[1] > 0) { +- if (write(pipefds[1], &status, 1) != 1) { ++ if (write(pipefds[1], &status, sizeof(status)) != sizeof(status)) { + xlog_err("WARN: writing to parent pipe failed: errno " + "%d (%s)\n", errno, strerror(errno)); + } +diff -up nfs-utils-1.3.0/support/nfs/svc_create.c.orig nfs-utils-1.3.0/support/nfs/svc_create.c +--- nfs-utils-1.3.0/support/nfs/svc_create.c.orig 2016-04-15 11:42:13.931460911 -0400 ++++ nfs-utils-1.3.0/support/nfs/svc_create.c 2016-04-15 11:42:38.366938365 -0400 +@@ -133,7 +133,7 @@ svc_create_bindaddr(struct netconfig *nc + hint.ai_family = AF_INET6; + #endif /* IPV6_SUPPORTED */ + else { +- xlog(D_GENERAL, "Unrecognized bind address family: %s", ++ xlog(L_ERROR, "Unrecognized bind address family: %s", + nconf->nc_protofmly); + return NULL; + } +@@ -143,7 +143,7 @@ svc_create_bindaddr(struct netconfig *nc + else if (strcmp(nconf->nc_proto, NC_TCP) == 0) + hint.ai_protocol = (int)IPPROTO_TCP; + else { +- xlog(D_GENERAL, "Unrecognized bind address protocol: %s", ++ xlog(L_ERROR, "Unrecognized bind address protocol: %s", + nconf->nc_proto); + return NULL; + } +@@ -275,7 +275,7 @@ svc_create_nconf_rand_port(const char *n + xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0); + freeaddrinfo(ai); + if (xprt == NULL) { +- xlog(D_GENERAL, "Failed to create listener xprt " ++ xlog(L_ERROR, "Failed to create listener xprt " + "(%s, %u, %s)", name, version, nconf->nc_netid); + return 0; + } +@@ -286,10 +286,12 @@ svc_create_nconf_rand_port(const char *n + return 0; + } + ++ rpc_createerr.cf_stat = rpc_createerr.cf_error.re_errno = 0; + if (!svc_reg(xprt, program, version, dispatch, nconf)) { + /* svc_reg(3) destroys @xprt in this case */ +- xlog(D_GENERAL, "Failed to register (%s, %u, %s)", +- name, version, nconf->nc_netid); ++ xlog(L_ERROR, "Failed to register (%s, %u, %s): %s", ++ name, version, nconf->nc_netid, ++ clnt_spcreateerror("svc_reg() err")); + return 0; + } + +diff -up nfs-utils-1.3.0/support/nfs/svc_socket.c.orig nfs-utils-1.3.0/support/nfs/svc_socket.c +--- nfs-utils-1.3.0/support/nfs/svc_socket.c.orig 2016-04-15 11:42:13.931460911 -0400 ++++ nfs-utils-1.3.0/support/nfs/svc_socket.c 2016-04-15 11:42:38.367938385 -0400 +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include "xlog.h" + + #ifdef _LIBC + # include +@@ -90,9 +91,9 @@ svcsock_nonblock(int sock) + * connection. + */ + if ((flags = fcntl(sock, F_GETFL)) < 0) +- perror(_("svc_socket: can't get socket flags")); ++ xlog(L_ERROR, "svc_socket: can't get socket flags: %m"); + else if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0) +- perror(_("svc_socket: can't set socket flags")); ++ xlog(L_ERROR, "svc_socket: can't set socket flags: %m"); + else + return sock; + +@@ -110,7 +111,7 @@ svc_socket (u_long number, int type, int + + if ((sock = __socket (AF_INET, type, protocol)) < 0) + { +- perror (_("svc_socket: socket creation problem")); ++ xlog(L_ERROR, "svc_socket: socket creation problem: %m"); + return sock; + } + +@@ -121,7 +122,7 @@ svc_socket (u_long number, int type, int + sizeof (ret)); + if (ret < 0) + { +- perror (_("svc_socket: socket reuse problem")); ++ xlog(L_ERROR, "svc_socket: socket reuse problem: %m"); + return ret; + } + } +@@ -132,7 +133,7 @@ svc_socket (u_long number, int type, int + + if (bind(sock, (struct sockaddr *) &addr, len) < 0) + { +- perror (_("svc_socket: bind problem")); ++ xlog(L_ERROR, "svc_socket: bind problem: %m"); + (void) __close(sock); + sock = -1; + } +diff -up nfs-utils-1.3.0/utils/gssd/context_heimdal.c.orig nfs-utils-1.3.0/utils/gssd/context_heimdal.c +--- nfs-utils-1.3.0/utils/gssd/context_heimdal.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/gssd/context_heimdal.c 2016-04-15 11:42:38.367938385 -0400 +@@ -260,7 +260,7 @@ serialize_krb5_ctx(gss_ctx_id_t *_ctx, g + if (write_heimdal_seq_key(&p, end, ctx)) goto out_err; + + buf->length = p - (char *)buf->value; +- printerr(2, "serialize_krb5_ctx: returning buffer " ++ printerr(4, "serialize_krb5_ctx: returning buffer " + "with %d bytes\n", buf->length); + + return 0; +diff -up nfs-utils-1.3.0/utils/gssd/context_lucid.c.orig nfs-utils-1.3.0/utils/gssd/context_lucid.c +--- nfs-utils-1.3.0/utils/gssd/context_lucid.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/gssd/context_lucid.c 2016-04-15 11:42:38.367938385 -0400 +@@ -206,7 +206,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc + if (WRITE_BYTES(&p, end, lctx->send_seq)) goto out_err; + + /* Protocol 0 here implies DES3 or RC4 */ +- printerr(2, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); ++ printerr(4, "%s: protocol %d\n", __FUNCTION__, lctx->protocol); + if (lctx->protocol == 0) { + enctype = lctx->rfc1964_kd.ctx_key.type; + keysize = lctx->rfc1964_kd.ctx_key.length; +@@ -219,7 +219,7 @@ prepare_krb5_rfc4121_buffer(gss_krb5_luc + keysize = lctx->cfx_kd.ctx_key.length; + } + } +- printerr(2, "%s: serializing key with enctype %d and size %d\n", ++ printerr(4, "%s: serializing key with enctype %d and size %d\n", + __FUNCTION__, enctype, keysize); + + if (WRITE_BYTES(&p, end, enctype)) goto out_err; +@@ -265,7 +265,7 @@ serialize_krb5_ctx(gss_ctx_id_t *ctx, gs + gss_krb5_lucid_context_v1_t *lctx = 0; + int retcode = 0; + +- printerr(2, "DEBUG: %s: lucid version!\n", __FUNCTION__); ++ printerr(4, "DEBUG: %s: lucid version!\n", __FUNCTION__); + maj_stat = gss_export_lucid_sec_context(&min_stat, ctx, + 1, &return_ctx); + if (maj_stat != GSS_S_COMPLETE) { +diff -up nfs-utils-1.3.0/utils/gssd/gssd.c.orig nfs-utils-1.3.0/utils/gssd/gssd.c +--- nfs-utils-1.3.0/utils/gssd/gssd.c.orig 2016-04-15 11:42:13.917460638 -0400 ++++ nfs-utils-1.3.0/utils/gssd/gssd.c 2016-04-15 11:42:38.369938424 -0400 +@@ -1,7 +1,7 @@ + /* + gssd.c + +- Copyright (c) 2000 The Regents of the University of Michigan. ++ Copyright (c) 2000, 2004 The Regents of the University of Michigan. + All rights reserved. + + Copyright (c) 2000 Dug Song . +@@ -40,9 +40,18 @@ + #include + #endif /* HAVE_CONFIG_H */ + ++#ifndef _GNU_SOURCE ++#define _GNU_SOURCE ++#endif ++ + #include + #include ++#include ++#include ++#include + #include ++#include ++#include + + #include + #include +@@ -51,41 +60,684 @@ + #include + #include + #include ++#include ++#include ++#include ++#include ++#include ++ + #include "gssd.h" + #include "err_util.h" + #include "gss_util.h" + #include "krb5_util.h" + #include "nfslib.h" + +-char pipefs_dir[PATH_MAX] = GSSD_PIPEFS_DIR; +-char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; +-char ccachedir[PATH_MAX] = GSSD_DEFAULT_CRED_DIR ":" GSSD_USER_CRED_DIR; +-char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; ++static char *pipefs_path = GSSD_PIPEFS_DIR; ++static DIR *pipefs_dir; ++static int pipefs_fd; ++static int inotify_fd; ++struct event inotify_ev; ++ ++char *keytabfile = GSSD_DEFAULT_KEYTAB_FILE; ++char **ccachesearch; + int use_memcache = 0; + int root_uses_machine_creds = 1; + unsigned int context_timeout = 0; + unsigned int rpc_timeout = 5; + char *preferred_realm = NULL; +-int pipefds[2] = { -1, -1 }; ++/* Avoid DNS reverse lookups on server names */ ++static bool avoid_dns = true; ++ ++ ++TAILQ_HEAD(topdir_list_head, topdir) topdir_list; ++ ++struct topdir { ++ TAILQ_ENTRY(topdir) list; ++ TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list; ++ int wd; ++ char name[]; ++}; ++ ++/* ++ * topdir_list: ++ * linked list of struct topdir with basic data about a topdir. ++ * ++ * clnt_list: ++ * linked list of struct clnt_info with basic data about a clntXXX dir, ++ * one per topdir. ++ * ++ * Directory structure: created by the kernel ++ * {rpc_pipefs}/{topdir}/clntXX : one per rpc_clnt struct in the kernel ++ * {rpc_pipefs}/{topdir}/clntXX/krb5 : read uid for which kernel wants ++ * a context, write the resulting context ++ * {rpc_pipefs}/{topdir}/clntXX/info : stores info such as server name ++ * {rpc_pipefs}/{topdir}/clntXX/gssd : pipe for all gss mechanisms using ++ * a text-based string of parameters ++ * ++ * Algorithm: ++ * Poll all {rpc_pipefs}/{topdir}/clntXX/YYYY files. When data is ready, ++ * read and process; performs rpcsec_gss context initialization protocol to ++ * get a cred for that user. Writes result to corresponding krb5 file ++ * in a form the kernel code will understand. ++ * In addition, we make sure we are notified whenever anything is ++ * created or destroyed in {rpc_pipefs} or in any of the clntXX directories, ++ * and rescan the whole {rpc_pipefs} when this happens. ++ */ ++ ++/* ++ * convert a presentation address string to a sockaddr_storage struct. Returns ++ * true on success or false on failure. ++ * ++ * Note that we do not populate the sin6_scope_id field here for IPv6 addrs. ++ * gssd nececessarily relies on hostname resolution and DNS AAAA records ++ * do not generally contain scope-id's. This means that GSSAPI auth really ++ * can't work with IPv6 link-local addresses. ++ * ++ * We *could* consider changing this if we did something like adopt the ++ * Microsoft "standard" of using the ipv6-literal.net domainname, but it's ++ * not really feasible at present. ++ */ ++static bool ++gssd_addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port) ++{ ++ int rc; ++ struct addrinfo *res; ++ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV }; ++ ++#ifndef IPV6_SUPPORTED ++ hints.ai_family = AF_INET; ++#endif /* IPV6_SUPPORTED */ ++ ++ rc = getaddrinfo(node, port, &hints, &res); ++ if (rc) { ++ printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n", ++ node, port, ++ rc == EAI_SYSTEM ? strerror(errno) : gai_strerror(rc)); ++ return false; ++ } ++ ++#ifdef IPV6_SUPPORTED ++ /* ++ * getnameinfo ignores the scopeid. If the address turns out to have ++ * a non-zero scopeid, we can't use it -- the resolved host might be ++ * completely different from the one intended. ++ */ ++ if (res->ai_addr->sa_family == AF_INET6) { ++ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; ++ if (sin6->sin6_scope_id) { ++ printerr(0, "ERROR: address %s has non-zero " ++ "sin6_scope_id!\n", node); ++ freeaddrinfo(res); ++ return false; ++ } ++ } ++#endif /* IPV6_SUPPORTED */ ++ ++ memcpy(sa, res->ai_addr, res->ai_addrlen); ++ freeaddrinfo(res); ++ return true; ++} ++ ++/* ++ * convert a sockaddr to a hostname ++ */ ++static char * ++gssd_get_servername(const char *name, const struct sockaddr *sa, const char *addr) ++{ ++ socklen_t addrlen; ++ int err; ++ char hbuf[NI_MAXHOST]; ++ unsigned char buf[sizeof(struct in6_addr)]; ++ ++ while (avoid_dns) { ++ /* ++ * Determine if this is a server name, or an IP address. ++ * If it is an IP address, do the DNS lookup otherwise ++ * skip the DNS lookup. ++ */ ++ if (strchr(name, '.') == NULL) ++ break; /* local name */ ++ else if (inet_pton(AF_INET, name, buf) == 1) ++ break; /* IPv4 address */ ++ else if (inet_pton(AF_INET6, name, buf) == 1) ++ break; /* IPv6 addrss */ ++ ++ return strdup(name); ++ } ++ ++ switch (sa->sa_family) { ++ case AF_INET: ++ addrlen = sizeof(struct sockaddr_in); ++ break; ++#ifdef IPV6_SUPPORTED ++ case AF_INET6: ++ addrlen = sizeof(struct sockaddr_in6); ++ break; ++#endif /* IPV6_SUPPORTED */ ++ default: ++ printerr(0, "ERROR: unrecognized addr family %d\n", ++ sa->sa_family); ++ return NULL; ++ } ++ ++ err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0, ++ NI_NAMEREQD); ++ if (err) { ++ printerr(0, "ERROR: unable to resolve %s to hostname: %s\n", ++ addr, err == EAI_SYSTEM ? strerror(errno) : ++ gai_strerror(err)); ++ return NULL; ++ } ++ ++ return strdup(hbuf); ++} ++ ++static void ++gssd_read_service_info(int dirfd, struct clnt_info *clp) ++{ ++ int fd; ++ FILE *info = NULL; ++ int numfields; ++ char *server = NULL; ++ char *service = NULL; ++ int program; ++ int version; ++ char *address = NULL; ++ char *protoname = NULL; ++ char *port = NULL; ++ char *servername = NULL; ++ ++ fd = openat(dirfd, "info", O_RDONLY); ++ if (fd < 0) { ++ printerr(0, "ERROR: can't open %s/info: %s\n", ++ clp->relpath, strerror(errno)); ++ goto fail; ++ } ++ ++ info = fdopen(fd, "r"); ++ if (!info) { ++ printerr(0, "ERROR: can't fdopen %s/info: %s\n", ++ clp->relpath, strerror(errno)); ++ close(fd); ++ goto fail; ++ } ++ ++ /* ++ * Some history: ++ * ++ * The first three lines were added with rpc_pipefs in 2003-01-13. ++ * (commit af2f003391786fb632889c02142c941b212ba4ff) ++ * ++ * The 'protocol' line was added in 2003-06-11. ++ * (commit 9bd741ae48785d0c0e75cf906ff66f893d600c2d) ++ * ++ * The 'port' line was added in 2007-09-26. ++ * (commit bf19aacecbeebccb2c3d150a8bd9416b7dba81fe) ++ */ ++ numfields = fscanf(info, ++ "RPC server: %ms\n" ++ "service: %ms (%d) version %d\n" ++ "address: %ms\n" ++ "protocol: %ms\n" ++ "port: %ms\n", ++ &server, ++ &service, &program, &version, ++ &address, ++ &protoname, ++ &port); ++ ++ ++ switch (numfields) { ++ case 5: ++ protoname = strdup("tcp"); ++ if (!protoname) ++ goto fail; ++ /* fall through */ ++ case 6: ++ /* fall through */ ++ case 7: ++ break; ++ default: ++ goto fail; ++ } ++ ++ if (!gssd_addrstr_to_sockaddr((struct sockaddr *)&clp->addr, ++ address, port ? port : "")) ++ goto fail; ++ ++ servername = gssd_get_servername(server, (struct sockaddr *)&clp->addr, address); ++ if (!servername) ++ goto fail; ++ ++ if (asprintf(&clp->servicename, "%s@%s", service, servername) < 0) ++ goto fail; ++ ++ clp->servername = servername; ++ clp->prog = program; ++ clp->vers = version; ++ clp->protocol = protoname; ++ ++ goto out; ++ ++fail: ++ printerr(0, "ERROR: failed to parse %s/info\n", clp->relpath); ++ free(servername); ++ free(protoname); ++ clp->servicename = NULL; ++ clp->servername = NULL; ++ clp->prog = 0; ++ clp->vers = 0; ++ clp->protocol = NULL; ++out: ++ if (info) ++ fclose(info); ++ ++ free(server); ++ free(service); ++ free(address); ++ free(port); ++} ++ ++static void ++gssd_destroy_client(struct clnt_info *clp) ++{ ++ if (clp->krb5_fd >= 0) { ++ close(clp->krb5_fd); ++ event_del(&clp->krb5_ev); ++ } ++ ++ 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); ++ free(clp->protocol); ++ free(clp); ++} ++ ++static void gssd_scan(void); ++ ++static void ++gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) ++{ ++ struct clnt_info *clp = data; ++ ++ handle_gssd_upcall(clp); ++} ++ ++static void ++gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) ++{ ++ struct clnt_info *clp = data; ++ ++ handle_krb5_upcall(clp); ++} ++ ++static struct clnt_info * ++gssd_get_clnt(struct topdir *tdi, const char *name) ++{ ++ struct clnt_info *clp; ++ ++ TAILQ_FOREACH(clp, &tdi->clnt_list, list) ++ if (!strcmp(clp->name, name)) ++ return clp; ++ ++ clp = calloc(1, sizeof(struct clnt_info)); ++ if (!clp) { ++ printerr(0, "ERROR: can't malloc clnt_info: %s\n", ++ strerror(errno)); ++ return NULL; ++ } ++ ++ if (asprintf(&clp->relpath, "%s/%s", tdi->name, name) < 0) { ++ clp->relpath = NULL; ++ goto out; ++ } ++ ++ clp->wd = inotify_add_watch(inotify_fd, clp->relpath, IN_CREATE | IN_DELETE); ++ if (clp->wd < 0) { ++ if (errno != ENOENT) ++ printerr(0, "ERROR: inotify_add_watch failed for %s: %s\n", ++ clp->relpath, strerror(errno)); ++ goto out; ++ } ++ ++ clp->name = clp->relpath + strlen(tdi->name) + 1; ++ clp->krb5_fd = -1; ++ clp->gssd_fd = -1; ++ ++ TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list); ++ return clp; ++ ++out: ++ free(clp->relpath); ++ free(clp); ++ return NULL; ++} ++ ++static int ++gssd_scan_clnt(struct clnt_info *clp) ++{ ++ int clntfd; ++ bool gssd_was_closed; ++ bool krb5_was_closed; ++ ++ gssd_was_closed = clp->gssd_fd < 0 ? true : false; ++ krb5_was_closed = clp->krb5_fd < 0 ? true : false; ++ ++ clntfd = openat(pipefs_fd, clp->relpath, O_RDONLY); ++ if (clntfd < 0) { ++ printerr(0, "ERROR: can't openat %s: %s\n", ++ clp->relpath, strerror(errno)); ++ return -1; ++ } ++ ++ if (clp->gssd_fd == -1) ++ clp->gssd_fd = openat(clntfd, "gssd", O_RDWR | O_NONBLOCK); ++ ++ if (clp->gssd_fd == -1 && clp->krb5_fd == -1) ++ 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); ++ } ++ ++ 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); ++ } ++ ++ if (clp->krb5_fd == -1 && clp->gssd_fd == -1) ++ /* not fatal, files might appear later */ ++ goto out; ++ ++ if (clp->prog == 0) ++ gssd_read_service_info(clntfd, clp); ++ ++out: ++ close(clntfd); ++ clp->scanned = true; ++ return 0; ++} ++ ++static int ++gssd_create_clnt(struct topdir *tdi, const char *name) ++{ ++ struct clnt_info *clp; ++ ++ clp = gssd_get_clnt(tdi, name); ++ if (!clp) ++ return -1; ++ ++ return gssd_scan_clnt(clp); ++} + +-void ++static struct topdir * ++gssd_get_topdir(const char *name) ++{ ++ struct topdir *tdi; ++ ++ TAILQ_FOREACH(tdi, &topdir_list, list) ++ if (!strcmp(tdi->name, name)) ++ return tdi; ++ ++ tdi = malloc(sizeof(*tdi) + strlen(name) + 1); ++ if (!tdi) { ++ printerr(0, "ERROR: Couldn't allocate struct topdir\n"); ++ return NULL; ++ } ++ ++ tdi->wd = inotify_add_watch(inotify_fd, name, IN_CREATE); ++ if (tdi->wd < 0) { ++ printerr(0, "ERROR: inotify_add_watch failed for top dir %s: %s\n", ++ tdi->name, strerror(errno)); ++ free(tdi); ++ return NULL; ++ } ++ ++ strcpy(tdi->name, name); ++ TAILQ_INIT(&tdi->clnt_list); ++ ++ TAILQ_INSERT_HEAD(&topdir_list, tdi, list); ++ return tdi; ++} ++ ++static void ++gssd_scan_topdir(const char *name) ++{ ++ struct topdir *tdi; ++ int dfd; ++ DIR *dir; ++ struct clnt_info *clp; ++ struct dirent *d; ++ ++ tdi = gssd_get_topdir(name); ++ if (!tdi) ++ return; ++ ++ dfd = openat(pipefs_fd, tdi->name, O_RDONLY); ++ if (dfd < 0) { ++ printerr(0, "ERROR: can't openat %s: %s\n", ++ tdi->name, strerror(errno)); ++ return; ++ } ++ ++ dir = fdopendir(dfd); ++ if (!dir) { ++ printerr(0, "ERROR: can't fdopendir %s: %s\n", ++ tdi->name, strerror(errno)); ++ return; ++ } ++ ++ TAILQ_FOREACH(clp, &tdi->clnt_list, list) ++ clp->scanned = false; ++ ++ while ((d = readdir(dir))) { ++ if (d->d_type != DT_DIR) ++ continue; ++ ++ if (strncmp(d->d_name, "clnt", strlen("clnt"))) ++ continue; ++ ++ gssd_create_clnt(tdi, d->d_name); ++ } ++ ++ closedir(dir); ++ ++ TAILQ_FOREACH(clp, &tdi->clnt_list, list) { ++ void *saveprev; ++ ++ if (clp->scanned) ++ continue; ++ ++ printerr(3, "destroying client %s\n", clp->relpath); ++ saveprev = clp->list.tqe_prev; ++ TAILQ_REMOVE(&tdi->clnt_list, clp, list); ++ gssd_destroy_client(clp); ++ clp = saveprev; ++ } ++} ++ ++static void ++gssd_scan(void) ++{ ++ struct dirent *d; ++ ++ printerr(3, "doing a full rescan\n"); ++ rewinddir(pipefs_dir); ++ ++ while ((d = readdir(pipefs_dir))) { ++ if (d->d_type != DT_DIR) ++ continue; ++ ++ if (d->d_name[0] == '.') ++ continue; ++ ++ gssd_scan_topdir(d->d_name); ++ } ++ ++ if (TAILQ_EMPTY(&topdir_list)) { ++ printerr(0, "ERROR: the rpc_pipefs directory is empty!\n"); ++ exit(EXIT_FAILURE); ++ } ++} ++ ++static void ++gssd_scan_cb(int UNUSED(fd), short UNUSED(which), void *UNUSED(data)) ++{ ++ gssd_scan(); ++} ++ ++static bool ++gssd_inotify_topdir(struct topdir *tdi, const struct inotify_event *ev) ++{ ++ printerr(5, "inotify event for topdir (%s) - " ++ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n", ++ tdi->name, ev->wd, ev->len > 0 ? ev->name : "", ev->mask); ++ ++ if (ev->mask & IN_IGNORED) { ++ printerr(0, "ERROR: topdir disappeared!\n"); ++ return false; ++ } ++ ++ if (ev->len == 0) ++ return false; ++ ++ if (ev->mask & IN_CREATE) { ++ if (!(ev->mask & IN_ISDIR)) ++ return true; ++ ++ if (strncmp(ev->name, "clnt", strlen("clnt"))) ++ return true; ++ ++ if (gssd_create_clnt(tdi, ev->name)) ++ return false; ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool ++gssd_inotify_clnt(struct topdir *tdi, struct clnt_info *clp, const struct inotify_event *ev) ++{ ++ printerr(5, "inotify event for clntdir (%s) - " ++ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n", ++ clp->relpath, ev->wd, ev->len > 0 ? ev->name : "", ev->mask); ++ ++ if (ev->mask & IN_IGNORED) { ++ TAILQ_REMOVE(&tdi->clnt_list, clp, list); ++ gssd_destroy_client(clp); ++ return true; ++ } ++ ++ if (ev->len == 0) ++ return false; ++ ++ if (ev->mask & IN_CREATE) { ++ if (!strcmp(ev->name, "gssd") || ++ !strcmp(ev->name, "krb5") || ++ !strcmp(ev->name, "info")) ++ if (gssd_scan_clnt(clp)) ++ return false; ++ ++ return true; ++ ++ } else if (ev->mask & IN_DELETE) { ++ if (!strcmp(ev->name, "gssd") && clp->gssd_fd >= 0) { ++ close(clp->gssd_fd); ++ event_del(&clp->gssd_ev); ++ clp->gssd_fd = -1; ++ ++ } else if (!strcmp(ev->name, "krb5") && clp->krb5_fd >= 0) { ++ close(clp->krb5_fd); ++ event_del(&clp->krb5_ev); ++ clp->krb5_fd = -1; ++ } ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++static void ++gssd_inotify_cb(int ifd, short UNUSED(which), void *UNUSED(data)) ++{ ++ bool rescan = false; ++ struct topdir *tdi; ++ struct clnt_info *clp; ++ ++ while (true) { ++ char buf[4096] __attribute__ ((aligned(__alignof__(struct inotify_event)))); ++ const struct inotify_event *ev; ++ ssize_t len; ++ char *ptr; ++ ++ len = read(ifd, buf, sizeof(buf)); ++ if (len == -1 && errno == EINTR) ++ continue; ++ ++ if (len <= 0) ++ break; ++ ++ for (ptr = buf; ptr < buf + len; ++ ptr += sizeof(struct inotify_event) + ev->len) { ++ ev = (const struct inotify_event *)ptr; ++ ++ if (ev->mask & IN_Q_OVERFLOW) { ++ printerr(0, "ERROR: inotify queue overflow\n"); ++ rescan = true; ++ break; ++ } ++ ++ TAILQ_FOREACH(tdi, &topdir_list, list) { ++ if (tdi->wd == ev->wd) { ++ if (!gssd_inotify_topdir(tdi, ev)) ++ rescan = true; ++ goto found; ++ } ++ ++ TAILQ_FOREACH(clp, &tdi->clnt_list, list) { ++ if (clp->wd == ev->wd) { ++ if (!gssd_inotify_clnt(tdi, clp, ev)) ++ rescan = true; ++ goto found; ++ } ++ } ++ } ++ ++found: ++ if (!tdi) { ++ printerr(5, "inotify event for unknown wd!!! - " ++ "ev->wd (%d) ev->name (%s) ev->mask (0x%08x)\n", ++ ev->wd, ev->len > 0 ? ev->name : "", ev->mask); ++ rescan = true; ++ } ++ } ++ } ++ ++ if (rescan) ++ gssd_scan(); ++} ++ ++static void + sig_die(int signal) + { +- /* destroy krb5 machine creds */ + if (root_uses_machine_creds) + gssd_destroy_krb5_machine_creds(); + printerr(1, "exiting on signal %d\n", signal); + exit(0); + } + +-void +-sig_hup(int signal) +-{ +- /* don't exit on SIGHUP */ +- printerr(1, "Received SIGHUP(%d)... Ignoring.\n", signal); +- return; +-} +- + static void + usage(char *progname) + { +@@ -104,8 +756,9 @@ main(int argc, char *argv[]) + int i; + extern char *optarg; + char *progname; ++ char *ccachedir = NULL; ++ struct event sighup_ev; + +- memset(ccachesearch, 0, sizeof(ccachesearch)); + while ((opt = getopt(argc, argv, "DfvrlmnMp:k:d:t:T:R:")) != -1) { + switch (opt) { + case 'f': +@@ -127,19 +780,13 @@ main(int argc, char *argv[]) + rpc_verbosity++; + break; + case 'p': +- strncpy(pipefs_dir, optarg, sizeof(pipefs_dir)); +- if (pipefs_dir[sizeof(pipefs_dir)-1] != '\0') +- errx(1, "pipefs path name too long"); ++ pipefs_path = optarg; + break; + case 'k': +- strncpy(keytabfile, optarg, sizeof(keytabfile)); +- if (keytabfile[sizeof(keytabfile)-1] != '\0') +- errx(1, "keytab path name too long"); ++ keytabfile = optarg; + break; + case 'd': +- strncpy(ccachedir, optarg, sizeof(ccachedir)); +- if (ccachedir[sizeof(ccachedir)-1] != '\0') +- errx(1, "ccachedir path name too long"); ++ ccachedir = optarg; + break; + case 't': + context_timeout = atoi(optarg); +@@ -158,7 +805,7 @@ main(int argc, char *argv[]) + #endif + break; + case 'D': +- avoid_dns = 0; ++ avoid_dns = false; + break; + default: + usage(argv[0]); +@@ -174,15 +821,41 @@ main(int argc, char *argv[]) + * the results of getpw*. + */ + if (setenv("HOME", "/", 1)) { +- printerr(1, "Unable to set $HOME: %s\n", strerror(errno)); ++ printerr(0, "gssd: Unable to set $HOME: %s\n", strerror(errno)); + exit(1); + } + +- i = 0; +- ccachesearch[i++] = strtok(ccachedir, ":"); +- do { +- ccachesearch[i++] = strtok(NULL, ":"); +- } while (ccachesearch[i-1] != NULL && i < GSSD_MAX_CCACHE_SEARCH); ++ if (ccachedir) { ++ char *ccachedir_copy; ++ char *ptr; ++ ++ for (ptr = ccachedir, i = 2; *ptr; ptr++) ++ if (*ptr == ':') ++ i++; ++ ++ ccachesearch = malloc(i * sizeof(char *)); ++ ccachedir_copy = strdup(ccachedir); ++ if (!ccachedir_copy || !ccachesearch) { ++ printerr(0, "malloc failure\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ i = 0; ++ ccachesearch[i++] = strtok(ccachedir, ":"); ++ while(ccachesearch[i - 1]) ++ ccachesearch[i++] = strtok(NULL, ":"); ++ ++ } else { ++ ccachesearch = malloc(3 * sizeof(char *)); ++ if (!ccachesearch) { ++ printerr(0, "malloc failure\n"); ++ exit(EXIT_FAILURE); ++ } ++ ++ ccachesearch[0] = GSSD_DEFAULT_CRED_DIR; ++ ccachesearch[1] = GSSD_USER_CRED_DIR; ++ ccachesearch[2] = NULL; ++ } + + if (preferred_realm == NULL) + gssd_k5_get_default_realm(&preferred_realm); +@@ -197,6 +870,13 @@ main(int argc, char *argv[]) + if (verbosity && rpc_verbosity == 0) + rpc_verbosity = verbosity; + authgss_set_debug_level(rpc_verbosity); ++#elif HAVE_LIBTIRPC_SET_DEBUG ++ /* ++ * Only set the libtirpc debug level if explicitly requested via -r... ++ * gssd is chatty enough as it is. ++ */ ++ if (rpc_verbosity > 0) ++ libtirpc_set_debug(progname, rpc_verbosity, fg); + #else + if (rpc_verbosity > 0) + printerr(0, "Warning: rpcsec_gss library does not " +@@ -206,14 +886,42 @@ main(int argc, char *argv[]) + if (gssd_check_mechs() != 0) + errx(1, "Problem with gssapi library"); + +- if (!fg) +- mydaemon(0, 0, pipefds); ++ daemon_init(fg); ++ ++ event_init(); ++ ++ pipefs_dir = opendir(pipefs_path); ++ if (!pipefs_dir) { ++ printerr(0, "ERROR: opendir(%s) failed: %s\n", pipefs_path, strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ ++ pipefs_fd = dirfd(pipefs_dir); ++ if (fchdir(pipefs_fd)) { ++ printerr(0, "ERROR: fchdir(%s) failed: %s\n", pipefs_path, strerror(errno)); ++ exit(EXIT_FAILURE); ++ } ++ ++ inotify_fd = inotify_init1(IN_NONBLOCK); ++ if (inotify_fd == -1) { ++ printerr(0, "ERROR: inotify_init1 failed: %s\n", strerror(errno)); ++ exit(EXIT_FAILURE); ++ } + + signal(SIGINT, sig_die); + signal(SIGTERM, sig_die); +- signal(SIGHUP, sig_hup); ++ 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); ++ ++ TAILQ_INIT(&topdir_list); ++ gssd_scan(); ++ daemon_ready(); + +- gssd_run(); +- printerr(0, "gssd_run returned!\n"); +- abort(); ++ event_dispatch(); ++ ++ printerr(0, "ERROR: event_dispatch() returned!\n"); ++ return EXIT_FAILURE; + } ++ +diff -up nfs-utils-1.3.0/utils/gssd/gssd.h.orig nfs-utils-1.3.0/utils/gssd/gssd.h +--- nfs-utils-1.3.0/utils/gssd/gssd.h.orig 2016-04-15 11:42:13.917460638 -0400 ++++ nfs-utils-1.3.0/utils/gssd/gssd.h 2016-04-15 11:42:38.369938424 -0400 +@@ -34,14 +34,12 @@ + #include + #include + #include ++#include ++#include + +-#define MAX_FILE_NAMELEN 32 +-#define FD_ALLOC_BLOCK 256 + #ifndef GSSD_PIPEFS_DIR + #define GSSD_PIPEFS_DIR "/var/lib/nfs/rpc_pipefs" + #endif +-#define INFO "info" +-#define KRB5 "krb5" + #define DNOTIFY_SIGNAL (SIGRTMIN + 3) + + #define GSSD_DEFAULT_CRED_DIR "/tmp" +@@ -50,60 +48,40 @@ + #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine" + #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" + #define GSSD_SERVICE_NAME "nfs" +-#define GSSD_SERVICE_NAME_LEN 3 +-#define GSSD_MAX_CCACHE_SEARCH 16 + + /* + * The gss mechanisms that we can handle + */ + enum {AUTHTYPE_KRB5, AUTHTYPE_LIPKEY}; + +- +- +-extern char pipefs_dir[PATH_MAX]; +-extern char keytabfile[PATH_MAX]; +-extern char *ccachesearch[]; ++extern char *keytabfile; ++extern char **ccachesearch; + extern int use_memcache; + extern int root_uses_machine_creds; + extern unsigned int context_timeout; + extern unsigned int rpc_timeout; + extern char *preferred_realm; +-extern int pipefds[2]; +- +-TAILQ_HEAD(clnt_list_head, clnt_info) clnt_list; + + struct clnt_info { + TAILQ_ENTRY(clnt_info) list; +- char *dirname; +- char *pdir; +- int dir_fd; ++ int wd; ++ bool scanned; ++ char *name; ++ char *relpath; + char *servicename; + char *servername; + int prog; + int vers; + char *protocol; + int krb5_fd; +- int krb5_poll_index; +- int krb5_close_me; +- int gssd_fd; +- int gssd_poll_index; +- int gssd_close_me; +- struct sockaddr_storage addr; +-}; +- +-TAILQ_HEAD(topdirs_list_head, topdirs_info) topdirs_list; +- +-struct topdirs_info { +- TAILQ_ENTRY(topdirs_info) list; +- char *dirname; +- int fd; ++ struct event krb5_ev; ++ int gssd_fd; ++ struct event gssd_ev; ++ struct sockaddr_storage addr; + }; + +-void init_client_list(void); +-int update_client_list(void); + void handle_krb5_upcall(struct clnt_info *clp); + void handle_gssd_upcall(struct clnt_info *clp); +-void gssd_run(void); + + + #endif /* _RPC_GSSD_H_ */ +diff -up nfs-utils-1.3.0/utils/gssd/gssd_proc.c.orig nfs-utils-1.3.0/utils/gssd/gssd_proc.c +--- nfs-utils-1.3.0/utils/gssd/gssd_proc.c.orig 2016-04-15 11:42:13.949461263 -0400 ++++ nfs-utils-1.3.0/utils/gssd/gssd_proc.c 2016-04-15 11:42:38.371938463 -0400 +@@ -9,6 +9,7 @@ + Copyright (c) 2002 Marius Aamodt Eriksen . + Copyright (c) 2002 Bruce Fields + Copyright (c) 2004 Kevin Coffman ++ Copyright (c) 2014 David H?rdeman + All rights reserved, all wrongs reversed. + + Redistribution and use in source and binary forms, with or without +@@ -52,7 +53,6 @@ + #include + #include + #include +-#include + + #include + #include +@@ -79,548 +79,6 @@ + #include "nfslib.h" + #include "gss_names.h" + +-/* +- * pollarray: +- * array of struct pollfd suitable to pass to poll. initialized to +- * zero - a zero struct is ignored by poll() because the events mask is 0. +- * +- * clnt_list: +- * linked list of struct clnt_info which associates a clntXXX directory +- * with an index into pollarray[], and other basic data about that client. +- * +- * Directory structure: created by the kernel +- * {rpc_pipefs}/{dir}/clntXX : one per rpc_clnt struct in the kernel +- * {rpc_pipefs}/{dir}/clntXX/krb5 : read uid for which kernel wants +- * a context, write the resulting context +- * {rpc_pipefs}/{dir}/clntXX/info : stores info such as server name +- * {rpc_pipefs}/{dir}/clntXX/gssd : pipe for all gss mechanisms using +- * a text-based string of parameters +- * +- * Algorithm: +- * Poll all {rpc_pipefs}/{dir}/clntXX/YYYY files. When data is ready, +- * read and process; performs rpcsec_gss context initialization protocol to +- * get a cred for that user. Writes result to corresponding krb5 file +- * in a form the kernel code will understand. +- * In addition, we make sure we are notified whenever anything is +- * created or destroyed in {rpc_pipefs} or in any of the clntXX directories, +- * and rescan the whole {rpc_pipefs} when this happens. +- */ +- +-struct pollfd * pollarray; +- +-unsigned long pollsize; /* the size of pollaray (in pollfd's) */ +- +-/* Avoid DNS reverse lookups on server names */ +-int avoid_dns = 1; +- +-/* +- * convert a presentation address string to a sockaddr_storage struct. Returns +- * true on success or false on failure. +- * +- * Note that we do not populate the sin6_scope_id field here for IPv6 addrs. +- * gssd nececessarily relies on hostname resolution and DNS AAAA records +- * do not generally contain scope-id's. This means that GSSAPI auth really +- * can't work with IPv6 link-local addresses. +- * +- * We *could* consider changing this if we did something like adopt the +- * Microsoft "standard" of using the ipv6-literal.net domainname, but it's +- * not really feasible at present. +- */ +-static int +-addrstr_to_sockaddr(struct sockaddr *sa, const char *node, const char *port) +-{ +- int rc; +- struct addrinfo *res; +- struct addrinfo hints = { .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV }; +- +-#ifndef IPV6_SUPPORTED +- hints.ai_family = AF_INET; +-#endif /* IPV6_SUPPORTED */ +- +- rc = getaddrinfo(node, port, &hints, &res); +- if (rc) { +- printerr(0, "ERROR: unable to convert %s|%s to sockaddr: %s\n", +- node, port, rc == EAI_SYSTEM ? strerror(errno) : +- gai_strerror(rc)); +- return 0; +- } +- +-#ifdef IPV6_SUPPORTED +- /* +- * getnameinfo ignores the scopeid. If the address turns out to have +- * a non-zero scopeid, we can't use it -- the resolved host might be +- * completely different from the one intended. +- */ +- if (res->ai_addr->sa_family == AF_INET6) { +- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)res->ai_addr; +- if (sin6->sin6_scope_id) { +- printerr(0, "ERROR: address %s has non-zero " +- "sin6_scope_id!\n", node); +- freeaddrinfo(res); +- return 0; +- } +- } +-#endif /* IPV6_SUPPORTED */ +- +- memcpy(sa, res->ai_addr, res->ai_addrlen); +- freeaddrinfo(res); +- return 1; +-} +- +-/* +- * convert a sockaddr to a hostname +- */ +-static char * +-get_servername(const char *name, const struct sockaddr *sa, const char *addr) +-{ +- socklen_t addrlen; +- int err; +- char *hostname; +- char hbuf[NI_MAXHOST]; +- unsigned char buf[sizeof(struct in6_addr)]; +- +- if (avoid_dns) { +- /* +- * Determine if this is a server name, or an IP address. +- * If it is an IP address, do the DNS lookup otherwise +- * skip the DNS lookup. +- */ +- int is_fqdn = 1; +- if (strchr(name, '.') == NULL) +- is_fqdn = 0; /* local name */ +- else if (inet_pton(AF_INET, name, buf) == 1) +- is_fqdn = 0; /* IPv4 address */ +- else if (inet_pton(AF_INET6, name, buf) == 1) +- is_fqdn = 0; /* IPv6 addrss */ +- +- if (is_fqdn) { +- return strdup(name); +- } +- /* Sorry, cannot avoid dns after all */ +- } +- +- switch (sa->sa_family) { +- case AF_INET: +- addrlen = sizeof(struct sockaddr_in); +- break; +-#ifdef IPV6_SUPPORTED +- case AF_INET6: +- addrlen = sizeof(struct sockaddr_in6); +- break; +-#endif /* IPV6_SUPPORTED */ +- default: +- printerr(0, "ERROR: unrecognized addr family %d\n", +- sa->sa_family); +- return NULL; +- } +- +- err = getnameinfo(sa, addrlen, hbuf, sizeof(hbuf), NULL, 0, +- NI_NAMEREQD); +- if (err) { +- printerr(0, "ERROR: unable to resolve %s to hostname: %s\n", +- addr, err == EAI_SYSTEM ? strerror(errno) : +- gai_strerror(err)); +- return NULL; +- } +- +- hostname = strdup(hbuf); +- +- return hostname; +-} +- +-/* XXX buffer problems: */ +-static int +-read_service_info(char *info_file_name, char **servicename, char **servername, +- int *prog, int *vers, char **protocol, +- struct sockaddr *addr) { +-#define INFOBUFLEN 256 +- char buf[INFOBUFLEN + 1]; +- static char server[128]; +- int nbytes; +- static char service[128]; +- static char address[128]; +- char program[16]; +- char version[16]; +- char protoname[16]; +- char port[128]; +- char *p; +- int fd = -1; +- int numfields; +- +- *servicename = *servername = *protocol = NULL; +- +- if ((fd = open(info_file_name, O_RDONLY)) == -1) { +- printerr(0, "ERROR: can't open %s: %s\n", info_file_name, +- strerror(errno)); +- goto fail; +- } +- if ((nbytes = read(fd, buf, INFOBUFLEN)) == -1) +- goto fail; +- close(fd); +- fd = -1; +- buf[nbytes] = '\0'; +- +- numfields = sscanf(buf,"RPC server: %127s\n" +- "service: %127s %15s version %15s\n" +- "address: %127s\n" +- "protocol: %15s\n", +- server, +- service, program, version, +- address, +- protoname); +- +- if (numfields == 5) { +- strcpy(protoname, "tcp"); +- } else if (numfields != 6) { +- goto fail; +- } +- +- port[0] = '\0'; +- if ((p = strstr(buf, "port")) != NULL) +- sscanf(p, "port: %127s\n", port); +- +- /* get program, and version numbers */ +- *prog = atoi(program + 1); /* skip open paren */ +- *vers = atoi(version); +- +- if (!addrstr_to_sockaddr(addr, address, port)) +- goto fail; +- +- *servername = get_servername(server, addr, address); +- if (*servername == NULL) +- goto fail; +- +- nbytes = snprintf(buf, INFOBUFLEN, "%s@%s", service, *servername); +- if (nbytes > INFOBUFLEN) +- goto fail; +- +- if (!(*servicename = calloc(strlen(buf) + 1, 1))) +- goto fail; +- memcpy(*servicename, buf, strlen(buf)); +- +- if (!(*protocol = strdup(protoname))) +- goto fail; +- return 0; +-fail: +- printerr(0, "ERROR: failed to read service info\n"); +- if (fd != -1) close(fd); +- free(*servername); +- free(*servicename); +- free(*protocol); +- *servicename = *servername = *protocol = NULL; +- return -1; +-} +- +-static void +-destroy_client(struct clnt_info *clp) +-{ +- if (clp->krb5_poll_index != -1) +- memset(&pollarray[clp->krb5_poll_index], 0, +- sizeof(struct pollfd)); +- if (clp->gssd_poll_index != -1) +- memset(&pollarray[clp->gssd_poll_index], 0, +- sizeof(struct pollfd)); +- if (clp->dir_fd != -1) close(clp->dir_fd); +- if (clp->krb5_fd != -1) close(clp->krb5_fd); +- if (clp->gssd_fd != -1) close(clp->gssd_fd); +- free(clp->dirname); +- free(clp->pdir); +- free(clp->servicename); +- free(clp->servername); +- free(clp->protocol); +- free(clp); +-} +- +-static struct clnt_info * +-insert_new_clnt(void) +-{ +- struct clnt_info *clp = NULL; +- +- if (!(clp = (struct clnt_info *)calloc(1,sizeof(struct clnt_info)))) { +- printerr(0, "ERROR: can't malloc clnt_info: %s\n", +- strerror(errno)); +- goto out; +- } +- clp->krb5_poll_index = -1; +- clp->gssd_poll_index = -1; +- clp->krb5_fd = -1; +- clp->gssd_fd = -1; +- clp->dir_fd = -1; +- +- TAILQ_INSERT_HEAD(&clnt_list, clp, list); +-out: +- return clp; +-} +- +-static int +-process_clnt_dir_files(struct clnt_info * clp) +-{ +- char name[PATH_MAX]; +- char gname[PATH_MAX]; +- char info_file_name[PATH_MAX]; +- +- if (clp->gssd_close_me) { +- printerr(2, "Closing 'gssd' pipe for %s\n", clp->dirname); +- close(clp->gssd_fd); +- memset(&pollarray[clp->gssd_poll_index], 0, +- sizeof(struct pollfd)); +- clp->gssd_fd = -1; +- clp->gssd_poll_index = -1; +- clp->gssd_close_me = 0; +- } +- if (clp->krb5_close_me) { +- printerr(2, "Closing 'krb5' pipe for %s\n", clp->dirname); +- close(clp->krb5_fd); +- memset(&pollarray[clp->krb5_poll_index], 0, +- sizeof(struct pollfd)); +- clp->krb5_fd = -1; +- clp->krb5_poll_index = -1; +- clp->krb5_close_me = 0; +- } +- +- if (clp->gssd_fd == -1) { +- snprintf(gname, sizeof(gname), "%s/gssd", clp->dirname); +- clp->gssd_fd = open(gname, O_RDWR); +- } +- if (clp->gssd_fd == -1) { +- if (clp->krb5_fd == -1) { +- snprintf(name, sizeof(name), "%s/krb5", clp->dirname); +- clp->krb5_fd = open(name, O_RDWR); +- } +- +- /* If we opened a gss-specific pipe, let's try opening +- * the new upcall pipe again. If we succeed, close +- * gss-specific pipe(s). +- */ +- if (clp->krb5_fd != -1) { +- clp->gssd_fd = open(gname, O_RDWR); +- if (clp->gssd_fd != -1) { +- if (clp->krb5_fd != -1) +- close(clp->krb5_fd); +- clp->krb5_fd = -1; +- } +- } +- } +- +- if ((clp->krb5_fd == -1) && (clp->gssd_fd == -1)) +- return -1; +- snprintf(info_file_name, sizeof(info_file_name), "%s/info", +- clp->dirname); +- if (clp->prog == 0) +- read_service_info(info_file_name, &clp->servicename, +- &clp->servername, &clp->prog, &clp->vers, +- &clp->protocol, (struct sockaddr *) &clp->addr); +- return 0; +-} +- +-static int +-get_poll_index(int *ind) +-{ +- unsigned int i; +- +- *ind = -1; +- for (i=0; igssd_fd != -1) && (clp->gssd_poll_index == -1)) { +- if (get_poll_index(&clp->gssd_poll_index)) { +- printerr(0, "ERROR: Too many gssd clients\n"); +- return -1; +- } +- pollarray[clp->gssd_poll_index].fd = clp->gssd_fd; +- pollarray[clp->gssd_poll_index].events |= POLLIN; +- } +- +- if ((clp->krb5_fd != -1) && (clp->krb5_poll_index == -1)) { +- if (get_poll_index(&clp->krb5_poll_index)) { +- printerr(0, "ERROR: Too many krb5 clients\n"); +- return -1; +- } +- pollarray[clp->krb5_poll_index].fd = clp->krb5_fd; +- pollarray[clp->krb5_poll_index].events |= POLLIN; +- } +- +- return 0; +-} +- +-static void +-process_clnt_dir(char *dir, char *pdir) +-{ +- struct clnt_info * clp; +- +- if (!(clp = insert_new_clnt())) +- goto fail_destroy_client; +- +- if (!(clp->pdir = strdup(pdir))) +- goto fail_destroy_client; +- +- /* An extra for the '/', and an extra for the null */ +- if (!(clp->dirname = calloc(strlen(dir) + strlen(pdir) + 2, 1))) { +- goto fail_destroy_client; +- } +- sprintf(clp->dirname, "%s/%s", pdir, dir); +- if ((clp->dir_fd = open(clp->dirname, O_RDONLY)) == -1) { +- if (errno != ENOENT) +- printerr(0, "ERROR: can't open %s: %s\n", +- clp->dirname, strerror(errno)); +- goto fail_destroy_client; +- } +- fcntl(clp->dir_fd, F_SETSIG, DNOTIFY_SIGNAL); +- fcntl(clp->dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MULTISHOT); +- +- if (process_clnt_dir_files(clp)) +- goto fail_keep_client; +- +- if (insert_clnt_poll(clp)) +- goto fail_destroy_client; +- +- return; +- +-fail_destroy_client: +- if (clp) { +- TAILQ_REMOVE(&clnt_list, clp, list); +- destroy_client(clp); +- } +-fail_keep_client: +- /* We couldn't find some subdirectories, but we keep the client +- * around in case we get a notification on the directory when the +- * subdirectories are created. */ +- return; +-} +- +-void +-init_client_list(void) +-{ +- struct rlimit rlim; +- TAILQ_INIT(&clnt_list); +- /* Eventually plan to grow/shrink poll array: */ +- pollsize = FD_ALLOC_BLOCK; +- if (getrlimit(RLIMIT_NOFILE, &rlim) == 0 && +- rlim.rlim_cur != RLIM_INFINITY) +- pollsize = rlim.rlim_cur; +- pollarray = calloc(pollsize, sizeof(struct pollfd)); +-} +- +-/* +- * This is run after a DNOTIFY signal, and should clear up any +- * directories that are no longer around, and re-scan any existing +- * directories, since the DNOTIFY could have been in there. +- */ +-static void +-update_old_clients(struct dirent **namelist, int size, char *pdir) +-{ +- struct clnt_info *clp; +- void *saveprev; +- int i, stillhere; +- char fname[PATH_MAX]; +- +- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { +- /* only compare entries in the global list that are from the +- * same pipefs parent directory as "pdir" +- */ +- if (strcmp(clp->pdir, pdir) != 0) continue; +- +- stillhere = 0; +- for (i=0; i < size; i++) { +- snprintf(fname, sizeof(fname), "%s/%s", +- pdir, namelist[i]->d_name); +- if (strcmp(clp->dirname, fname) == 0) { +- stillhere = 1; +- break; +- } +- } +- if (!stillhere) { +- printerr(2, "destroying client %s\n", clp->dirname); +- saveprev = clp->list.tqe_prev; +- TAILQ_REMOVE(&clnt_list, clp, list); +- destroy_client(clp); +- clp = saveprev; +- } +- } +- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { +- if (!process_clnt_dir_files(clp)) +- insert_clnt_poll(clp); +- } +-} +- +-/* Search for a client by directory name, return 1 if found, 0 otherwise */ +-static int +-find_client(char *dirname, char *pdir) +-{ +- struct clnt_info *clp; +- char fname[PATH_MAX]; +- +- for (clp = clnt_list.tqh_first; clp != NULL; clp = clp->list.tqe_next) { +- snprintf(fname, sizeof(fname), "%s/%s", pdir, dirname); +- if (strcmp(clp->dirname, fname) == 0) +- return 1; +- } +- return 0; +-} +- +-static int +-process_pipedir(char *pipe_name) +-{ +- struct dirent **namelist; +- int i, j; +- +- if (chdir(pipe_name) < 0) { +- printerr(0, "ERROR: can't chdir to %s: %s\n", +- pipe_name, strerror(errno)); +- return -1; +- } +- +- j = scandir(pipe_name, &namelist, NULL, alphasort); +- if (j < 0) { +- printerr(0, "ERROR: can't scandir %s: %s\n", +- pipe_name, strerror(errno)); +- return -1; +- } +- +- update_old_clients(namelist, j, pipe_name); +- for (i=0; i < j; i++) { +- if (!strncmp(namelist[i]->d_name, "clnt", 4) +- && !find_client(namelist[i]->d_name, pipe_name)) +- process_clnt_dir(namelist[i]->d_name, pipe_name); +- free(namelist[i]); +- } +- +- free(namelist); +- +- return 0; +-} +- +-/* Used to read (and re-read) list of clients, set up poll array. */ +-int +-update_client_list(void) +-{ +- int retval = -1; +- struct topdirs_info *tdi; +- +- TAILQ_FOREACH(tdi, &topdirs_list, list) { +- retval = process_pipedir(tdi->dirname); +- if (retval) +- printerr(1, "WARNING: error processing %s\n", +- tdi->dirname); +- +- } +- return retval; +-} +- + /* Encryption types supported by the kernel rpcsec_gss code */ + int num_krb5_enctypes = 0; + krb5_enctype *krb5_enctypes = NULL; +@@ -691,7 +149,7 @@ do_downcall(int k5_fd, uid_t uid, struct + unsigned int timeout = context_timeout; + unsigned int buf_size = 0; + +- printerr(1, "doing downcall: lifetime_rec=%u acceptor=%.*s\n", ++ printerr(2, "doing downcall: lifetime_rec=%u acceptor=%.*s\n", + 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 + +@@ -730,7 +188,7 @@ do_error_downcall(int k5_fd, uid_t uid, + unsigned int timeout = 0; + int zero = 0; + +- printerr(1, "doing error downcall\n"); ++ printerr(2, "doing error downcall\n"); + + if (WRITE_BYTES(&p, end, uid)) goto out_err; + if (WRITE_BYTES(&p, end, timeout)) goto out_err; +@@ -772,7 +230,7 @@ populate_port(struct sockaddr *sa, const + 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; + } +@@ -780,7 +238,7 @@ populate_port(struct sockaddr *sa, const + #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; + } +@@ -941,7 +399,7 @@ create_auth_rpc_client(struct clnt_info + auth = authgss_create_default(rpc_clnt, tgtname, &sec); + if (!auth) { + /* Our caller should print appropriate message */ +- printerr(2, "WARNING: Failed to create krb5 context for " ++ printerr(1, "WARNING: Failed to create krb5 context for " + "user with uid %d for server %s\n", + uid, tgtname); + goto out_fail; +@@ -1032,7 +490,7 @@ krb5_not_machine_creds(struct clnt_info + char **dname; + int err, resp = -1; + +- printerr(1, "krb5_not_machine_creds: uid %d tgtname %s\n", ++ printerr(2, "krb5_not_machine_creds: uid %d tgtname %s\n", + uid, tgtname); + + *chg_err = change_identity(uid); +@@ -1079,7 +537,7 @@ krb5_use_machine_creds(struct clnt_info + int nocache = 0; + int success = 0; + +- printerr(1, "krb5_use_machine_creds: uid %d tgtname %s\n", ++ printerr(2, "krb5_use_machine_creds: uid %d tgtname %s\n", + uid, tgtname); + + do { +@@ -1149,8 +607,6 @@ process_krb5_upcall(struct clnt_info *cl + gss_OID mech; + gss_buffer_desc acceptor = {0}; + +- printerr(1, "handling krb5 upcall (%s)\n", clp->dirname); +- + token.length = 0; + token.value = NULL; + memset(&pd, 0, sizeof(struct authgss_private_data)); +@@ -1176,8 +632,6 @@ process_krb5_upcall(struct clnt_info *cl + * used for this case is not important. + * + */ +- printerr(2, "%s: service is '%s'\n", __func__, +- service ? service : ""); + if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && + service == NULL)) { + +@@ -1191,7 +645,7 @@ process_krb5_upcall(struct clnt_info *cl + /* Child: fall through to rest of function */ + childpid = getpid(); + unsetenv("KRB5CCNAME"); +- printerr(1, "CHILD forked pid %d \n", childpid); ++ printerr(2, "CHILD forked pid %d \n", childpid); + break; + case -1: + /* fork() failed! */ +@@ -1224,9 +678,7 @@ no_fork: + if (auth == NULL) + goto out_return_error; + } else { +- printerr(1, "WARNING: Failed to create krb5 context " +- "for user with uid %d for server %s\n", +- uid, clp->servername); ++ /* krb5_not_machine_creds logs the error */ + goto out_return_error; + } + } +@@ -1257,7 +709,7 @@ no_fork: + * try to use it after this point. + */ + if (serialize_context_for_kernel(&pd.pd_ctx, &token, &krb5oid, NULL)) { +- printerr(0, "WARNING: Failed to serialize krb5 context for " ++ printerr(1, "WARNING: Failed to serialize krb5 context for " + "user with uid %d for server %s\n", + uid, clp->servername); + goto out_return_error; +@@ -1300,6 +752,8 @@ handle_krb5_upcall(struct clnt_info *clp + return; + } + ++ printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath); ++ + process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL); + } + +@@ -1311,85 +765,66 @@ handle_gssd_upcall(struct clnt_info *clp + int lbuflen = 0; + char *p; + char *mech = NULL; ++ char *uidstr = NULL; + char *target = NULL; + char *service = NULL; + char *enctypes = NULL; + +- printerr(1, "handling gssd upcall (%s)\n", clp->dirname); +- + if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed reading request\n"); + return; + } +- printerr(2, "%s: '%s'\n", __func__, lbuf); + +- /* find the mechanism name */ +- if ((p = strstr(lbuf, "mech=")) != NULL) { +- mech = malloc(lbuflen); +- if (!mech) +- goto out; +- if (sscanf(p, "mech=%s", mech) != 1) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "failed to parse gss mechanism name " +- "in upcall string '%s'\n", lbuf); +- goto out; +- } +- } else { ++ printerr(2, "\n%s: '%s' (%s)\n", __func__, lbuf, clp->relpath); ++ ++ 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="))) ++ uidstr = p + strlen("uid="); ++ else if (!strncmp(p, "enctypes=", strlen("enctypes="))) ++ enctypes = p + strlen("enctypes="); ++ else if (!strncmp(p, "target=", strlen("target="))) ++ target = p + strlen("target="); ++ else if (!strncmp(p, "service=", strlen("service="))) ++ service = p + strlen("service="); ++ } ++ ++ if (!mech || strlen(mech) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find gss mechanism name " + "in upcall string '%s'\n", lbuf); +- goto out; ++ return; + } + +- /* read uid */ +- if ((p = strstr(lbuf, "uid=")) != NULL) { +- if (sscanf(p, "uid=%d", &uid) != 1) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "failed to parse uid " +- "in upcall string '%s'\n", lbuf); +- goto out; +- } +- } else { ++ if (uidstr) { ++ uid = (uid_t)strtol(uidstr, &p, 10); ++ if (p == uidstr || *p != '\0') ++ uidstr = NULL; ++ } ++ ++ if (!uidstr) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find uid " + "in upcall string '%s'\n", lbuf); +- goto out; ++ return; + } + +- /* read supported encryption types if supplied */ +- if ((p = strstr(lbuf, "enctypes=")) != NULL) { +- enctypes = malloc(lbuflen); +- if (!enctypes) +- goto out; +- if (sscanf(p, "enctypes=%s", enctypes) != 1) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "failed to parse encryption types " +- "in upcall string '%s'\n", lbuf); +- goto out; +- } +- if (parse_enctypes(enctypes) != 0) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "parsing encryption types failed: errno %d\n", errno); +- } ++ if (enctypes && parse_enctypes(enctypes) != 0) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "parsing encryption types failed: errno %d\n", errno); ++ return; + } + +- /* read target name */ +- if ((p = strstr(lbuf, "target=")) != NULL) { +- target = malloc(lbuflen); +- if (!target) +- goto out; +- if (sscanf(p, "target=%s", target) != 1) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "failed to parse target name " +- "in upcall string '%s'\n", lbuf); +- goto out; +- } ++ if (target && strlen(target) < 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse target name " ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + /* +- * read the service name +- * + * The presence of attribute "service=" indicates that machine + * credentials should be used for this request. If the value + * is "*", then any machine credentials available can be used. +@@ -1397,16 +832,11 @@ handle_gssd_upcall(struct clnt_info *clp + * the specified service name (always "nfs" for now) should be + * used. + */ +- if ((p = strstr(lbuf, "service=")) != NULL) { +- service = malloc(lbuflen); +- if (!service) +- goto out; +- if (sscanf(p, "service=%s", service) != 1) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "failed to parse service type " +- "in upcall string '%s'\n", lbuf); +- goto out; +- } ++ if (service && strlen(service) < 1) { ++ printerr(0, "WARNING: handle_gssd_upcall: " ++ "failed to parse service type " ++ "in upcall string '%s'\n", lbuf); ++ return; + } + + if (strcmp(mech, "krb5") == 0 && clp->servername) +@@ -1417,13 +847,5 @@ handle_gssd_upcall(struct clnt_info *clp + "received unknown gss mech '%s'\n", mech); + do_error_downcall(clp->gssd_fd, uid, -EACCES); + } +- +-out: +- free(lbuf); +- free(mech); +- free(enctypes); +- free(target); +- free(service); +- return; + } + +diff -up nfs-utils-1.3.0/utils/gssd/gss_util.h.orig nfs-utils-1.3.0/utils/gssd/gss_util.h +--- nfs-utils-1.3.0/utils/gssd/gss_util.h.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/gssd/gss_util.h 2016-04-15 11:42:38.368938404 -0400 +@@ -52,6 +52,4 @@ int gssd_check_mechs(void); + gss_krb5_set_allowable_enctypes(min, cred, num, types) + #endif + +-extern int avoid_dns; +- + #endif /* _GSS_UTIL_H_ */ +diff -up nfs-utils-1.3.0/utils/gssd/krb5_util.c.orig nfs-utils-1.3.0/utils/gssd/krb5_util.c +--- nfs-utils-1.3.0/utils/gssd/krb5_util.c.orig 2016-04-15 11:42:13.953461341 -0400 ++++ nfs-utils-1.3.0/utils/gssd/krb5_util.c 2016-04-15 11:42:38.372938482 -0400 +@@ -356,7 +356,7 @@ gssd_get_single_krb5_cred(krb5_context c + */ + now += 300; + if (ple->ccname && ple->endtime > now && !nocache) { +- printerr(2, "INFO: Credentials in CC '%s' are good until %d\n", ++ printerr(3, "INFO: Credentials in CC '%s' are good until %d\n", + ple->ccname, ple->endtime); + code = 0; + goto out; +@@ -383,7 +383,7 @@ gssd_get_single_krb5_cred(krb5_context c + "tickets. May have problems behind a NAT.\n"); + #ifdef TEST_SHORT_LIFETIME + /* set a short lifetime (for debugging only!) */ +- printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); ++ printerr(1, "WARNING: Using (debug) short machine cred lifetime!\n"); + krb5_get_init_creds_opt_set_tkt_life(init_opts, 5*60); + #endif + opts = init_opts; +@@ -451,8 +451,7 @@ gssd_get_single_krb5_cred(krb5_context c + } + + code = 0; +- printerr(2, "Successfully obtained machine credentials for " +- "principal '%s' stored in ccache '%s'\n", pname, cc_name); ++ printerr(2, "%s: principal '%s' ccache:'%s'\n", __func__, pname, cc_name); + out: + #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS + if (init_opts) +@@ -477,7 +476,7 @@ gssd_set_krb5_ccache_name(char *ccname) + #ifdef USE_GSS_KRB5_CCACHE_NAME + u_int maj_stat, min_stat; + +- printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n", ++ printerr(3, "using gss_krb5_ccache_name to select krb5 ccache %s\n", + ccname); + maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL); + if (maj_stat != GSS_S_COMPLETE) { +@@ -492,7 +491,7 @@ gssd_set_krb5_ccache_name(char *ccname) + * function above for which there is no generic gssapi + * equivalent.) + */ +- printerr(2, "using environment variable to select krb5 ccache %s\n", ++ printerr(3, "using environment variable to select krb5 ccache %s\n", + ccname); + setenv("KRB5CCNAME", ccname, 1); + #endif +@@ -1093,8 +1092,8 @@ gssd_setup_krb5_user_gss_ccache(uid_t ui + struct dirent *d; + int err, i, j; + +- printerr(2, "getting credentials for client with uid %u for " +- "server %s\n", uid, servername); ++ printerr(3, "looking for client creds with uid %u for " ++ "server %s in %s\n", uid, servername, dirpattern); + + for (i = 0, j = 0; dirpattern[i] != '\0'; i++) { + switch (dirpattern[i]) { +@@ -1410,16 +1409,21 @@ gssd_acquire_krb5_cred(gss_cred_id_t *gs + int + gssd_acquire_user_cred(gss_cred_id_t *gss_cred) + { +- OM_uint32 min_stat; ++ OM_uint32 maj_stat, min_stat; + int ret; + + ret = gssd_acquire_krb5_cred(gss_cred); + + /* force validation of cred to check for expiry */ + if (ret == 0) { +- if (gss_inquire_cred(&min_stat, *gss_cred, NULL, NULL, +- NULL, NULL) != GSS_S_COMPLETE) +- ret = -1; ++ maj_stat = gss_inquire_cred(&min_stat, *gss_cred, ++ NULL, NULL, NULL, NULL); ++ if (maj_stat != GSS_S_COMPLETE) { ++ if (get_verbosity() > 0) ++ pgsserr("gss_inquire_cred", ++ maj_stat, min_stat, &krb5oid); ++ ret = -1; ++ } + } + + return ret; +diff -up nfs-utils-1.3.0/utils/gssd/Makefile.am.orig nfs-utils-1.3.0/utils/gssd/Makefile.am +--- nfs-utils-1.3.0/utils/gssd/Makefile.am.orig 2016-04-15 11:42:13.942461126 -0400 ++++ nfs-utils-1.3.0/utils/gssd/Makefile.am 2016-04-15 11:42:38.367938385 -0400 +@@ -29,7 +29,6 @@ COMMON_SRCS = \ + gssd_SOURCES = \ + $(COMMON_SRCS) \ + gssd.c \ +- gssd_main_loop.c \ + gssd_proc.c \ + krb5_util.c \ + \ +@@ -37,12 +36,23 @@ gssd_SOURCES = \ + krb5_util.h \ + write_bytes.h + +-gssd_LDADD = ../../support/nfs/libnfs.a \ +- $(RPCSECGSS_LIBS) $(KRBLIBS) $(GSSAPI_LIBS) +-gssd_LDFLAGS = $(KRBLDFLAGS) $(LIBTIRPC) ++gssd_LDADD = \ ++ ../../support/nfs/libnfs.a \ ++ $(LIBEVENT) \ ++ $(RPCSECGSS_LIBS) \ ++ $(KRBLIBS) \ ++ $(GSSAPI_LIBS) \ ++ $(LIBTIRPC) + +-gssd_CFLAGS = $(AM_CFLAGS) $(CFLAGS) \ +- $(RPCSECGSS_CFLAGS) $(KRBCFLAGS) $(GSSAPI_CFLAGS) ++gssd_LDFLAGS = \ ++ $(KRBLDFLAGS) ++ ++gssd_CFLAGS = \ ++ $(AM_CFLAGS) \ ++ $(CFLAGS) \ ++ $(RPCSECGSS_CFLAGS) \ ++ $(KRBCFLAGS) \ ++ $(GSSAPI_CFLAGS) + + svcgssd_SOURCES = \ + $(COMMON_SRCS) \ +diff -up nfs-utils-1.3.0/utils/gssd/svcgssd.c.orig nfs-utils-1.3.0/utils/gssd/svcgssd.c +--- nfs-utils-1.3.0/utils/gssd/svcgssd.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/gssd/svcgssd.c 2016-04-15 11:42:38.372938482 -0400 +@@ -62,8 +62,6 @@ + #include "gss_util.h" + #include "err_util.h" + +-static int pipefds[2] = { -1, -1 }; +- + void + sig_die(int signal) + { +@@ -137,6 +135,13 @@ main(int argc, char *argv[]) + if (verbosity && rpc_verbosity == 0) + rpc_verbosity = verbosity; + authgss_set_debug_level(rpc_verbosity); ++#elif HAVE_LIBTIRPC_SET_DEBUG ++ /* ++ * Only set the libtirpc debug level if explicitly requested via -r... ++ * svcgssd is chatty enough as it is. ++ */ ++ if (rpc_verbosity > 0) ++ libtirpc_set_debug(progname, rpc_verbosity, fg); + #else + if (rpc_verbosity > 0) + printerr(0, "Warning: rpcsec_gss library does not " +@@ -157,8 +162,7 @@ main(int argc, char *argv[]) + exit(1); + } + +- if (!fg) +- mydaemon(0, 0, pipefds); ++ daemon_init(fg); + + signal(SIGINT, sig_die); + signal(SIGTERM, sig_die); +@@ -187,8 +191,7 @@ main(int argc, char *argv[]) + } + } + +- if (!fg) +- release_parent(pipefds); ++ daemon_ready(); + + nfs4_init_name_mapping(NULL); /* XXX: should only do this once */ + gssd_run(); +diff -up nfs-utils-1.3.0/utils/idmapd/idmapd.c.orig nfs-utils-1.3.0/utils/idmapd/idmapd.c +--- nfs-utils-1.3.0/utils/idmapd/idmapd.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/idmapd/idmapd.c 2016-04-15 11:42:38.373938502 -0400 +@@ -164,7 +164,6 @@ static char pipefsdir[PATH_MAX]; + static char *nobodyuser, *nobodygroup; + static uid_t nobodyuid; + static gid_t nobodygid; +-static int pipefds[2] = { -1, -1 }; + + /* Used by conffile.c in libnfs.a */ + char *conf_path; +@@ -302,8 +301,7 @@ main(int argc, char **argv) + if (nfs4_init_name_mapping(conf_path)) + errx(1, "Unable to create name to user id mappings."); + +- if (!fg) +- mydaemon(0, 0, pipefds); ++ daemon_init(fg); + + event_init(); + +@@ -380,7 +378,7 @@ main(int argc, char **argv) + if (nfsdret != 0 && fd == 0) + xlog_err("main: Neither NFS client nor NFSd found"); + +- release_parent(pipefds); ++ daemon_ready(); + + if (event_dispatch() < 0) + xlog_err("main: event_dispatch returns errno %d (%s)", +diff -up nfs-utils-1.3.0/utils/statd/statd.c.orig nfs-utils-1.3.0/utils/statd/statd.c +--- nfs-utils-1.3.0/utils/statd/statd.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/utils/statd/statd.c 2016-04-15 11:42:38.373938502 -0400 +@@ -248,13 +248,12 @@ int main (int argc, char **argv) + int nlm_udp = 0, nlm_tcp = 0; + struct rlimit rlim; + +- int pipefds[2] = { -1, -1}; +- char status; +- + /* Default: daemon mode, no other options */ + run_mode = 0; +- xlog_stderr(0); +- xlog_syslog(1); ++ ++ /* Log to stderr if there's an error during startup */ ++ xlog_stderr(1); ++ xlog_syslog(0); + + /* Set the basename */ + if ((name_p = strrchr(argv[0],'/')) != NULL) { +@@ -394,52 +393,17 @@ int main (int argc, char **argv) + simulator (--argc, ++argv); /* simulator() does exit() */ + #endif + +- if (!(run_mode & MODE_NODAEMON)) { +- int tempfd; +- +- if (pipe(pipefds)<0) { +- perror("statd: unable to create pipe"); +- exit(1); +- } +- if ((pid = fork ()) < 0) { +- perror ("statd: Could not fork"); +- exit (1); +- } else if (pid != 0) { +- /* Parent. +- * Wait for status from child. +- */ +- close(pipefds[1]); +- if (read(pipefds[0], &status, 1) != 1) +- exit(1); +- exit (0); +- } +- /* Child. */ +- close(pipefds[0]); +- setsid (); +- +- while (pipefds[1] <= 2) { +- pipefds[1] = dup(pipefds[1]); +- if (pipefds[1]<0) { +- perror("statd: dup"); +- exit(1); +- } +- } +- tempfd = open("/dev/null", O_RDWR); +- dup2(tempfd, 0); +- dup2(tempfd, 1); +- dup2(tempfd, 2); +- dup2(pipefds[1], 3); +- pipefds[1] = 3; +- closeall(4); +- } +- +- /* Child. */ ++ daemon_init((run_mode & MODE_NODAEMON)); + + if (run_mode & MODE_LOG_STDERR) { + xlog_syslog(0); + xlog_stderr(1); + xlog_config(D_ALL, 1); ++ } else { ++ xlog_syslog(1); ++ xlog_stderr(0); + } ++ + xlog_open(name_p); + xlog(L_NOTICE, "Version " VERSION " starting"); + +@@ -512,16 +476,8 @@ int main (int argc, char **argv) + } + atexit(statd_unregister); + +- /* If we got this far, we have successfully started, so notify parent */ +- if (pipefds[1] > 0) { +- status = 0; +- if (write(pipefds[1], &status, 1) != 1) { +- xlog_warn("writing to parent pipe failed: errno %d (%s)\n", +- errno, strerror(errno)); +- } +- close(pipefds[1]); +- pipefds[1] = -1; +- } ++ /* If we got this far, we have successfully started */ ++ daemon_ready(); + + for (;;) { + /* diff --git a/SOURCES/nfs-utils-1.3.0-rpcgssd-findkeytab.patch b/SOURCES/nfs-utils-1.3.0-rpcgssd-findkeytab.patch new file mode 100644 index 0000000..ab1ce68 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-rpcgssd-findkeytab.patch @@ -0,0 +1,23 @@ +diff -up nfs-utils-1.3.0/utils/gssd/krb5_util.c.orig nfs-utils-1.3.0/utils/gssd/krb5_util.c +--- nfs-utils-1.3.0/utils/gssd/krb5_util.c.orig 2016-05-16 08:46:45.554915290 -0400 ++++ nfs-utils-1.3.0/utils/gssd/krb5_util.c 2016-05-16 09:13:09.479879099 -0400 +@@ -768,7 +768,7 @@ find_keytab_entry(krb5_context context, + char **realmnames = NULL; + char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; + char myhostad[NI_MAXHOST+1]; +- int i, j, retval; ++ int i, j, k, retval; + char *default_realm = NULL; + char *realm; + char *k5err = NULL; +@@ -913,8 +913,8 @@ find_keytab_entry(krb5_context context, + * moving on to the svcname + */ + if (strcmp(svcnames[j],"$") == 0 && !tried_upper) { +- for (i = 0; myhostad[i] != '$'; ++i) { +- myhostad[i] = toupper(myhostad[i]); ++ for (k = 0; myhostad[k] != '$'; ++k) { ++ myhostad[k] = toupper(myhostad[k]); + } + j--; + tried_upper = 1; diff --git a/SOURCES/nfs-utils-1.3.0-rpcgssd-multithread.patch b/SOURCES/nfs-utils-1.3.0-rpcgssd-multithread.patch new file mode 100644 index 0000000..6d333b4 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-rpcgssd-multithread.patch @@ -0,0 +1,594 @@ +diff --git a/aclocal/kerberos5.m4 b/aclocal/kerberos5.m4 +index 0bf35d3..8a0f3e4 100644 +--- a/aclocal/kerberos5.m4 ++++ b/aclocal/kerberos5.m4 +@@ -43,15 +43,6 @@ AC_DEFUN([AC_KERBEROS_V5],[ + -f $dir/lib/libgssapi_krb5.so \) ; then + AC_DEFINE(HAVE_KRB5, 1, [Define this if you have MIT Kerberos libraries]) + KRBDIR="$dir" +- dnl If we are using MIT K5 1.3.1 and before, we *MUST* use the +- dnl private function (gss_krb5_ccache_name) to get correct +- dnl behavior of changing the ccache used by gssapi. +- dnl Starting in 1.3.2, we *DO NOT* want to use +- dnl gss_krb5_ccache_name, instead we want to set KRB5CCNAME +- dnl to get gssapi to use a different ccache +- if test $K5VERS -le 131; then +- AC_DEFINE(USE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the private function, gss_krb5_cache_name, must be used to tell the Kerberos library which credentials cache to use. Otherwise, this is done by setting the KRB5CCNAME environment variable]) +- fi + gssapi_lib=gssapi_krb5 + break + dnl The following ugly hack brought on by the split installation +@@ -92,8 +83,6 @@ AC_DEFUN([AC_KERBEROS_V5],[ + AC_DEFINE(HAVE_LUCID_CONTEXT_SUPPORT, 1, [Define this if the Kerberos GSS library supports gss_krb5_export_lucid_sec_context]), ,$KRBLIBS) + AC_CHECK_LIB($gssapi_lib, gss_krb5_set_allowable_enctypes, + AC_DEFINE(HAVE_SET_ALLOWABLE_ENCTYPES, 1, [Define this if the Kerberos GSS library supports gss_krb5_set_allowable_enctypes]), ,$KRBLIBS) +- AC_CHECK_LIB($gssapi_lib, gss_krb5_ccache_name, +- AC_DEFINE(HAVE_GSS_KRB5_CCACHE_NAME, 1, [Define this if the Kerberos GSS library supports gss_krb5_ccache_name]), ,$KRBLIBS) + AC_CHECK_LIB($gssapi_lib, gss_krb5_free_lucid_sec_context, + AC_DEFINE(HAVE_GSS_KRB5_FREE_LUCID_SEC_CONTEXT, 1, [Define this if the Kerberos GSS library supports gss_krb5_free_lucid_sec_context]), ,$KRBLIBS) + +diff --git a/aclocal/libpthread.m4 b/aclocal/libpthread.m4 +new file mode 100644 +index 0000000..e87d2a0 +--- /dev/null ++++ b/aclocal/libpthread.m4 +@@ -0,0 +1,13 @@ ++dnl Checks for pthreads library and headers ++dnl ++AC_DEFUN([AC_LIBPTHREAD], [ ++ ++ dnl Check for library, but do not add -lpthreads to LIBS ++ AC_CHECK_LIB([pthread], [pthread_create], [LIBPTHREAD=-lpthread], ++ [AC_MSG_ERROR([libpthread not found.])]) ++ AC_SUBST(LIBPTHREAD) ++ ++ AC_CHECK_HEADERS([pthread.h], , ++ [AC_MSG_ERROR([libpthread headers not found.])]) ++ ++])dnl +diff --git a/configure.ac b/configure.ac +index 4ee4db5..56f7f3e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -355,6 +355,9 @@ if test "$enable_gss" = yes; then + dnl Invoked after AC_KERBEROS_V5; AC_LIBRPCSECGSS needs to have KRBLIBS set + AC_LIBRPCSECGSS + ++ dnl Check for pthreads ++ AC_LIBPTHREAD ++ + dnl librpcsecgss already has a dependency on libgssapi, + dnl but we need to make sure we get the right version + if test "$enable_gss" = yes; then +diff --git a/utils/gssd/Makefile.am b/utils/gssd/Makefile.am +index fd488a1..f387c16 100644 +--- a/utils/gssd/Makefile.am ++++ b/utils/gssd/Makefile.am +@@ -42,7 +42,8 @@ gssd_LDADD = \ + $(RPCSECGSS_LIBS) \ + $(KRBLIBS) \ + $(GSSAPI_LIBS) \ +- $(LIBTIRPC) ++ $(LIBTIRPC) \ ++ $(LIBPTHREAD) + + gssd_LDFLAGS = \ + $(KRBLDFLAGS) +diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c +index 7ba27b1..43fccaf 100644 +--- a/utils/gssd/gssd.c ++++ b/utils/gssd/gssd.c +@@ -87,7 +87,9 @@ unsigned int rpc_timeout = 5; + char *preferred_realm = NULL; + /* Avoid DNS reverse lookups on server names */ + static bool avoid_dns = true; +- ++int thread_started = false; ++pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_cond_t pcond = PTHREAD_COND_INITIALIZER; + + TAILQ_HEAD(topdir_list_head, topdir) topdir_list; + +@@ -361,20 +363,91 @@ gssd_destroy_client(struct clnt_info *clp) + + 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; ++ info->clp = clp; ++ ++ return 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. ++ */ + 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(info); ++ return; ++ } ++ info->lbuf[info->lbuflen-1] = 0; + +- handle_gssd_upcall(clp); ++ if (start_upcall_thread(handle_gssd_upcall, info)) ++ free(info); + } + + 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(info); ++ return; ++ } + +- handle_krb5_upcall(clp); ++ if (start_upcall_thread(handle_krb5_upcall, info)) ++ free(info); + } + + static struct clnt_info * +diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h +index c6937c5..f4f5975 100644 +--- a/utils/gssd/gssd.h ++++ b/utils/gssd/gssd.h +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #ifndef GSSD_PIPEFS_DIR + #define GSSD_PIPEFS_DIR "/var/lib/nfs/rpc_pipefs" +@@ -48,7 +49,7 @@ + #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine" + #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" + #define GSSD_SERVICE_NAME "nfs" +- ++#define RPC_CHAN_BUF_SIZE 32768 + /* + * The gss mechanisms that we can handle + */ +@@ -61,6 +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; +@@ -80,8 +85,15 @@ struct clnt_info { + struct sockaddr_storage addr; + }; + +-void handle_krb5_upcall(struct clnt_info *clp); +-void handle_gssd_upcall(struct clnt_info *clp); ++struct clnt_upcall_info { ++ struct clnt_info *clp; ++ char lbuf[RPC_CHAN_BUF_SIZE]; ++ int lbuflen; ++ uid_t uid; ++}; ++ ++void handle_krb5_upcall(struct clnt_upcall_info *clp); ++void handle_gssd_upcall(struct clnt_upcall_info *clp); + + + #endif /* _RPC_GSSD_H_ */ +diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c +index 69c6a34..fda7595 100644 +--- a/utils/gssd/gssd_proc.c ++++ b/utils/gssd/gssd_proc.c +@@ -69,6 +69,7 @@ + #include + #include + #include ++#include + + #include "gssd.h" + #include "err_util.h" +@@ -442,7 +443,7 @@ change_identity(uid_t uid) + struct passwd *pw; + + /* drop list of supplimentary groups first */ +- if (setgroups(0, NULL) != 0) { ++ if (syscall(SYS_setgroups, 0, 0) != 0) { + printerr(0, "WARNING: unable to drop supplimentary groups!"); + return errno; + } +@@ -459,20 +460,18 @@ change_identity(uid_t uid) + } + } + +- /* +- * Switch the GIDs. Note that we leave the saved-set-gid alone in an +- * attempt to prevent attacks via ptrace() ++ /* Switch the UIDs and GIDs. */ ++ /* For the threaded version we have to set uid,gid per thread instead ++ * of per process. glibc setresuid() when called from a thread, it'll ++ * send a signal to all other threads to synchronize the uid in all ++ * other threads. To bypass this, we have to call syscall() directly. + */ +- if (setresgid(pw->pw_gid, pw->pw_gid, -1) != 0) { ++ if (syscall(SYS_setresgid, pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + printerr(0, "WARNING: failed to set gid to %u!\n", pw->pw_gid); + return errno; + } + +- /* +- * Switch UIDs, but leave saved-set-uid alone to prevent ptrace() by +- * other processes running with this uid. +- */ +- if (setresuid(uid, uid, -1) != 0) { ++ if (syscall(SYS_setresuid, uid, uid, uid) != 0) { + printerr(0, "WARNING: Failed to setuid for user with uid %u\n", + uid); + return errno; +@@ -554,7 +553,15 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname, + goto out; + } + for (ccname = credlist; ccname && *ccname; ccname++) { +- gssd_setup_krb5_machine_gss_ccache(*ccname); ++ u_int min_stat; ++ ++ if (gss_krb5_ccache_name(&min_stat, *ccname, NULL) != ++ GSS_S_COMPLETE) { ++ printerr(1, "WARNING: gss_krb5_ccache_name " ++ "with name '%s' failed (%s)\n", ++ *ccname, error_message(min_stat)); ++ continue; ++ } + if ((create_auth_rpc_client(clp, tgtname, rpc_clnt, + &auth, uid, + AUTHTYPE_KRB5, +@@ -602,7 +609,6 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, + gss_buffer_desc token; + int err, downcall_err = -EACCES; + OM_uint32 maj_stat, min_stat, lifetime_rec; +- pid_t pid, childpid = -1; + gss_name_t gacceptor = GSS_C_NO_NAME; + gss_OID mech; + gss_buffer_desc acceptor = {0}; +@@ -635,36 +641,6 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname, + if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 && + service == NULL)) { + +- /* already running as uid 0 */ +- if (uid == 0) +- goto no_fork; +- +- pid = fork(); +- switch(pid) { +- case 0: +- /* Child: fall through to rest of function */ +- childpid = getpid(); +- unsetenv("KRB5CCNAME"); +- printerr(2, "CHILD forked pid %d \n", childpid); +- break; +- case -1: +- /* fork() failed! */ +- printerr(0, "WARNING: unable to fork() to handle" +- "upcall: %s\n", strerror(errno)); +- return; +- default: +- /* Parent: just wait on child to exit and return */ +- do { +- pid = wait(&err); +- } while(pid == -1 && errno != -ECHILD); +- +- if (WIFSIGNALED(err)) +- printerr(0, "WARNING: forked child was killed" +- "with signal %d\n", WTERMSIG(err)); +- return; +- } +-no_fork: +- + auth = krb5_not_machine_creds(clp, uid, tgtname, &downcall_err, + &err, &rpc_clnt); + if (err) +@@ -730,11 +706,7 @@ out: + if (rpc_clnt) + clnt_destroy(rpc_clnt); + +- pid = getpid(); +- if (pid == childpid) +- exit(0); +- else +- return; ++ return; + + out_return_error: + do_error_downcall(fd, uid, downcall_err); +@@ -742,27 +714,21 @@ out_return_error: + } + + void +-handle_krb5_upcall(struct clnt_info *clp) ++handle_krb5_upcall(struct clnt_upcall_info *info) + { +- uid_t uid; +- +- 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; +- } ++ struct clnt_info *clp = info->clp; + +- printerr(2, "\n%s: uid %d (%s)\n", __func__, uid, clp->relpath); ++ printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); + +- process_krb5_upcall(clp, uid, clp->krb5_fd, NULL, NULL); ++ process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL); ++ free(info); + } + + void +-handle_gssd_upcall(struct clnt_info *clp) ++handle_gssd_upcall(struct clnt_upcall_info *info) + { ++ struct clnt_info *clp = info->clp; + uid_t uid; +- char *lbuf = NULL; +- int lbuflen = 0; + char *p; + char *mech = NULL; + char *uidstr = NULL; +@@ -770,15 +736,9 @@ handle_gssd_upcall(struct clnt_info *clp) + char *service = NULL; + char *enctypes = NULL; + +- if (readline(clp->gssd_fd, &lbuf, &lbuflen) != 1) { +- printerr(0, "WARNING: handle_gssd_upcall: " +- "failed reading request\n"); +- return; +- } +- +- printerr(2, "\n%s: '%s' (%s)\n", __func__, lbuf, clp->relpath); ++ printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath); + +- for (p = strtok(lbuf, " "); p; p = strtok(NULL, " ")) { ++ for (p = strtok(info->lbuf, " "); p; p = strtok(NULL, " ")) { + if (!strncmp(p, "mech=", strlen("mech="))) + mech = p + strlen("mech="); + else if (!strncmp(p, "uid=", strlen("uid="))) +@@ -794,8 +754,8 @@ handle_gssd_upcall(struct clnt_info *clp) + if (!mech || strlen(mech) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find gss mechanism name " +- "in upcall string '%s'\n", lbuf); +- return; ++ "in upcall string '%s'\n", info->lbuf); ++ goto out; + } + + if (uidstr) { +@@ -807,21 +767,21 @@ handle_gssd_upcall(struct clnt_info *clp) + if (!uidstr) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to find uid " +- "in upcall string '%s'\n", lbuf); +- return; ++ "in upcall string '%s'\n", info->lbuf); ++ goto out; + } + + if (enctypes && parse_enctypes(enctypes) != 0) { + printerr(0, "WARNING: handle_gssd_upcall: " + "parsing encryption types failed: errno %d\n", errno); +- return; ++ goto out; + } + + if (target && strlen(target) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse target name " +- "in upcall string '%s'\n", lbuf); +- return; ++ "in upcall string '%s'\n", info->lbuf); ++ goto out; + } + + /* +@@ -835,8 +795,8 @@ handle_gssd_upcall(struct clnt_info *clp) + if (service && strlen(service) < 1) { + printerr(0, "WARNING: handle_gssd_upcall: " + "failed to parse service type " +- "in upcall string '%s'\n", lbuf); +- return; ++ "in upcall string '%s'\n", info->lbuf); ++ goto out; + } + + if (strcmp(mech, "krb5") == 0 && clp->servername) +@@ -847,5 +807,8 @@ handle_gssd_upcall(struct clnt_info *clp) + "received unknown gss mech '%s'\n", mech); + do_error_downcall(clp->gssd_fd, uid, -EACCES); + } ++out: ++ free(info); ++ return; + } + +diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c +index 3849b6a..bc29787 100644 +--- a/utils/gssd/krb5_util.c ++++ b/utils/gssd/krb5_util.c +@@ -128,6 +128,7 @@ + + /* 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; + + #ifdef HAVE_SET_ALLOWABLE_ENCTYPES + int limit_to_legacy_enctypes = 0; +@@ -467,37 +468,6 @@ gssd_get_single_krb5_cred(krb5_context context, + } + + /* +- * Depending on the version of Kerberos, we either need to use +- * a private function, or simply set the environment variable. +- */ +-static void +-gssd_set_krb5_ccache_name(char *ccname) +-{ +-#ifdef USE_GSS_KRB5_CCACHE_NAME +- u_int maj_stat, min_stat; +- +- printerr(3, "using gss_krb5_ccache_name to select krb5 ccache %s\n", +- ccname); +- maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL); +- if (maj_stat != GSS_S_COMPLETE) { +- printerr(0, "WARNING: gss_krb5_ccache_name with " +- "name '%s' failed (%s)\n", +- ccname, error_message(min_stat)); +- } +-#else +- /* +- * Set the KRB5CCNAME environment variable to tell the krb5 code +- * which credentials cache to use. (Instead of using the private +- * function above for which there is no generic gssapi +- * equivalent.) +- */ +- printerr(3, "using environment variable to select krb5 ccache %s\n", +- ccname); +- setenv("KRB5CCNAME", ccname, 1); +-#endif +-} +- +-/* + * Given a principal, find a matching ple structure + */ + static struct gssd_k5_kt_princ * +@@ -586,10 +556,12 @@ get_ple_by_princ(krb5_context context, krb5_principal princ) + + /* 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); + } ++ pthread_mutex_unlock(&ple_lock); + + return ple; + } +@@ -1091,6 +1063,7 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern) + const char *cctype; + struct dirent *d; + int err, i, j; ++ u_int maj_stat, min_stat; + + printerr(3, "looking for client creds with uid %u for " + "server %s in %s\n", uid, servername, dirpattern); +@@ -1126,22 +1099,16 @@ gssd_setup_krb5_user_gss_ccache(uid_t uid, char *servername, char *dirpattern) + + printerr(2, "using %s as credentials cache for client with " + "uid %u for server %s\n", buf, uid, servername); +- gssd_set_krb5_ccache_name(buf); +- return 0; +-} + +-/* +- * Let the gss code know where to find the machine credentials ccache. +- * +- * Returns: +- * void +- */ +-void +-gssd_setup_krb5_machine_gss_ccache(char *ccname) +-{ +- printerr(2, "using %s as credentials cache for machine creds\n", +- ccname); +- gssd_set_krb5_ccache_name(ccname); ++ printerr(3, "using gss_krb5_ccache_name to select krb5 ccache %s\n", ++ buf); ++ maj_stat = gss_krb5_ccache_name(&min_stat, buf, NULL); ++ if (maj_stat != GSS_S_COMPLETE) { ++ printerr(0, "ERROR: unable to get user cred cache '%s' " ++ "failed (%s)\n", buf, error_message(min_stat)); ++ return maj_stat; ++ } ++ return 0; + } + + /* +diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h +index a319588..e3bbb07 100644 +--- a/utils/gssd/krb5_util.h ++++ b/utils/gssd/krb5_util.h +@@ -27,7 +27,6 @@ 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_setup_krb5_machine_gss_ccache(char *servername); + void gssd_destroy_krb5_machine_creds(void); + int gssd_refresh_krb5_machine_credential(char *hostname, + struct gssd_k5_kt_princ *ple, +@@ -55,8 +54,6 @@ int limit_krb5_enctypes(struct rpc_gss_sec *sec); + #define k5_free_unparsed_name(ctx, name) free(name) + #define k5_free_default_realm(ctx, realm) free(realm) + #define k5_free_kt_entry(ctx, kte) krb5_kt_free_entry((ctx),(kte)) +-#undef USE_GSS_KRB5_CCACHE_NAME +-#define USE_GSS_KRB5_CCACHE_NAME 1 + #endif + + #endif /* KRB5_UTIL_H */ diff --git a/SOURCES/nfs-utils-1.3.0-rpcidmapd-usage.patch b/SOURCES/nfs-utils-1.3.0-rpcidmapd-usage.patch new file mode 100644 index 0000000..5fc918f --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-rpcidmapd-usage.patch @@ -0,0 +1,97 @@ +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index aff89d1..f55d2e1 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -199,6 +199,12 @@ flush_nfsd_idmap_cache(void) + return ret; + } + ++void usage(char *progname) ++{ ++ fprintf(stderr, "Usage: %s [-hfvCS] [-p path] [-c path]\n", ++ basename(progname)); ++} ++ + int + main(int argc, char **argv) + { +@@ -225,16 +231,18 @@ main(int argc, char **argv) + progname = argv[0]; + xlog_open(progname); + +-#define GETOPTSTR "vfd:p:U:G:c:CS" ++#define GETOPTSTR "hvfd:p:U:G:c:CS" + opterr=0; /* Turn off error messages */ + while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) { + if (opt == 'c') + conf_path = optarg; + if (opt == '?') { + if (strchr(GETOPTSTR, optopt)) +- errx(1, "'-%c' option requires an argument.", optopt); ++ warnx("'-%c' option requires an argument.", optopt); + else +- errx(1, "'-%c' is an invalid argument.", optopt); ++ warnx("'-%c' is an invalid argument.", optopt); ++ usage(progname); ++ exit(1); + } + } + optind = 1; +@@ -276,6 +284,9 @@ main(int argc, char **argv) + case 'S': + clientstart = 0; + break; ++ case 'h': ++ usage(progname); ++ exit(0); + default: + break; + } +diff --git a/utils/idmapd/idmapd.man b/utils/idmapd/idmapd.man +index 185cd1b..b9200c7 100644 +--- a/utils/idmapd/idmapd.man ++++ b/utils/idmapd/idmapd.man +@@ -10,8 +10,11 @@ + .Sh SYNOPSIS + .\" For a program: program [-abc] file ... + .Nm rpc.idmapd +-.Op Fl v ++.Op Fl h + .Op Fl f ++.Op Fl v ++.Op Fl C ++.Op Fl S + .Op Fl p Ar path + .Op Fl c Ar path + .Sh DESCRIPTION +@@ -20,8 +23,20 @@ is the NFSv4 ID <-> name mapping daemon. It provides functionality to + the NFSv4 kernel client and server, to which it communicates via + upcalls, by translating user and group IDs to names, and vice versa. + .Pp ++Note that on more recent kernels only the NFSv4 server uses ++.Nm . ++The NFSv4 client instead uses ++.Xr nfsidmap 8 , ++and only falls back to ++.Nm ++if there was a problem running the ++.Xr nfsidmap 8 ++program. ++.Pp + The options are as follows: + .Bl -tag -width Ds_imagedir ++.It Fl h ++Display usage message. + .It Fl v + Increases the verbosity level (can be specified multiple times). + .It Fl f +@@ -58,7 +73,8 @@ messages to console, and with a verbosity level of 3. + .Sh FILES + .Pa /etc/idmapd.conf + .Sh SEE ALSO +-.Xr idmapd.conf 5 ++.Xr idmapd.conf 5 , ++.Xr nfsidmap 8 + .\".Sh SEE ALSO + .\".Xr nylon.conf 4 + .\" .Sh COMPATIBILITY diff --git a/SOURCES/nfs-utils-1.3.0-start-statd-flock.patch b/SOURCES/nfs-utils-1.3.0-start-statd-flock.patch new file mode 100644 index 0000000..ee7d22b --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-start-statd-flock.patch @@ -0,0 +1,27 @@ +commit 8fef90084f3d19e90ba1bb22b8cd1d58ddaf6ef3 +Author: Steve Dickson +Date: Tue Jun 21 12:06:06 2016 -0400 + + start-statd: Use flock to serialize the running of this script + + To once and for all stop multiple rpc.statd from + being started (mostly in HA environments), use + flock to serialize the running of the script + + Signed-off-by: Steve Dickson + +diff --git a/utils/statd/start-statd b/utils/statd/start-statd +index 19e6eb2..2fd6039 100755 +--- a/utils/statd/start-statd ++++ b/utils/statd/start-statd +@@ -6,6 +6,10 @@ + # site. + PATH="/sbin:/usr/sbin:/bin:/usr/bin" + ++# Use flock to serialize the running of this script ++exec 200> /var/run/rpc.statd.lock ++flock -e 200 ++ + if [ -s /var/run/rpc.statd.pid ] && + [ 1`cat /var/run/rpc.statd.pid` -gt 1 ] && + kill -0 `cat /var/run/rpc.statd.pid` > /dev/null 2>&1 diff --git a/SOURCES/nfs-utils-1.3.0-start-statd-once.patch b/SOURCES/nfs-utils-1.3.0-start-statd-once.patch new file mode 100644 index 0000000..0dc4172 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-start-statd-once.patch @@ -0,0 +1,31 @@ +diff -up nfs-utils-1.3.0/utils/statd/start-statd.orig nfs-utils-1.3.0/utils/statd/start-statd +--- nfs-utils-1.3.0/utils/statd/start-statd.orig 2016-04-14 14:16:47.608999000 -0400 ++++ nfs-utils-1.3.0/utils/statd/start-statd 2016-04-26 11:04:26.019962000 -0400 +@@ -1,4 +1,4 @@ +-#!/bin/bash -p ++#!/bin/sh + # nfsmount calls this script when mounting a filesystem with locking + # enabled, but when statd does not seem to be running (based on + # /var/run/rpc.statd.pid). +@@ -6,11 +6,19 @@ + # site. + PATH="/sbin:/usr/sbin:/bin:/usr/bin" + ++if [ -s /var/run/rpc.statd.pid ] && ++ [ 1`cat /var/run/rpc.statd.pid` -gt 1 ] && ++ kill -0 `cat /var/run/rpc.statd.pid` > /dev/null 2>&1 ++then ++ # statd already running - must have been slow to respond. ++ exit 0 ++fi + # First try systemd if it's installed. + if [ -d /run/systemd/system ]; then +- # Quit only if the call worked. +- systemctl start rpc-statd.service && exit ++ # Quit only if the call worked. ++ systemctl start rpc-statd.service && exit + fi + ++cd / + # Fall back to launching it ourselves. + exec rpc.statd --no-notify diff --git a/SOURCES/nfs-utils-1.3.0-start-statd-root.patch b/SOURCES/nfs-utils-1.3.0-start-statd-root.patch new file mode 100644 index 0000000..43277a7 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-start-statd-root.patch @@ -0,0 +1,35 @@ +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 4f8c15c..515249b 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -796,6 +796,8 @@ int start_statd(void) + if (stat(START_STATD, &stb) == 0) { + if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) { + int cnt = STATD_TIMEOUT * 10; ++ int status = 0; ++ char * const envp[1] = { NULL }; + const struct timespec ts = { + .tv_sec = 0, + .tv_nsec = 100000000, +@@ -803,14 +805,19 @@ int start_statd(void) + pid_t pid = fork(); + switch (pid) { + case 0: /* child */ +- execl(START_STATD, START_STATD, NULL); ++ setgid(0); ++ setuid(0); ++ execle(START_STATD, START_STATD, NULL, envp); + exit(1); + case -1: /* error */ + nfs_error(_("%s: fork failed: %s"), + progname, strerror(errno)); + break; + default: /* parent */ +- waitpid(pid, NULL,0); ++ if (waitpid(pid, &status,0) == pid && ++ status == 0) ++ /* assume it worked */ ++ return 1; + break; + } + while (1) { diff --git a/SOURCES/nfs-utils-1.3.0-statd-monlists.patch b/SOURCES/nfs-utils-1.3.0-statd-monlists.patch new file mode 100644 index 0000000..77b2195 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-statd-monlists.patch @@ -0,0 +1,95 @@ +commit 76f8ce8ce02868490ddfc5686bd48562fa73eab1 +Author: Scott Mayhew +Date: Mon Nov 23 10:43:03 2015 -0500 + + statd: Update existing record if we receive SM_MON with new cookie + + This prevents rpc.statd's in-memory (and on-disk) monitor lists from + winding up with multiple records for the same peer with outdated + cookie values. This happens in some HA-NFS configurations where + rpc.statd is always running. + + Signed-off-by: Scott Mayhew + Signed-off-by: Steve Dickson + +diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c +index 286a5e2..368bd80 100644 +--- a/utils/statd/monitor.c ++++ b/utils/statd/monitor.c +@@ -72,6 +72,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), + }; + char *dnsname = NULL; ++ int existing = 0; + + xlog(D_CALL, "Received SM_MON for %s from %s", mon_name, my_name); + +@@ -148,17 +149,26 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) + if (statd_matchhostname(NL_MY_NAME(clnt), my_name) && + NL_MY_PROC(clnt) == id->my_proc && + NL_MY_PROG(clnt) == id->my_prog && +- NL_MY_VERS(clnt) == id->my_vers && +- memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE) == 0) { +- /* Hey! We already know you guys! */ +- xlog(D_GENERAL, +- "Duplicate SM_MON request for %s " +- "from procedure on %s", +- mon_name, my_name); ++ NL_MY_VERS(clnt) == id->my_vers) { ++ if (memcmp(NL_PRIV(clnt), argp->priv, SM_PRIV_SIZE)) { ++ xlog(D_GENERAL, ++ "Received SM_MON request with new " ++ "cookie for %s from procedure on %s", ++ mon_name, my_name); ++ ++ existing = 1; ++ break; ++ } else { ++ /* Hey! We already know you guys! */ ++ xlog(D_GENERAL, ++ "Duplicate SM_MON request for %s " ++ "from procedure on %s", ++ mon_name, my_name); + +- /* But we'll let you pass anyway. */ +- free(dnsname); +- goto success; ++ /* But we'll let you pass anyway. */ ++ free(dnsname); ++ goto success; ++ } + } + clnt = NL_NEXT(clnt); + } +@@ -167,7 +177,7 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) + * We're committed...ignoring errors. Let's hope that a malloc() + * doesn't fail. (I should probably fix this assumption.) + */ +- if (!(clnt = nlist_new(my_name, mon_name, 0))) { ++ if (!existing && !(clnt = nlist_new(my_name, mon_name, 0))) { + free(dnsname); + xlog_warn("out of memory"); + goto failure; +@@ -180,8 +190,11 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) + clnt->dns_name = dnsname; + + /* +- * Now, Create file on stable storage for host. ++ * Now, Create file on stable storage for host, first deleting any ++ * existing records on file. + */ ++ nsm_delete_monitored_host(dnsname, mon_name, my_name); ++ + if (!nsm_insert_monitored_host(dnsname, + (struct sockaddr *)(char *)&my_addr, argp)) { + nlist_free(NULL, clnt); +@@ -190,7 +203,8 @@ sm_mon_1_svc(struct mon *argp, struct svc_req *rqstp) + + /* PRC: do the HA callout: */ + ha_callout("add-client", mon_name, my_name, -1); +- nlist_insert(&rtnl, clnt); ++ if (!existing) ++ nlist_insert(&rtnl, clnt); + xlog(D_GENERAL, "MONITORING %s for %s", mon_name, my_name); + success: + result.res_stat = STAT_SUCC; diff --git a/SOURCES/nfs-utils-1.3.0-statd-warnings.patch b/SOURCES/nfs-utils-1.3.0-statd-warnings.patch new file mode 100644 index 0000000..f7cfae3 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-statd-warnings.patch @@ -0,0 +1,91 @@ +diff -up nfs-utils-1.3.0/support/include/nsm.h.orig nfs-utils-1.3.0/support/include/nsm.h +--- nfs-utils-1.3.0/support/include/nsm.h.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/support/include/nsm.h 2016-06-23 10:09:34.216841414 -0400 +@@ -59,7 +59,8 @@ extern unsigned int + extern _Bool nsm_insert_monitored_host(const char *hostname, + const struct sockaddr *sap, const struct mon *m); + extern void nsm_delete_monitored_host(const char *hostname, +- const char *mon_name, const char *my_name); ++ const char *mon_name, const char *my_name, ++ const int chatty); + extern void nsm_delete_notified_host(const char *hostname, + const char *mon_name, const char *my_name); + extern size_t nsm_priv_to_hex(const char *priv, char *buf, +diff -up nfs-utils-1.3.0/support/nsm/file.c.orig nfs-utils-1.3.0/support/nsm/file.c +--- nfs-utils-1.3.0/support/nsm/file.c.orig 2014-03-25 11:12:07.000000000 -0400 ++++ nfs-utils-1.3.0/support/nsm/file.c 2016-06-23 10:09:34.216841414 -0400 +@@ -1012,7 +1012,7 @@ nsm_load_notify_list(nsm_populate_t func + + static void + nsm_delete_host(const char *directory, const char *hostname, +- const char *mon_name, const char *my_name) ++ const char *mon_name, const char *my_name, const int chatty) + { + char line[LINELEN + 1 + SM_MAXSTRLEN + 2]; + char *outbuf = NULL; +@@ -1028,8 +1028,9 @@ nsm_delete_host(const char *directory, c + } + + if (stat(path, &stb) == -1) { +- xlog(L_ERROR, "Failed to delete: " +- "could not stat original file %s: %m", path); ++ if (chatty) ++ xlog(L_ERROR, "Failed to delete: " ++ "could not stat original file %s: %m", path); + goto out; + } + remaining = (size_t)stb.st_size + 1; +@@ -1108,13 +1109,14 @@ out: + * @hostname: '\0'-terminated C string containing hostname of record to delete + * @mon_name: '\0'-terminated C string containing monname of record to delete + * @my_name: '\0'-terminated C string containing myname of record to delete ++ * @chatty: should an error be logged if the monitor file doesn't exist? + * + */ + void + nsm_delete_monitored_host(const char *hostname, const char *mon_name, +- const char *my_name) ++ const char *my_name, const int chatty) + { +- nsm_delete_host(NSM_MONITOR_DIR, hostname, mon_name, my_name); ++ nsm_delete_host(NSM_MONITOR_DIR, hostname, mon_name, my_name, chatty); + } + + /** +@@ -1128,5 +1130,5 @@ void + nsm_delete_notified_host(const char *hostname, const char *mon_name, + const char *my_name) + { +- nsm_delete_host(NSM_NOTIFY_DIR, hostname, mon_name, my_name); ++ nsm_delete_host(NSM_NOTIFY_DIR, hostname, mon_name, my_name, 1); + } +diff -up nfs-utils-1.3.0/utils/statd/monitor.c.orig nfs-utils-1.3.0/utils/statd/monitor.c +--- nfs-utils-1.3.0/utils/statd/monitor.c.orig 2016-06-23 10:08:34.442835693 -0400 ++++ nfs-utils-1.3.0/utils/statd/monitor.c 2016-06-23 10:09:34.216841414 -0400 +@@ -193,7 +193,7 @@ sm_mon_1_svc(struct mon *argp, struct sv + * Now, Create file on stable storage for host, first deleting any + * existing records on file. + */ +- nsm_delete_monitored_host(dnsname, mon_name, my_name); ++ nsm_delete_monitored_host(dnsname, mon_name, my_name, 0); + + if (!nsm_insert_monitored_host(dnsname, + (struct sockaddr *)(char *)&my_addr, argp)) { +@@ -324,7 +324,7 @@ sm_unmon_1_svc(struct mon_id *argp, stru + ha_callout("del-client", mon_name, my_name, -1); + + nsm_delete_monitored_host(clnt->dns_name, +- mon_name, my_name); ++ mon_name, my_name, 1); + nlist_free(&rtnl, clnt); + + return (&result); +@@ -379,7 +379,7 @@ sm_unmon_all_1_svc(struct my_id *argp, s + /* PRC: do the HA callout: */ + ha_callout("del-client", mon_name, my_name, -1); + nsm_delete_monitored_host(clnt->dns_name, +- mon_name, my_name); ++ mon_name, my_name, 1); + nlist_free(&rtnl, clnt); + ++count; + clnt = temp; diff --git a/SOURCES/nfs-utils-1.3.0-systemd-config.patch b/SOURCES/nfs-utils-1.3.0-systemd-config.patch new file mode 100644 index 0000000..6f1ba46 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-systemd-config.patch @@ -0,0 +1,37 @@ +commit c4940fad2a73481cad67732746a4e2bb74e8d32e +Author: NeilBrown +Date: Wed Mar 16 12:18:40 2016 -0400 + + systemd: ensure nfs-config service is re-run as needed. + + The nfs-config service translates distro-specific startup + configuration into "environment" variable read and used + by systemd unit files. + + Currently it is only run once, so subsequent changes to the + distro-specific files do not take effect when an nfs service is + restarted. + + If we change "RemainAfterExit=yes" to "RemainAfterExit=no" then the + service will be restarted before any dependant service is started, so + the environment file will always be up to date. + + Reported-and-tested-by: Benjamin Coddington + Signed-off-by: NeilBrown + Signed-off-by: Steve Dickson + +diff --git a/systemd/nfs-config.service b/systemd/nfs-config.service +index 7f65305..4b206b5 100644 +--- a/systemd/nfs-config.service ++++ b/systemd/nfs-config.service +@@ -5,5 +5,9 @@ DefaultDependencies=no + + [Service] + Type=oneshot +-RemainAfterExit=yes ++# This service needs to run any time any nfs service ++# is started, so changes to local config files get ++# incorporated. Having "RemainAfterExit=no" (the default) ++# ensures this happens. ++RemainAfterExit=no + ExecStart=/usr/lib/systemd/scripts/nfs-utils_env.sh diff --git a/SOURCES/nfs-utils-1.3.0-umount-opt-typo.patch b/SOURCES/nfs-utils-1.3.0-umount-opt-typo.patch new file mode 100644 index 0000000..9d65e09 --- /dev/null +++ b/SOURCES/nfs-utils-1.3.0-umount-opt-typo.patch @@ -0,0 +1,21 @@ +commit a5f1e5797f465d326043eb9daa5f1ad3750d4c9d +Author: Steve Dickson +Date: Tue Jun 7 16:07:27 2016 -0400 + + umount: fixed typo in usage message + + Signed-off-by: Steve Dickson + +diff --git a/utils/mount/utils.c b/utils/mount/utils.c +index 92662ed..865a4a0 100644 +--- a/utils/mount/utils.c ++++ b/utils/mount/utils.c +@@ -110,7 +110,7 @@ void mount_usage(void) + void umount_usage(void) + { + printf(_("usage: %s dir [-fvnrlh]\n"), progname); +- printf(_("options:\n\t-f\t\tforce unmount\n")); ++ printf(_("options:\n\t-f\tforce unmount\n")); + printf(_("\t-v\tverbose\n")); + printf(_("\t-n\tDo not update /etc/mtab\n")); + printf(_("\t-r\tremount\n")); diff --git a/SOURCES/nfs-utils_env.sh b/SOURCES/nfs-utils_env.sh index 6462a92..aa41822 100644 --- a/SOURCES/nfs-utils_env.sh +++ b/SOURCES/nfs-utils_env.sh @@ -11,15 +11,6 @@ if test -r $nfs_config; then . $nfs_config fi -[ -n "$LOCKDARG" ] && /sbin/modprobe lockd $LOCKDARG -if [ -n "$LOCKD_TCPPORT" -o -n "$LOCKD_UDPPORT" ]; then - [ -z "$LOCKDARG" ] && /sbin/modprobe lockd $LOCKDARG - [ -n "$LOCKD_TCPPORT" ] && \ - /sbin/sysctl -w fs.nfs.nlm_tcpport=$LOCKD_TCPPORT >/dev/null 2>&1 - [ -n "$LOCKD_UDPPORT" ] && \ - /sbin/sysctl -w fs.nfs.nlm_udpport=$LOCKD_UDPPORT >/dev/null 2>&1 -fi - if [ -n "$MOUNTD_PORT" ]; then RPCMOUNTDOPTS="$RPCMOUNTDOPTS -p $MOUNTD_PORT" fi diff --git a/SOURCES/nfs.sysconfig b/SOURCES/nfs.sysconfig index 4b71eff..0fcbe56 100644 --- a/SOURCES/nfs.sysconfig +++ b/SOURCES/nfs.sysconfig @@ -1,15 +1,10 @@ + # -# Note: For new values to take effect the nfs-config service -# has to be restarted with the following command: -# systemctl restart nfs-config # -# Optional arguments passed to in-kernel lockd -#LOCKDARG= -# TCP port rpc.lockd should listen on. -#LOCKD_TCPPORT=32803 -# UDP port rpc.lockd should listen on. -#LOCKD_UDPPORT=32769 +# To set lockd kernel module parameters please see +# /etc/modprobe.d/lockd.conf # + # Optional arguments passed to rpc.nfsd. See rpc.nfsd(8) RPCNFSDARGS="" # Number of nfs server processes to be started. @@ -45,6 +40,12 @@ SMNOTIFYARGS="" RPCIDMAPDARGS="" # # Optional arguments passed to rpc.gssd. See rpc.gssd(8) +# Note: The rpc-gssd service will not start unless the +# file /etc/krb5.keytab exists. If an alternate +# keytab is needed, that separate keytab file +# location may be defined in the rpc-gssd.service's +# systemd unit file under the ConditionPathExists +# parameter RPCGSSDARGS="" # # Enable usage of gssproxy. See gssproxy-mech(8). diff --git a/SPECS/nfs-utils.spec b/SPECS/nfs-utils.spec index bbddf39..ef59f5b 100644 --- a/SPECS/nfs-utils.spec +++ b/SPECS/nfs-utils.spec @@ -2,7 +2,7 @@ Summary: NFS utilities and supporting clients and daemons for the kernel NFS ser Name: nfs-utils URL: http://sourceforge.net/projects/nfs Version: 1.3.0 -Release: 0.21%{?dist}.1 +Release: 0.33%{?dist} Epoch: 1 # group all 32bit related archs @@ -13,6 +13,7 @@ Source0: https://www.kernel.org/pub/linux/utils/nfs-utils/%{version}/%{name}-%{v Source1: id_resolver.conf Source2: nfs.sysconfig Source3: nfs-utils_env.sh +Source4: lockd.conf # # RHEL7.1 @@ -50,13 +51,45 @@ Patch027: nfs-utils-1.3.0-blkmapd-loop.patch Patch028: nfs-utils-1.3.0-gssd-noclear-retval.patch Patch029: nfs-utils-1.3.0-gssd-tgt-flood.patch Patch030: nfs-utils-1.3.0-systemd-decouple.patch - # -# RHEL7.2-Z +# RHEL7.3 # -Patch031: nfs-utils-1.3.0-hostpton-eainoname.patch -Patch032: nfs-utils-1.3.0-rpcgssd-maccout-nocase.patch -Patch033: nfs-utils-1.3.0-nfsdcltrack-v2schema-update.patch +Patch031: nfs-utils-1.3.0-nfsidmap-timeout.patch +Patch032: nfs-utils-1.3.0-hostpton-eainoname.patch +Patch033: nfs-utils-1.3.0-rpcgssd-maccout-nocase.patch +Patch034: nfs-utils-1.3.0-nfsdcltrack-v2schema-update.patch +Patch035: nfs-utils-1.3.0-mountd-usage-error.patch +Patch036: nfs-utils-1.3.0-mountd-usage.patch +Patch037: nfs-utils-1.3.0-rpcgssd-debug.patch +Patch038: nfs-utils-1.3.0-start-statd-once.patch +Patch039: nfs-utils-1.2.9-exportfs-badentries.patch +Patch040: nfs-utils-1.3.0-mount-remount.patch +Patch041: nfs-utils-1.3.0-nfs_connect_nb-eintr.patch +Patch042: nfs-utils-1.3.0-statd-monlists.patch +Patch043: nfs-utils-1.3.0-nfsidmap-update.patch +Patch044: nfs-utils-1.3.0-mountstats-manpage-fix.patch +Patch045: nfs-utils-1.3.0-systemd-config.patch +Patch046: nfs-utils-1.3.0-exportfs-slashes.patch +Patch047: nfs-utils-1.3.0-exportfs-hostnames.patch +Patch048: nfs-utils-1.3.0-exportfs-bufsiz.patch +Patch049: nfs-utils-1.3.0-blkmapd-usage.patch +Patch050: nfs-utils-1.3.0-rpcidmapd-usage.patch +Patch051: nfs-utils-1.3.0-rpcgssd-multithread.patch +Patch052: nfs-utils-1.3.0-rpcgssd-findkeytab.patch +Patch053: nfs-utils-1.3.0-start-statd-root.patch +Patch054: nfs-utils-1.3.0-nfsd-rdma.patch +Patch055: nfs-utils-1.3.0-nfsd-warnings.patch +Patch056: nfs-utils-1.3.0-mountd-manpage-P.patch +Patch057: nfs-utils-1.3.0-mountstats-manpage-fix2.patch +Patch058: nfs-utils-1.3.0-mountd-netgroups.patch +Patch059: nfs-utils-1.3.0-umount-opt-typo.patch +Patch060: nfs-utils-1.3.0-mount-usage.patch +Patch061: nfs-utils-1.3.0-nfsidmap-h-opt.patch +Patch062: nfs-utils-1.3.0-exportfs-empty-exports.patch +Patch063: nfs-utils-1.3.0-statd-warnings.patch +Patch064: nfs-utils-1.3.0-start-statd-flock.patch +Patch065: nfs-utils-1.3.0-mountd-root.patch +Patch066: nfs-utils-1.3.0-mount-nfs-types.patch Patch100: nfs-utils-1.2.1-statdpath-man.patch Patch101: nfs-utils-1.2.1-exp-subtree-warn-off.patch @@ -85,7 +118,7 @@ License: MIT and GPLv2 and GPLv2+ and BSD Requires: rpcbind, sed, gawk, sh-utils, fileutils, textutils, grep Requires: kmod, keyutils, quota BuildRequires: libevent-devel libcap-devel -BuildRequires: libnfsidmap-devel libtirpc-devel libblkid-devel +BuildRequires: libnfsidmap-devel libtirpc-devel >= 0.2.4-0.7 libblkid-devel BuildRequires: krb5-libs >= 1.4 autoconf >= 2.57 openldap-devel >= 2.2 BuildRequires: automake, libtool, glibc-headers, device-mapper-devel BuildRequires: krb5-devel, tcp_wrappers-devel, libmount-devel @@ -93,7 +126,7 @@ BuildRequires: fedfs-utils-devel >= 0.8.0-7, sqlite-devel Requires(pre): shadow-utils >= 4.0.3-25 Requires(pre): /sbin/chkconfig /sbin/nologin Requires: libnfsidmap libevent -Requires: libtirpc >= 0.2.3-1 libblkid libcap libmount +Requires: libtirpc >= 0.2.4-0.7 libblkid libcap libmount Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units @@ -175,12 +208,78 @@ This package also contains the mount.nfs and umount.nfs program. %patch029 -p1 # 1266993 - restarting rpbind also restart the the nfs server %patch030 -p1 -# 1288435 - exportfs -u incorrectly exits with a 1 whenever... +# 1161222 - nfsidmap not setting key timeouts %patch031 -p1 -# 1288433 - rpc.gssd should not assume that the machine account is in uppercase +# 1276099 - exportfs -u incorrectly exits with a 1 whenever... %patch032 -p1 -# 1309625 - updated nfs-utils package broke nfsdcltrack +# 1268040 - rpc.gssd should not assume that the machine account is in uppercase %patch033 -p1 +# 1285097 - updated nfs-utils package broke nfsdcltrack +%patch034 -p1 +# 1003539 - If rpc.mountd specify options: "-N 2 -N 3 -N 4", should output... +%patch035 -p1 +# 1003716 - rpc.mountd -h: 1. In help info not include -r|--reverse-lookup... +%patch036 -p1 +# 1273163 - Allow gssd and svcgssd to set the libtirpc debug level +%patch037 -p1 +# 1300007 - start-statd: don't run multiple rpc.statds on the one host. +%patch038 -p1 +# 1287468 - Unable to start nfs server.service if any of the client is down... +%patch039 -p1 +# 1313550 - Nondeterministic DNS lookups cause NFS kdump targets to fail +%patch040 -p1 +# 1299003 - Unhandled EINTR during connection establishment leads... +%patch041 -p1 +# 1284576 - lockd's and statd's monitor lists get out of sync +%patch042 -p1 +# 1290488 Request a method(like a testperm) to show parameters of idmapd.conf +%patch043 -p1 +# 1266013 - need to remove a few bogus .R macros of mountstats man page +%patch044 -p1 +# 1331460 - nfs-config service is *not* being re-run as needed +%patch045 -p1 +# 1276534 - exportfs -u cannot unexport when the specified... +%patch046 -p1 +# 1331801 - exportfs -u can exit with status 1 if there are multiple... +%patch047 -p1 +# 1243234 - exportfs: code defect, when export path length > 986... +%patch048 -p1 +# 1001431 - RFE: feature blkmapd add help -h option, to output "usage:" info +%patch049 -p1 +# 1001438 - RFE: feature rpc.idmapd add help -h option, to output "usage:" info +%patch050 -p1 +# 1331540 - multi-threaded rpc.gssd +%patch051 -p1 +# 1334848 - cthon - rpc.gssd crash reading krb5.keytab in find_keytab_entry() +%patch052 -p1 +# 1275082 - [vdsm] NFS mount fails sometimes with "rpc.statd is not running... +%patch053 -p1 +# 1310691 - "--rdma" option of rpc.nfsd enables the wrong port +%patch054 -p1 +# 1336419 - nfsd: Remove some warnings nfsd.c +%patch055 -p1 +# 1037924 - [rpc.mountd] update rpc.mountd(8) manpage to change -P... +%patch056 -p1 +# 1263966 - typo in mountstats man page that should not contain "-s|--since" +%patch057 -p1 +# 1341908 - rpc.mountd does not check for membership of IP... +%patch058 -p1 +# 1246329 - umount.nfs -h/--help output typo with -f force unmount +%patch059 -p1 +# 1001443 - if no args specified, mount.nfs can not output the usage info +%patch060 -p1 +# 1339877 - nfsidmap add help -h option to output usage info +%patch061 -p1 +# 1340788 - [exportfs] should not fail on empty exports file with... +%patch062 -p1 +# 1347030 - rpc.statd emits warnings like "Failed to delete:.... +%patch063 -p1 +# 1300007 - start-statd: don't run multiple rpc.statds on the one host. (v2) +%patch064 -p1 +# 1353680 - NFSv4 export of "/" doesn't respect "crossmnt" export option +%patch065 -p1 +# 1363737 - man mount.nfs incorrectly mentions the default mount.... +%patch066 -p1 %patch100 -p1 %patch101 -p1 @@ -221,11 +320,10 @@ rm -rf $RPM_BUILD_ROOT/* mkdir -p $RPM_BUILD_ROOT%/sbin mkdir -p $RPM_BUILD_ROOT%{_sbindir} mkdir -p $RPM_BUILD_ROOT%{_unitdir} -mkdir -p $RPM_BUILD_ROOT%{_unitdir}/nfs.target.wants mkdir -p ${RPM_BUILD_ROOT}%{_mandir}/man8 mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/request-key.d -mkdir -p $RPM_BUILD_ROOT/lib/modprobe.d/ +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d make DESTDIR=$RPM_BUILD_ROOT install install -s -m 755 tools/rpcdebug/rpcdebug $RPM_BUILD_ROOT%{_sbindir} install -m 644 utils/mount/nfsmount.conf $RPM_BUILD_ROOT%{_sysconfdir} @@ -247,6 +345,7 @@ done mkdir -p $RPM_BUILD_ROOT/run/sysconfig mkdir -p $RPM_BUILD_ROOT/usr/lib/systemd/scripts install -m 755 %{SOURCE3} $RPM_BUILD_ROOT/usr/lib/systemd/scripts/nfs-utils_env.sh +install -m 644 %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir}/modprobe.d/lockd.conf # # For backwards compatablity @@ -355,6 +454,21 @@ if [ ! -f /etc/sysconfig/nfs-utils-disable-postun ]; then /bin/systemctl --system daemon-reload >/dev/null 2>&1 || : fi +%posttrans +# clean up cruft left over by upgrade from versions < 1.3.0-0.8.el7 +if [ -h /etc/systemd/system/multi-user.target.wants/nfs.target ]; then + tgt=$(readlink /etc/systemd/system/multi-user.target.wants/nfs.target) + if [ ! -e $tgt ]; then + /bin/systemctl --quiet is-enabled nfs-server &>/dev/null + reenable=$? + rm -rf /etc/systemd/system/multi-user.target.wants/nfs.target &>/dev/null + rm -rf /etc/systemd/system/nfs.target.wants &>/dev/null + if [ $reenable ]; then + /bin/systemctl --quiet reenable nfs-server &>/dev/null || : + fi + fi +fi + %triggerun -- nfs-utils < 1:1.2.9-0.5 /bin/systemctl stop nfs-secure.service >/dev/null 2>&1 || : /bin/systemctl disable nfs-secure.service >/dev/null 2>&1 || : @@ -379,7 +493,7 @@ fi %config(noreplace) /etc/nfsmount.conf %dir %{_sysconfdir}/exports.d %dir %{_sharedstatedir}/nfs/v4recovery -%dir %{_sharedstatedir}/nfs/rpc_pipefs +%dir %attr(555,root,root) %{_sharedstatedir}/nfs/rpc_pipefs %dir %{_sharedstatedir}/nfs %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd %dir %attr(700,rpcuser,rpcuser) %{_sharedstatedir}/nfs/statd/sm @@ -389,6 +503,7 @@ fi %config(noreplace) %verify(not md5 size mtime) %{_sharedstatedir}/nfs/etab %config(noreplace) %verify(not md5 size mtime) %{_sharedstatedir}/nfs/rmtab %config(noreplace) %{_sysconfdir}/request-key.d/id_resolver.conf +%config(noreplace) %{_sysconfdir}/modprobe.d/lockd.conf %doc linux-nfs/ChangeLog linux-nfs/KNOWNBUGS linux-nfs/NEW linux-nfs/README %doc linux-nfs/THANKS linux-nfs/TODO /sbin/rpc.statd @@ -419,12 +534,73 @@ fi /sbin/umount.nfs4 %changelog -* Mon Feb 22 2016 Steve Dickson 1.3.0-0.21_2.1 -- Update to nfsdcltrack v2 schema (bz 1309625) - -* Tue Dec 15 2015 Steve Dickson 1.3.0-0.21_2 -- exportfs: Restore the EAI_NONAME check in host_pton() (bz 1288435) -- gssd: Don't assume the machine account will be in uppercase (bz 1288433) +* Wed Aug 17 2016 Scott Mayhew 1.3.0-0.33 +spec: clean up cruft left over by upgrade from older versions (bz 1203765) + +* Thu Aug 4 2016 Steve Dickson 1.3.0-0.32 +- mount.nfs.man, nfs.man: Update distinction between fstypes (bz 1363737) + +* Mon Jul 11 2016 Steve Dickson 1.3.0-0.31 +- mountd: fix next_mnt handling for "/" (bz 1353680) + +* Thu Jun 23 2016 Steve Dickson 1.3.0-0.30 +- statd: suppress a benign log message in nsm_delete_host() (bz 1347030) +- start-statd: Use flock to serialize the running of this script (bz 1300007) +- spec: update requires version of libtirpc-devel (bz 1346711) + +* Thu Jun 9 2016 Steve Dickson 1.3.0-0.29 +- mountd: fix netgroup lookup for resolvable IP addresses (bz 1341908) +- umount: fixed typo in usage message (bz 1246329) +- mount.nfs: added usage output when no arguemnts are given (bz 1001443) +- nfsidmap: added the -h option (bz 1339877) +- exportfs: Do not fail on empty exports file (bz 1340788) + +* Tue Jun 7 2016 Steve Dickson 1.3.0-0.28 +- Added max/min to nlm_timeout comment in lockd.conf (bz 1264387) + +* Fri May 20 2016 Steve Dickson 1.3.0-0.27 +- nfsd: use correct byte order on rdma port (bz 1310691) +- blkmapd: Add the -h flag on the man page (bz 1001431) +- nfsd: Remove some warnings nfsd.c (bz 1336419) +- mountd.man: Update to change -P option as an alias for -p (bz 1037924) +- mountd: cleaned up usage message (bz 1003716) +- mountstats.man: fixed typo in man page (bz 1263966) + +* Mon May 16 2016 Steve Dickson 1.3.0-0.26 +- gssd: use pthreads to handle upcalls (bz 1331540) +- gssd: Fix inner-loop variable reuse (bz 1334848) +- mount: run START_STATD fully as root (bz 1275082) +- lockd: added lockd.conf to set module parameters (bz 1264387) + +* Tue May 3 2016 Steve Dickson 1.3.0-0.25 +- mountstats.man: Remove a few bogus .R macros (bz 1266013) +- systemd: ensure nfs-config service is re-run as needed (bz 1331460) +- exportfs: Deal with path's trailing "/" (bz 1276534) +- exportfs: replace one xlog(D_GENERAL) in host_canonname() (bz 1331801) +- exportfs: Fix buf size in test_export() dump() (bz 1243234) +- blkmapd: Added a usage routine (bz 1001431) +- rpc.idmapd: Added a usage routine (bz 1001438) + +* Thu Apr 28 2016 Steve Dickson 1.3.0-0.24 +- rpcgssd: added upstream debugging support (bz 1273163) +- start-statd: start rpc.statd only once (bz 1300007) +- exportfs: Don't stop the server from coming up when exportfs fails (bz 1287468) +- Changed install permissions on /var/lib/nfs/rpc_pipefs (bz 1291514) +- nfs.sysconfig: added note about the default keytab needing to exist (bz 1292607) +- mount.nfs: skip server address resolution on remount (bz 1313550) +- nfs_connect_nb: handle EINTR during connection establishment (bz 1299003) +- statd: Update existing record if we rece SM_MON with new cookie (bz 1284576) +- nfsidmap: updated to add in two new features (bz 1290488) + +* Thu Feb 11 2016 Steve Dickson 1.3.0-0.23 +- Update to nfsdcltrack v2 schema (bz 1285097) +- mountd: print an error message when no versions are specified (bz 1003539) +- mountd: added missing arugment to usage string (bz 1003716) + +* Thu Dec 3 2015 Steve Dickson 1.3.0-0.22 +- nfsidmap: Correct a failure to set key timeout values (bz 1161222) +- exportfs: Restore the EAI_NONAME check in host_pton() (bz 1276099) +- gssd: Don't assume the machine account will be in uppercase (bz 1268040) * Tue Sep 29 2015 Steve Dickson 1.3.0-0.21 - Stop gssd from flooding the KDC with TGT fetches (bz 1264999)