[nfs-utils] Fixed a regession with -p arguemnt to rpc.mountd

Steve Dickson steved at fedoraproject.org
Mon Oct 4 19:00:48 UTC 2010


commit d542391695c008ceaee05473ad5808245e95d1d0
Author: Steve Dickson <steved at redhat.com>
Date:   Mon Oct 4 14:58:34 2010 -0400

    Fixed a regession with -p arguemnt to rpc.mountd
    
    Signed-off-by: Steve Dickson <steved at redhat.com>

 nfs-utils-1.2.3-libnfs-multiports.patch |  323 +++++++++++++++++++++++++++++++
 nfs-utils.spec                          |    8 +-
 2 files changed, 330 insertions(+), 1 deletions(-)
---
diff --git a/nfs-utils-1.2.3-libnfs-multiports.patch b/nfs-utils-1.2.3-libnfs-multiports.patch
new file mode 100644
index 0000000..2c197e5
--- /dev/null
+++ b/nfs-utils-1.2.3-libnfs-multiports.patch
@@ -0,0 +1,323 @@
+commit 0e94118815e8ff9c1142117764ee3e6cddba0395
+Author: Chuck Lever <chuck.lever at oracle.com>
+Date:   Fri Oct 1 15:04:20 2010 -0400
+
+    libnfs.a: Allow multiple RPC listeners to share listener port number
+    
+    Normally, when "-p" is not specified on the mountd command line, the
+    TI-RPC library chooses random port numbers for each listener.  If a
+    port number _is_ specified on the command line, all the listeners
+    will get the same port number, so SO_REUSEADDR needs to be set on
+    each socket.
+    
+    Thus we can't let TI-RPC create the listener sockets for us in this
+    case; we must create them ourselves and then set SO_REUSEADDR (and
+    other socket options) by hand.
+    
+    Different versions of the same RPC program have to share the same
+    listener and SVCXPRT, so we have to cache xprts we create, and re-use
+    them when additional requests for registration come from the
+    application.
+    
+    Though it doesn't look like it, this fix was "copied" from the legacy
+    rpc_init() function.  It's more complicated for TI-RPC, of course,
+    since you can have an arbitrary number of listeners, not just two
+    (one for AF_INET UDP and one for AF_INET TCP).
+    
+    Fix for:
+    
+      https://bugzilla.linux-nfs.org/show_bug.cgi?id=190
+    
+    There have been no reports of problems with specifying statd's
+    listener port, but I expect this is a problem for statd too.
+    
+    Signed-off-by: Chuck Lever <chuck.lever at oracle.com>
+
+diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
+index 59ba505..fdc4846 100644
+--- a/support/nfs/svc_create.c
++++ b/support/nfs/svc_create.c
+@@ -27,6 +27,7 @@
+ #include <memory.h>
+ #include <signal.h>
+ #include <unistd.h>
++#include <errno.h>
+ #include <netdb.h>
+ 
+ #include <netinet/in.h>
+@@ -41,11 +42,68 @@
+ #include "tcpwrapper.h"
+ #endif
+ 
++#include "sockaddr.h"
+ #include "rpcmisc.h"
+ #include "xlog.h"
+ 
+ #ifdef HAVE_LIBTIRPC
+ 
++#define SVC_CREATE_XPRT_CACHE_SIZE	(8)
++static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
++
++/*
++ * Cache an SVC xprt, in case there are more programs or versions to
++ * register against it.
++ */
++static void
++svc_create_cache_xprt(SVCXPRT *xprt)
++{
++	unsigned int i;
++
++	/* Check if we've already got this one... */
++	for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
++		if (svc_create_xprt_cache[i] == xprt)
++			return;
++
++	/* No, we don't.  Cache it. */
++	for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
++		if (svc_create_xprt_cache[i] == NULL) {
++			svc_create_xprt_cache[i] = xprt;
++			return;
++		}
++
++	xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
++}
++
++/*
++ * Find a previously cached SVC xprt structure with the given bind address
++ * and transport semantics.
++ *
++ * Returns pointer to a SVC xprt.
++ *
++ * If no matching SVC XPRT can be found, NULL is returned.
++ */
++static SVCXPRT *
++svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
++{
++	unsigned int i;
++
++	for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
++		SVCXPRT *xprt = svc_create_xprt_cache[i];
++		struct sockaddr *sap;
++
++		if (xprt == NULL)
++			continue;
++		if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
++			continue;
++		sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
++		if (!nfs_compare_sockaddr(bindaddr, sap))
++			continue;
++		return xprt;
++	}
++	return NULL;
++}
++
+ /*
+  * Set up an appropriate bind address, given @port and @nconf.
+  *
+@@ -98,17 +156,112 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
+ 	return ai;
+ }
+ 
++/*
++ * Create a listener socket on a specific bindaddr, and set
++ * special socket options to allow it to share the same port
++ * as other listeners.
++ *
++ * Returns an open, bound, and possibly listening network
++ * socket on success.
++ *
++ * Otherwise returns -1 if some error occurs.
++ */
++static int
++svc_create_sock(const struct sockaddr *sap, socklen_t salen,
++		struct netconfig *nconf)
++{
++	int fd, type, protocol;
++	int one = 1;
++
++	switch(nconf->nc_semantics) {
++	case NC_TPI_CLTS:
++		type = SOCK_DGRAM;
++		break;
++	case NC_TPI_COTS_ORD:
++		type = SOCK_STREAM;
++		break;
++	default:
++		xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u",
++			__func__, nconf->nc_semantics);
++		return -1;
++	}
++
++	if (strcmp(nconf->nc_proto, NC_UDP) == 0)
++		protocol = (int)IPPROTO_UDP;
++	else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
++		protocol = (int)IPPROTO_TCP;
++	else {
++		xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
++			__func__, nconf->nc_proto);
++		return -1;
++	}
++
++	fd = socket((int)sap->sa_family, type, protocol);
++	if (fd == -1) {
++		xlog(L_ERROR, "Could not make a socket: (%d) %m",
++			errno);
++		return -1;
++	}
++
++#ifdef IPV6_SUPPORTED
++	if (sap->sa_family == AF_INET6) {
++		if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
++				&one, sizeof(one)) == -1) {
++			xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m",
++				errno);
++			(void)close(fd);
++			return -1;
++		}
++	}
++#endif	/* IPV6_SUPPORTED */
++
++	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
++		       &one, sizeof(one)) == -1) {
++		xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
++			errno);
++		(void)close(fd);
++		return -1;
++	}
++
++	if (bind(fd, sap, salen) == -1) {
++		xlog(L_ERROR, "Could not bind socket: (%d) %m",
++			errno);
++		(void)close(fd);
++		return -1;
++	}
++
++	if (nconf->nc_semantics == NC_TPI_COTS_ORD)
++		if (listen(fd, SOMAXCONN) == -1) {
++			xlog(L_ERROR, "Could not listen on socket: (%d) %m",
++				errno);
++			(void)close(fd);
++			return -1;
++		}
++
++	return fd;
++}
++
++/*
++ * The simple case is allowing the TI-RPC library to create a
++ * transport itself, given just the bind address and transport
++ * semantics.
++ *
++ * The port is chosen at random by the library; we don't know
++ * what it is.  So the new xprt cannot be cached here.
++ *
++ * Returns the count of started listeners (one or zero).
++ */
+ static unsigned int
+-svc_create_nconf(const char *name, const rpcprog_t program,
++svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
+ 		const rpcvers_t version,
+ 		void (*dispatch)(struct svc_req *, SVCXPRT *),
+-		const uint16_t port, struct netconfig *nconf)
++		struct netconfig *nconf)
+ {
+ 	struct t_bind bindaddr;
+ 	struct addrinfo *ai;
+ 	SVCXPRT	*xprt;
+ 
+-	ai = svc_create_bindaddr(nconf, port);
++	ai = svc_create_bindaddr(nconf, 0);
+ 	if (ai == NULL)
+ 		return 0;
+ 
+@@ -119,7 +272,7 @@ svc_create_nconf(const char *name, const rpcprog_t program,
+ 	freeaddrinfo(ai);
+ 	if (xprt == NULL) {
+ 		xlog(D_GENERAL, "Failed to create listener xprt "
+-				"(%s, %u, %s)", name, version, nconf->nc_netid);
++			"(%s, %u, %s)", name, version, nconf->nc_netid);
+ 		return 0;
+ 	}
+ 
+@@ -133,6 +286,81 @@ svc_create_nconf(const char *name, const rpcprog_t program,
+ 	return 1;
+ }
+ 
++/*
++ * If a port is specified on the command line, that port value will be
++ * the same for all listeners created here.  Create each listener socket
++ * in advance and set SO_REUSEADDR, rather than allowing the RPC library
++ * to create the listeners for us on a randomly chosen port (RPC_ANYFD).
++ *
++ * Also, to support multiple RPC versions on the same listener, register
++ * any new versions on the same transport that is already handling other
++ * versions on the same bindaddr and transport.  To accomplish this,
++ * cache previously created xprts on a list, and check that list before
++ * creating a new socket for this [program, version].
++ *
++ * Returns the count of started listeners (one or zero).
++ */
++static unsigned int
++svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
++		const rpcvers_t version,
++		void (*dispatch)(struct svc_req *, SVCXPRT *),
++		const uint16_t port, struct netconfig *nconf)
++{
++	struct addrinfo *ai;
++	SVCXPRT	*xprt;
++
++	ai = svc_create_bindaddr(nconf, port);
++	if (ai == NULL)
++		return 0;
++
++	xprt = svc_create_find_xprt(ai->ai_addr, nconf);
++	if (xprt == NULL) {
++		int fd;
++
++		fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
++		if (fd == -1)
++			goto out_free;
++
++		xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
++		if (xprt == NULL) {
++			xlog(D_GENERAL, "Failed to create listener xprt "
++				"(%s, %u, %s)", name, version, nconf->nc_netid);
++			(void)close(fd);
++			goto out_free;
++		}
++	}
++
++	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);
++		goto out_free;
++	}
++
++	svc_create_cache_xprt(xprt);
++
++	freeaddrinfo(ai);
++	return 1;
++
++out_free:
++	freeaddrinfo(ai);
++	return 0;
++}
++
++static unsigned int
++svc_create_nconf(const char *name, const rpcprog_t program,
++		const rpcvers_t version,
++		void (*dispatch)(struct svc_req *, SVCXPRT *),
++		const uint16_t port, struct netconfig *nconf)
++{
++	if (port != 0)
++		return svc_create_nconf_fixed_port(name, program,
++			version, dispatch, port, nconf);
++
++	return svc_create_nconf_rand_port(name, program,
++			version, dispatch, nconf);
++}
++
+ /**
+  * nfs_svc_create - start up RPC svc listeners
+  * @name: C string containing name of new service
diff --git a/nfs-utils.spec b/nfs-utils.spec
index e40c3ad..efd8d4b 100644
--- a/nfs-utils.spec
+++ b/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.2.3
-Release: 0%{?dist}
+Release: 0.1%{?dist}
 Epoch: 1
 
 # group all 32bit related archs
@@ -18,6 +18,8 @@ Source13: rpcgssd.init
 Source14: rpcsvcgssd.init
 Source15: nfs.sysconfig
 
+Patch001: nfs-utils-1.2.3-libnfs-multiports.patch
+
 Patch100: nfs-utils-1.2.1-statdpath-man.patch
 Patch101: nfs-utils-1.2.2-statdpath.patch
 Patch102: nfs-utils-1.2.1-exp-subtree-warn-off.patch
@@ -68,6 +70,7 @@ This package also contains the mount.nfs and umount.nfs program.
 
 %prep
 %setup -q
+%patch001 -p1
 
 %patch100 -p1
 %patch101 -p1
@@ -247,6 +250,9 @@ fi
 %attr(4755,root,root)   /sbin/umount.nfs4
 
 %changelog
+* Mon Oct  4 2010 Steve Dickson <steved at redhat.com> 1.2.3-0.1
+- Fixed a regession with -p arguemnt to rpc.mountd 
+
 * Thu Sep 30 2010 Steve Dickson <steved at redhat.com> 1.2.3-0
 - Updated to latest upstream release: nfs-utils-1-2-3
 


More information about the scm-commits mailing list