autofs-5.0.6 - catch EHOSTUNREACH and bail out early

From: Ian Kent <raven@themaw.net>

Now that the lower layers of the rpc code has been reworked
to propogate error returns up to the top level code we can
catch the EHOSTUNREACH return and stop the probe since the
host isn't responding.

Also, since UDP is a broadcast protocol we don't get the
EHOSTUNREACH and always have to wait, so change the probe
order to try TCP first. Using UDP first was originally
done to reduce reserved port usage but autofs probing uses
higher numbered ports now so this shouldn't introduce
problem even for older implementations.
---

 CHANGELOG            |    1 +
 include/replicated.h |    3 +++
 modules/replicated.c |   55 ++++++++++++++++++++++++++++++++++++--------------
 3 files changed, 44 insertions(+), 15 deletions(-)


diff --git a/CHANGELOG b/CHANGELOG
index c581099..69ade49 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -31,6 +31,7 @@
 - add function to check mount.nfs version.
 - reinstate singleton mount probe.
 - rework error return handling in rpc code.
+- catch EHOSTUNREACH and bail out early.
 
 28/06/2011 autofs-5.0.6
 -----------------------
diff --git a/include/replicated.h b/include/replicated.h
index 206918e..a143ccf 100644
--- a/include/replicated.h
+++ b/include/replicated.h
@@ -48,6 +48,9 @@
 #define TCP_SELECTED_MASK	0x00FF
 #define UDP_SELECTED_MASK	0xFF00
 
+#define IS_ERR(supported)	(0x8000 & supported)
+#define ERR(supported)		(IS_ERR(supported) ? (~supported + 1) : supported)
+
 #define RPC_TIMEOUT		5
 
 struct host {
diff --git a/modules/replicated.c b/modules/replicated.c
index ab8fdf3..e14c5da 100644
--- a/modules/replicated.c
+++ b/modules/replicated.c
@@ -563,7 +563,9 @@ static unsigned int get_nfs_info(unsigned logopt, struct host *host,
 		status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
 	else
 		status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS4_VERSION);
-	if (!status) {
+	if (status == -EHOSTUNREACH)
+		return (unsigned int) status;
+	else if (!status) {
 		gettimeofday(&start, &tz);
 		status = rpc_ping_proto(rpc_info);
 		gettimeofday(&end, &tz);
@@ -589,7 +591,10 @@ v3_ver:
 		status = rpc_portmap_getclient(pm_info,
 				host->name, host->addr, host->addr_len,
 				proto, RPC_CLOSE_DEFAULT);
-		if (status)
+		if (status == -EHOSTUNREACH) {
+			supported = status;
+			goto done_ver;
+		} else if (status)
 			goto done_ver;
 	}
 
@@ -602,16 +607,23 @@ v3_ver:
 	} else {
 		parms.pm_prot = rpc_info->proto->p_proto;
 		parms.pm_vers = NFS3_VERSION;
-		rpc_info->port = rpc_portmap_getport(pm_info, &parms);
-		if (rpc_info->port < 0)
+		status = rpc_portmap_getport(pm_info, &parms);
+		if (status == -EHOSTUNREACH) {
+			supported = status;
+			goto done_ver;
+		} else if (status < 0)
 			goto v2_ver;
+		rpc_info->port = status;
 	}
 
 	if (rpc_info->proto->p_proto == IPPROTO_UDP)
 		status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
 	else
 		status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS3_VERSION);
-	if (!status) {
+	if (status == -EHOSTUNREACH) {
+		supported = status;
+		goto done_ver;
+	} else if (!status) {
 		gettimeofday(&start, &tz);
 		status = rpc_ping_proto(rpc_info);
 		gettimeofday(&end, &tz);
@@ -643,15 +655,23 @@ v2_ver:
 		parms.pm_prot = rpc_info->proto->p_proto;
 		parms.pm_vers = NFS2_VERSION;
 		rpc_info->port = rpc_portmap_getport(pm_info, &parms);
-		if (rpc_info->port < 0)
+		status = rpc_portmap_getport(pm_info, &parms);
+		if (status == -EHOSTUNREACH) {
+			supported = status;
+			goto done_ver;
+		} else if (status < 0)
 			goto done_ver;
+		rpc_info->port = status;
 	}
 
 	if (rpc_info->proto->p_proto == IPPROTO_UDP)
 		status = rpc_udp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
 	else
 		status = rpc_tcp_getclient(rpc_info, NFS_PROGRAM, NFS2_VERSION);
-	if (!status) {
+	if (status == -EHOSTUNREACH) {
+		supported = status;
+		goto done_ver;
+	} else if (!status) {
 		gettimeofday(&start, &tz);
 		status = rpc_ping_proto(rpc_info);
 		gettimeofday(&end, &tz);
@@ -728,21 +748,24 @@ static int get_vers_and_cost(unsigned logopt, struct host *host,
 
 	vers &= version;
 
-	if (version & UDP_REQUESTED) {
+	if (version & TCP_REQUESTED) {
 		supported = get_nfs_info(logopt, host,
-				   &pm_info, &rpc_info, "udp", vers, options);
-		if (supported) {
+				   &pm_info, &rpc_info, "tcp", vers, options);
+		if (IS_ERR(supported)) {
+			if (ERR(supported) == EHOSTUNREACH)
+				return ret;
+		} else if (supported) {
 			ret = 1;
-			host->version |= (supported << 8);
+			host->version |= supported;
 		}
 	}
 
-	if (version & TCP_REQUESTED) {
+	if (version & UDP_REQUESTED) {
 		supported = get_nfs_info(logopt, host,
-				   &pm_info, &rpc_info, "tcp", vers, options);
+				   &pm_info, &rpc_info, "udp", vers, options);
 		if (supported) {
 			ret = 1;
-			host->version |= supported;
+			host->version |= (supported << 8);
 		}
 	}
 
@@ -848,7 +871,9 @@ static int get_supported_ver_and_cost(unsigned logopt, struct host *host,
 		status = rpc_udp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
 	else
 		status = rpc_tcp_getclient(&rpc_info, NFS_PROGRAM, parms.pm_vers);
-	if (!status) {
+	if (status == -EHOSTUNREACH)
+		goto done;
+	else if (!status) {
 		gettimeofday(&start, &tz);
 		status = rpc_ping_proto(&rpc_info);
 		gettimeofday(&end, &tz);