From e955777c56324835582fd94f13e02889789f8c8b Mon Sep 17 00:00:00 2001 From: Steve Dickson Date: Jul 15 2009 14:37:40 +0000 Subject: - Added upstream 1.2.1-rc2 patch - A large number of mount command changes. --- diff --git a/nfs-utils-1.2.1-rc2.patch b/nfs-utils-1.2.1-rc2.patch new file mode 100644 index 0000000..455b3fc --- /dev/null +++ b/nfs-utils-1.2.1-rc2.patch @@ -0,0 +1,1966 @@ +diff --git a/.gitignore b/.gitignore +index cfc329a..632609e 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -13,6 +13,11 @@ ltmain.sh + Makefile.in + missing + support/include/config.h.in ++aclocal/libtool.m4 ++aclocal/ltoptions.m4 ++aclocal/ltsugar.m4 ++aclocal/ltversion.m4 ++aclocal/lt~obsolete.m4 + # files generated by configure + confdefs.h + config.status +diff --git a/support/include/nfsrpc.h b/support/include/nfsrpc.h +index 543c35b..dff6af7 100644 +--- a/support/include/nfsrpc.h ++++ b/support/include/nfsrpc.h +@@ -49,6 +49,25 @@ + #define NSMPROG ((rpcprog_t)100024) + #endif + ++/** ++ * nfs_clear_rpc_createerr - zap all error reporting fields ++ * ++ */ ++static inline void nfs_clear_rpc_createerr(void) ++{ ++ memset(&rpc_createerr, 0, sizeof(rpc_createerr)); ++} ++ ++/* ++ * Extract port value from a socket address ++ */ ++extern uint16_t nfs_get_port(const struct sockaddr *); ++ ++/* ++ * Set port value in a socket address ++ */ ++extern void nfs_set_port(struct sockaddr *, const uint16_t); ++ + /* + * Look up an RPC program name in /etc/rpc + */ +@@ -73,8 +92,7 @@ extern CLIENT *nfs_get_priv_rpcclient( const struct sockaddr *, + /* + * Convert a socket address to a universal address + */ +-extern char *nfs_sockaddr2universal(const struct sockaddr *, +- const socklen_t); ++extern char *nfs_sockaddr2universal(const struct sockaddr *); + + /* + * Extract port number from a universal address +@@ -114,7 +132,6 @@ extern unsigned short nfs_rpcb_getaddr(const struct sockaddr *, + const socklen_t, + const unsigned short, + const struct sockaddr *, +- const socklen_t, + const rpcprog_t, + const rpcvers_t, + const unsigned short, +diff --git a/support/nfs/getport.c b/support/nfs/getport.c +index cf1677e..4bdf556 100644 +--- a/support/nfs/getport.c ++++ b/support/nfs/getport.c +@@ -65,77 +65,52 @@ static const rpcvers_t default_rpcb_version = RPCBVERS_4; + static const rpcvers_t default_rpcb_version = PMAPVERS; + #endif /* !HAVE_LIBTIRPC */ + +-#ifdef HAVE_DECL_AI_ADDRCONFIG + /* +- * getaddrinfo(3) generates a usable loopback address based on how the +- * local network interfaces are configured. RFC 3484 requires that the +- * results are sorted so that the first result has the best likelihood +- * of working, so we try just that first result. ++ * Historical: Map TCP connect timeouts to timeout ++ * error code used by UDP. ++ */ ++static void ++nfs_gp_map_tcp_errorcodes(const unsigned short protocol) ++{ ++ if (protocol != IPPROTO_TCP) ++ return; ++ ++ switch (rpc_createerr.cf_error.re_errno) { ++ case ETIMEDOUT: ++ rpc_createerr.cf_stat = RPC_TIMEDOUT; ++ break; ++ case ECONNREFUSED: ++ rpc_createerr.cf_stat = RPC_CANTRECV; ++ break; ++ } ++} ++ ++/* ++ * There's no easy way to tell how the local system's networking ++ * and rpcbind is configured (ie. whether we want to use IPv6 or ++ * IPv4 loopback to contact RPC services on the local host). We ++ * punt and simply try to look up "localhost". + * + * Returns TRUE on success. + */ + static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen) + { + struct addrinfo *gai_results; +- struct addrinfo gai_hint = { +- .ai_flags = AI_ADDRCONFIG, +- }; +- socklen_t len = *salen; + int ret = 0; + +- if (getaddrinfo(NULL, "sunrpc", &gai_hint, &gai_results)) ++ if (getaddrinfo("localhost", NULL, NULL, &gai_results)) + return 0; + +- switch (gai_results->ai_addr->sa_family) { +- case AF_INET: +- case AF_INET6: +- if (len >= gai_results->ai_addrlen) { +- memcpy(sap, gai_results->ai_addr, +- gai_results->ai_addrlen); +- *salen = gai_results->ai_addrlen; +- ret = 1; +- } ++ if (*salen >= gai_results->ai_addrlen) { ++ memcpy(sap, gai_results->ai_addr, ++ gai_results->ai_addrlen); ++ *salen = gai_results->ai_addrlen; ++ ret = 1; + } + + freeaddrinfo(gai_results); + return ret; + } +-#else +-/* +- * Old versions of getaddrinfo(3) don't support AI_ADDRCONFIG, so we +- * have a fallback for building on legacy systems. +- */ +-static int nfs_gp_loopback_address(struct sockaddr *sap, socklen_t *salen) +-{ +- struct sockaddr_in *sin = (struct sockaddr_in *)sap; +- +- memset(sin, 0, sizeof(*sin)); +- +- sin->sin_family = AF_INET; +- sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); +- *salen = sizeof(*sin); +- +- return 1; +-} +-#endif +- +-/* +- * Plant port number in @sap. @port is already in network byte order. +- */ +-static void nfs_gp_set_port(struct sockaddr *sap, const in_port_t port) +-{ +- struct sockaddr_in *sin = (struct sockaddr_in *)sap; +- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; +- +- switch (sap->sa_family) { +- case AF_INET: +- sin->sin_port = port; +- break; +- case AF_INET6: +- sin6->sin6_port = port; +- break; +- } +-} + + /* + * Look up a network service in /etc/services and return the +@@ -201,7 +176,7 @@ static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol) + * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to + * reflect the error. + */ +-static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap, ++static CLIENT *nfs_gp_get_rpcbclient(struct sockaddr *sap, + const socklen_t salen, + const unsigned short transport, + const rpcvers_t version, +@@ -214,15 +189,14 @@ static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap, + "sunrpc", + NULL, + }; +- struct sockaddr_storage address; +- struct sockaddr *saddr = (struct sockaddr *)&address; + rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, rpcb_pgmtbl); ++ CLIENT *clnt; + +- memcpy(saddr, sap, (size_t)salen); +- nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport)); +- +- return nfs_get_rpcclient(saddr, salen, transport, rpcb_prog, +- version, timeout); ++ nfs_set_port(sap, ntohs(nfs_gp_get_rpcb_port(transport))); ++ clnt = nfs_get_rpcclient(sap, salen, transport, rpcb_prog, ++ version, timeout); ++ nfs_gp_map_tcp_errorcodes(transport); ++ return clnt; + } + + /* +@@ -352,7 +326,6 @@ int nfs_universal2port(const char *uaddr) + /** + * nfs_sockaddr2universal - convert a sockaddr to a "universal address" + * @sap: pointer to a socket address +- * @salen: length of socket address + * + * Universal addresses (defined in RFC 1833) are used when calling an + * rpcbind daemon via protocol versions 3 or 4.. +@@ -361,81 +334,56 @@ int nfs_universal2port(const char *uaddr) + * the returned string. Otherwise NULL is returned and + * rpc_createerr.cf_stat is set to reflect the error. + * ++ * inet_ntop(3) is used here, since getnameinfo(3) is not available ++ * in some earlier glibc releases, and we don't require support for ++ * scope IDs for universal addresses. + */ +-#ifdef HAVE_GETNAMEINFO +- +-char *nfs_sockaddr2universal(const struct sockaddr *sap, +- const socklen_t salen) ++char *nfs_sockaddr2universal(const struct sockaddr *sap) + { +- struct sockaddr_un *sun = (struct sockaddr_un *)sap; +- char buf[NI_MAXHOST]; ++ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; ++ const struct sockaddr_un *sun = (const struct sockaddr_un *)sap; ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; ++ char buf[INET6_ADDRSTRLEN + 8 /* for port information */]; + uint16_t port; ++ size_t count; ++ char *result; ++ int len; + + switch (sap->sa_family) { + case AF_LOCAL: + return strndup(sun->sun_path, sizeof(sun->sun_path)); + case AF_INET: +- if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), +- NULL, 0, NI_NUMERICHOST) != 0) ++ if (inet_ntop(AF_INET, (const void *)&sin->sin_addr.s_addr, ++ buf, (socklen_t)sizeof(buf)) == NULL) + goto out_err; +- port = ntohs(((struct sockaddr_in *)sap)->sin_port); ++ port = ntohs(sin->sin_port); + break; + case AF_INET6: +- if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf), +- NULL, 0, NI_NUMERICHOST) != 0) ++ if (inet_ntop(AF_INET6, (const void *)&sin6->sin6_addr, ++ buf, (socklen_t)sizeof(buf)) == NULL) + goto out_err; +- port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port); ++ port = ntohs(sin6->sin6_port); + break; + default: + goto out_err; + } + +- (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u", ++ count = sizeof(buf) - strlen(buf); ++ len = snprintf(buf + strlen(buf), count, ".%u.%u", + (unsigned)(port >> 8), (unsigned)(port & 0xff)); +- +- return strdup(buf); +- +-out_err: +- rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; +- return NULL; +-} +- +-#else /* HAVE_GETNAMEINFO */ +- +-char *nfs_sockaddr2universal(const struct sockaddr *sap, +- const socklen_t salen) +-{ +- struct sockaddr_un *sun = (struct sockaddr_un *)sap; +- char buf[NI_MAXHOST]; +- uint16_t port; +- char *addr; +- +- switch (sap->sa_family) { +- case AF_LOCAL: +- return strndup(sun->sun_path, sizeof(sun->sun_path)); +- case AF_INET: +- addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr); +- if (addr != NULL && strlen(addr) > sizeof(buf)) +- goto out_err; +- strcpy(buf, addr); +- port = ntohs(((struct sockaddr_in *)sap)->sin_port); +- break; +- default: ++ /* before glibc 2.0.6, snprintf(3) could return -1 */ ++ if (len < 0 || (size_t)len > count) + goto out_err; +- } + +- (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u", +- (unsigned)(port >> 8), (unsigned)(port & 0xff)); +- +- return strdup(buf); ++ result = strdup(buf); ++ if (result != NULL) ++ return result; + + out_err: + rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE; + return NULL; + } + +-#endif /* HAVE_GETNAMEINFO */ +- + /* + * Send a NULL request to the indicated RPC service. + * +@@ -450,6 +398,10 @@ static int nfs_gp_ping(CLIENT *client, struct timeval timeout) + (xdrproc_t)xdr_void, NULL, + timeout); + ++ if (status != RPC_SUCCESS) { ++ rpc_createerr.cf_stat = status; ++ CLNT_GETERR(client, &rpc_createerr.cf_error); ++ } + return (int)(status == RPC_SUCCESS); + } + +@@ -458,15 +410,10 @@ static int nfs_gp_ping(CLIENT *client, struct timeval timeout) + /* + * Initialize the rpcb argument for a GETADDR request. + * +- * The rpcbind daemon ignores the parms.r_owner field in GETADDR +- * requests, but we plant an eye-catcher to help distinguish these +- * requests in network traces. +- * + * Returns 1 if successful, and caller must free strings pointed + * to by r_netid and r_addr; otherwise 0. + */ + static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, +- const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, +@@ -478,7 +425,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, + if (netid == NULL) + return 0; + +- addr = nfs_sockaddr2universal(sap, salen); ++ addr = nfs_sockaddr2universal(sap); + if (addr == NULL) { + free(netid); + return 0; +@@ -489,7 +436,7 @@ static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap, + parms->r_vers = version; + parms->r_netid = netid; + parms->r_addr = addr; +- parms->r_owner = "nfs-utils"; /* eye-catcher */ ++ parms->r_owner = ""; + + return 1; + } +@@ -531,7 +478,7 @@ static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client, + case RPC_SUCCESS: + if ((uaddr == NULL) || (uaddr[0] == '\0')) { + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; +- continue; ++ return 0; + } + + port = nfs_universal2port(uaddr); +@@ -587,7 +534,7 @@ static unsigned long nfs_gp_pmap_getport(CLIENT *client, + + if (status != RPC_SUCCESS) { + rpc_createerr.cf_stat = status; +- clnt_geterr(client, &rpc_createerr.cf_error); ++ CLNT_GETERR(client, &rpc_createerr.cf_error); + port = 0; + } else if (port == 0) + rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; +@@ -599,7 +546,6 @@ static unsigned long nfs_gp_pmap_getport(CLIENT *client, + + static unsigned short nfs_gp_getport_rpcb(CLIENT *client, + const struct sockaddr *sap, +- const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, +@@ -608,8 +554,8 @@ static unsigned short nfs_gp_getport_rpcb(CLIENT *client, + unsigned short port = 0; + struct rpcb parms; + +- if (nfs_gp_init_rpcb_parms(sap, salen, program, +- version, protocol, &parms) != 0) { ++ if (nfs_gp_init_rpcb_parms(sap, program, version, ++ protocol, &parms) != 0) { + port = nfs_gp_rpcb_getaddr(client, &parms, timeout); + nfs_gp_free_rpcb_parms(&parms); + } +@@ -645,7 +591,6 @@ static unsigned long nfs_gp_getport_pmap(CLIENT *client, + */ + static unsigned short nfs_gp_getport(CLIENT *client, + const struct sockaddr *sap, +- const socklen_t salen, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, +@@ -654,7 +599,7 @@ static unsigned short nfs_gp_getport(CLIENT *client, + switch (sap->sa_family) { + #ifdef HAVE_LIBTIRPC + case AF_INET6: +- return nfs_gp_getport_rpcb(client, sap, salen, program, ++ return nfs_gp_getport_rpcb(client, sap, program, + version, protocol, timeout); + #endif /* HAVE_LIBTIRPC */ + case AF_INET: +@@ -667,7 +612,7 @@ static unsigned short nfs_gp_getport(CLIENT *client, + } + + /** +- * nfs_rcp_ping - Determine if RPC service is responding to requests ++ * nfs_rpc_ping - Determine if RPC service is responding to requests + * @sap: pointer to address of server to query (port is already filled in) + * @salen: length of server address + * @program: requested RPC program number +@@ -682,6 +627,8 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen, + const rpcprog_t program, const rpcvers_t version, + const unsigned short protocol, const struct timeval *timeout) + { ++ struct sockaddr_storage address; ++ struct sockaddr *saddr = (struct sockaddr *)&address; + CLIENT *client; + struct timeval tout = { -1, 0 }; + int result = 0; +@@ -689,9 +636,14 @@ int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen, + if (timeout != NULL) + tout = *timeout; + +- client = nfs_get_rpcclient(sap, salen, protocol, program, version, &tout); ++ nfs_clear_rpc_createerr(); ++ ++ memcpy(saddr, sap, (size_t)salen); ++ client = nfs_get_rpcclient(saddr, salen, protocol, ++ program, version, &tout); + if (client != NULL) { + result = nfs_gp_ping(client, tout); ++ nfs_gp_map_tcp_errorcodes(protocol); + CLNT_DESTROY(client); + } + +@@ -744,14 +696,19 @@ unsigned short nfs_getport(const struct sockaddr *sap, + const rpcvers_t version, + const unsigned short protocol) + { ++ struct sockaddr_storage address; ++ struct sockaddr *saddr = (struct sockaddr *)&address; + struct timeval timeout = { -1, 0 }; + unsigned short port = 0; + CLIENT *client; + +- client = nfs_gp_get_rpcbclient(sap, salen, protocol, ++ nfs_clear_rpc_createerr(); ++ ++ memcpy(saddr, sap, (size_t)salen); ++ client = nfs_gp_get_rpcbclient(saddr, salen, protocol, + default_rpcb_version, &timeout); + if (client != NULL) { +- port = nfs_gp_getport(client, sap, salen, program, ++ port = nfs_gp_getport(client, saddr, program, + version, protocol, timeout); + CLNT_DESTROY(client); + } +@@ -786,10 +743,12 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, + CLIENT *client; + int result = 0; + ++ nfs_clear_rpc_createerr(); ++ + client = nfs_gp_get_rpcbclient(sap, salen, protocol, + default_rpcb_version, &timeout); + if (client != NULL) { +- port = nfs_gp_getport(client, sap, salen, program, ++ port = nfs_gp_getport(client, sap, program, + version, protocol, timeout); + CLNT_DESTROY(client); + client = NULL; +@@ -800,18 +759,21 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, + struct sockaddr *saddr = (struct sockaddr *)&address; + + memcpy(saddr, sap, (size_t)salen); +- nfs_gp_set_port(saddr, htons(port)); ++ nfs_set_port(saddr, port); ++ ++ nfs_clear_rpc_createerr(); + + client = nfs_get_rpcclient(saddr, salen, protocol, + program, version, &timeout); + if (client != NULL) { + result = nfs_gp_ping(client, timeout); ++ nfs_gp_map_tcp_errorcodes(protocol); + CLNT_DESTROY(client); + } + } + + if (result) +- nfs_gp_set_port(sap, htons(port)); ++ nfs_set_port(sap, port); + + return result; + } +@@ -840,13 +802,6 @@ int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen, + * isn't listening on /var/run/rpcbind.sock), send a query via UDP to localhost + * (UDP doesn't leave a socket in TIME_WAIT, and the timeout is a relatively + * short 3 seconds). +- * +- * getaddrinfo(3) generates a usable loopback address. RFC 3484 requires that +- * the results are sorted so that the first result has the best likelihood of +- * working, so we try just that first result. If IPv6 is all that is +- * available, we are sure to generate an AF_INET6 loopback address and use +- * rpcbindv4/v3 GETADDR. AF_INET6 requests go via rpcbind v4/3 in order to +- * detect if the requested RPC service supports AF_INET6 or not. + */ + unsigned short nfs_getlocalport(const rpcprot_t program, + const rpcvers_t version, +@@ -867,11 +822,13 @@ unsigned short nfs_getlocalport(const rpcprot_t program, + CLIENT *client; + struct timeval timeout = { -1, 0 }; + ++ nfs_clear_rpc_createerr(); ++ + client = nfs_gp_get_rpcbclient(sap, salen, 0, RPCBVERS_4, &timeout); + if (client != NULL) { + struct rpcb parms; + +- if (nfs_gp_init_rpcb_parms(sap, salen, program, version, ++ if (nfs_gp_init_rpcb_parms(sap, program, version, + protocol, &parms) != 0) { + port = nfs_gp_rpcb_getaddr(client, &parms, timeout); + nfs_gp_free_rpcb_parms(&parms); +@@ -881,6 +838,8 @@ unsigned short nfs_getlocalport(const rpcprot_t program, + #endif /* NFS_GP_LOCAL */ + + if (port == 0) { ++ nfs_clear_rpc_createerr(); ++ + if (nfs_gp_loopback_address(lb_addr, &lb_len)) { + port = nfs_getport(lb_addr, lb_len, + program, version, protocol); +@@ -897,7 +856,6 @@ unsigned short nfs_getlocalport(const rpcprot_t program, + * @salen: length of server address + * @transport: transport protocol to use for the query + * @addr: pointer to r_addr address +- * @addrlen: length of address + * @program: requested RPC program number + * @version: requested RPC version number + * @protocol: requested IPPROTO_ value of transport protocol +@@ -928,12 +886,13 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, + const socklen_t salen, + const unsigned short transport, + const struct sockaddr *addr, +- const socklen_t addrlen, + const rpcprog_t program, + const rpcvers_t version, + const unsigned short protocol, + const struct timeval *timeout) + { ++ struct sockaddr_storage address; ++ struct sockaddr *saddr = (struct sockaddr *)&address; + CLIENT *client; + struct rpcb parms; + struct timeval tout = { -1, 0 }; +@@ -942,9 +901,13 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, + if (timeout != NULL) + tout = *timeout; + +- client = nfs_gp_get_rpcbclient(sap, salen, transport, RPCBVERS_4, &tout); ++ nfs_clear_rpc_createerr(); ++ ++ memcpy(saddr, sap, (size_t)salen); ++ client = nfs_gp_get_rpcbclient(saddr, salen, transport, ++ RPCBVERS_4, &tout); + if (client != NULL) { +- if (nfs_gp_init_rpcb_parms(addr, addrlen, program, version, ++ if (nfs_gp_init_rpcb_parms(addr, program, version, + protocol, &parms) != 0) { + port = nfs_gp_rpcb_getaddr(client, &parms, tout); + nfs_gp_free_rpcb_parms(&parms); +@@ -957,16 +920,17 @@ unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, + + #else /* !HAVE_LIBTIRPC */ + +-unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap, +- const socklen_t salen, +- const unsigned short transport, +- const struct sockaddr *addr, +- const socklen_t addrlen, +- const rpcprog_t program, +- const rpcvers_t version, +- const unsigned short protocol, +- const struct timeval *timeout) ++unsigned short nfs_rpcb_getaddr(__attribute__((unused)) const struct sockaddr *sap, ++ __attribute__((unused)) const socklen_t salen, ++ __attribute__((unused)) const unsigned short transport, ++ __attribute__((unused)) const struct sockaddr *addr, ++ __attribute__((unused)) const rpcprog_t program, ++ __attribute__((unused)) const rpcvers_t version, ++ __attribute__((unused)) const unsigned short protocol, ++ __attribute__((unused)) const struct timeval *timeout) + { ++ nfs_clear_rpc_createerr(); ++ + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return 0; + } +@@ -1008,6 +972,8 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin, + const unsigned long protocol, + const struct timeval *timeout) + { ++ struct sockaddr_in address; ++ struct sockaddr *saddr = (struct sockaddr *)&address; + CLIENT *client; + struct pmap parms = { + .pm_prog = program, +@@ -1020,8 +986,10 @@ unsigned long nfs_pmap_getport(const struct sockaddr_in *sin, + if (timeout != NULL) + tout = *timeout; + +- client = nfs_gp_get_rpcbclient((struct sockaddr *)sin, +- (socklen_t)sizeof(*sin), ++ nfs_clear_rpc_createerr(); ++ ++ memcpy(saddr, sin, sizeof(address)); ++ client = nfs_gp_get_rpcbclient(saddr, (socklen_t)sizeof(*sin), + transport, PMAPVERS, &tout); + if (client != NULL) { + port = nfs_gp_pmap_getport(client, &parms, tout); +diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c +index cebf83d..9c20f61 100644 +--- a/support/nfs/rpc_socket.c ++++ b/support/nfs/rpc_socket.c +@@ -132,7 +132,7 @@ static int nfs_bind(const int sock, const sa_family_t family) + return -1; + } + +-#ifdef IPV6_SUPPORTED ++#ifdef HAVE_LIBTIRPC + + /* + * Bind a socket using an unused privileged source port. +@@ -162,7 +162,7 @@ static int nfs_bindresvport(const int sock, const sa_family_t family) + return -1; + } + +-#else /* !IPV6_SUPPORTED */ ++#else /* !HAVE_LIBTIRPC */ + + /* + * Bind a socket using an unused privileged source port. +@@ -180,7 +180,7 @@ static int nfs_bindresvport(const int sock, const sa_family_t family) + return bindresvport(sock, NULL); + } + +-#endif /* !IPV6_SUPPORTED */ ++#endif /* !HAVE_LIBTIRPC */ + + /* + * Perform a non-blocking connect on the socket fd. +@@ -326,7 +326,9 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap, + version, *timeout, &sock); + #endif /* !HAVE_LIBTIRPC */ + if (client != NULL) { +- CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout); ++ struct timeval retry_timeout = { 1, 0 }; ++ CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, ++ (char *)&retry_timeout); + CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL); + } else + (void)close(sock); +@@ -414,6 +416,49 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap, + } + + /** ++ * nfs_get_port - extract port value from a socket address ++ * @sap: pointer to socket address ++ * ++ * Returns port value in host byte order. ++ */ ++uint16_t ++nfs_get_port(const struct sockaddr *sap) ++{ ++ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; ++ const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; ++ ++ switch (sap->sa_family) { ++ case AF_INET: ++ return ntohs(sin->sin_port); ++ case AF_INET6: ++ return ntohs(sin6->sin6_port); ++ } ++ return 0; ++} ++ ++/** ++ * nfs_set_port - set port value in a socket address ++ * @sap: pointer to socket address ++ * @port: port value to set ++ * ++ */ ++void ++nfs_set_port(struct sockaddr *sap, const uint16_t port) ++{ ++ struct sockaddr_in *sin = (struct sockaddr_in *)sap; ++ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; ++ ++ switch (sap->sa_family) { ++ case AF_INET: ++ sin->sin_port = htons(port); ++ break; ++ case AF_INET6: ++ sin6->sin6_port = htons(port); ++ break; ++ } ++} ++ ++/** + * nfs_get_rpcclient - acquire an RPC client + * @sap: pointer to socket address of RPC server + * @salen: length of socket address +@@ -438,27 +483,21 @@ CLIENT *nfs_get_rpcclient(const struct sockaddr *sap, + const rpcvers_t version, + struct timeval *timeout) + { +- struct sockaddr_in *sin = (struct sockaddr_in *)sap; +- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; ++ nfs_clear_rpc_createerr(); + + switch (sap->sa_family) { + case AF_LOCAL: + return nfs_get_localclient(sap, salen, program, + version, timeout); + case AF_INET: +- if (sin->sin_port == 0) { +- rpc_createerr.cf_stat = RPC_UNKNOWNADDR; +- return NULL; +- } +- break; + case AF_INET6: +- if (sin6->sin6_port == 0) { ++ if (nfs_get_port(sap) == 0) { + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + break; + default: +- rpc_createerr.cf_stat = RPC_UNKNOWNHOST; ++ rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + +@@ -501,27 +540,21 @@ CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap, + const rpcvers_t version, + struct timeval *timeout) + { +- struct sockaddr_in *sin = (struct sockaddr_in *)sap; +- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap; ++ nfs_clear_rpc_createerr(); + + switch (sap->sa_family) { + case AF_LOCAL: + return nfs_get_localclient(sap, salen, program, + version, timeout); + case AF_INET: +- if (sin->sin_port == 0) { +- rpc_createerr.cf_stat = RPC_UNKNOWNADDR; +- return NULL; +- } +- break; + case AF_INET6: +- if (sin6->sin6_port == 0) { ++ if (nfs_get_port(sap) == 0) { + rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + break; + default: +- rpc_createerr.cf_stat = RPC_UNKNOWNHOST; ++ rpc_createerr.cf_stat = RPC_UNKNOWNADDR; + return NULL; + } + +diff --git a/utils/gssd/svcgssd.c b/utils/gssd/svcgssd.c +index 69d2a69..729b6a6 100644 +--- a/utils/gssd/svcgssd.c ++++ b/utils/gssd/svcgssd.c +@@ -117,10 +117,16 @@ mydaemon(int nochdir, int noclose) + + if (noclose == 0) { + tempfd = open("/dev/null", O_RDWR); +- dup2(tempfd, 0); +- dup2(tempfd, 1); +- dup2(tempfd, 2); +- closeall(3); ++ if (tempfd >= 0) { ++ dup2(tempfd, 0); ++ dup2(tempfd, 1); ++ dup2(tempfd, 2); ++ close(tempfd); ++ } else { ++ printerr(1, "mydaemon: can't open /dev/null: errno %d " ++ "(%s)\n", errno, strerror(errno)); ++ exit(1); ++ } + } + + return; +diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c +index b690e21..9cbe96c 100644 +--- a/utils/idmapd/idmapd.c ++++ b/utils/idmapd/idmapd.c +@@ -978,9 +978,12 @@ mydaemon(int nochdir, int noclose) + dup2(tempfd, 0); + dup2(tempfd, 1); + dup2(tempfd, 2); +- closeall(3); +- } else +- closeall(0); ++ close(tempfd); ++ } else { ++ err(1, "mydaemon: can't open /dev/null: errno %d", ++ errno); ++ exit(1); ++ } + } + + return; +diff --git a/utils/mount/error.c b/utils/mount/error.c +index 5c9d3f2..1b64bd7 100644 +--- a/utils/mount/error.c ++++ b/utils/mount/error.c +@@ -70,9 +70,15 @@ static int rpc_strerror(int spos) + pos = snprintf(tmp, (erreob - tmp), + _("System Error: %s"), + strerror(cf_errno)); +- else +- pos = snprintf(tmp, (erreob - tmp), +- _("RPC Error:%s"), estr); ++ else { ++ if (cf_errno) ++ pos = snprintf(tmp, (erreob - tmp), ++ _("RPC Error:%s; errno = %s"), ++ estr, strerror(cf_errno)); ++ else ++ pos = snprintf(tmp, (erreob - tmp), ++ _("RPC Error:%s"), estr); ++ } + } + return pos; + } +@@ -300,6 +306,8 @@ void umount_error(int err, const char *dev) + #define EDQUOT ENOSPC + #endif + ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) ++ + static struct { + enum nfsstat stat; + int errnum; +@@ -329,19 +337,17 @@ static struct { + #endif + /* Throw in some NFSv3 values for even more fun (HP returns these) */ + { 71, EREMOTE }, +- +- { -1, EIO } + }; + +-char *nfs_strerror(int stat) ++char *nfs_strerror(unsigned int stat) + { +- int i; ++ unsigned int i; + static char buf[256]; + +- for (i = 0; nfs_errtbl[i].stat != -1; i++) { ++ for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) { + if (nfs_errtbl[i].stat == stat) + return strerror(nfs_errtbl[i].errnum); + } +- sprintf(buf, _("unknown nfs status return value: %d"), stat); ++ sprintf(buf, _("unknown nfs status return value: %u"), stat); + return buf; + } +diff --git a/utils/mount/error.h b/utils/mount/error.h +index 7126de5..42b28cf 100644 +--- a/utils/mount/error.h ++++ b/utils/mount/error.h +@@ -24,7 +24,7 @@ + #ifndef _NFS_UTILS_MOUNT_ERROR_H + #define _NFS_UTILS_MOUNT_ERROR_H + +-char *nfs_strerror(int); ++char *nfs_strerror(unsigned int); + + void mount_error(const char *, const char *, int); + void rpc_mount_errors(char *, int, int); +diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c +index 7668167..2775d0b 100644 +--- a/utils/mount/fstab.c ++++ b/utils/mount/fstab.c +@@ -285,7 +285,7 @@ handler (int sig) { + } + + static void +-setlkw_timeout (int sig) { ++setlkw_timeout (__attribute__((unused)) int sig) { + /* nothing, fcntl will fail anyway */ + } + +diff --git a/utils/mount/mount.c b/utils/mount/mount.c +index 06e2804..a668cd9 100644 +--- a/utils/mount/mount.c ++++ b/utils/mount/mount.c +@@ -156,7 +156,7 @@ static void parse_opts(const char *options, int *flags, char **extra_opts); + */ + static void discover_nfs_mount_data_version(void) + { +- int kernel_version = linux_version_code(); ++ unsigned int kernel_version = linux_version_code(); + + if (kernel_version) { + if (kernel_version < MAKE_VERSION(2, 1, 32)) +@@ -417,7 +417,7 @@ static int chk_mountpoint(char *mount_point) + + static int try_mount(char *spec, char *mount_point, int flags, + char *fs_type, char **extra_opts, char *mount_opts, +- int fake, int nomtab, int bg) ++ int fake, int bg) + { + int ret; + +@@ -582,7 +582,7 @@ int main(int argc, char *argv[]) + } + + mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts, +- mount_opts, fake, nomtab, FOREGROUND); ++ mount_opts, fake, FOREGROUND); + if (mnt_err == EX_BG) { + printf(_("%s: backgrounding \"%s\"\n"), + progname, spec); +@@ -600,7 +600,7 @@ int main(int argc, char *argv[]) + + mnt_err = try_mount(spec, mount_point, flags, fs_type, + &extra_opts, mount_opts, fake, +- nomtab, BACKGROUND); ++ BACKGROUND); + if (verbose && mnt_err) + printf(_("%s: giving up \"%s\"\n"), + progname, spec); +diff --git a/utils/mount/network.c b/utils/mount/network.c +index 04a62ab..f6fa5fd 100644 +--- a/utils/mount/network.c ++++ b/utils/mount/network.c +@@ -170,21 +170,6 @@ static const unsigned long probe_mnt3_first[] = { + 0, + }; + +-static void nfs_set_port(struct sockaddr *sap, const unsigned short port) +-{ +- switch (sap->sa_family) { +- case AF_INET: +- ((struct sockaddr_in *)sap)->sin_port = htons(port); +- break; +- case AF_INET6: +- ((struct sockaddr_in6 *)sap)->sin6_port = htons(port); +- break; +- default: +- nfs_error(_("%s: unrecognized address family in %s"), +- progname, __func__); +- } +-} +- + static int nfs_lookup(const char *hostname, const sa_family_t family, + struct sockaddr *sap, socklen_t *salen) + { +@@ -270,7 +255,6 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) + /** + * nfs_string_to_sockaddr - convert string address to sockaddr + * @address: pointer to presentation format address to convert +- * @addrlen: length of presentation address + * @sap: pointer to socket address buffer to fill in + * @salen: IN: length of address buffer + * OUT: length of converted socket address +@@ -284,8 +268,8 @@ int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin) + * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details + * on presenting IPv6 addresses as text strings. + */ +-int nfs_string_to_sockaddr(const char *address, const size_t addrlen, +- struct sockaddr *sap, socklen_t *salen) ++int nfs_string_to_sockaddr(const char *address, struct sockaddr *sap, ++ socklen_t *salen) + { + struct addrinfo *gai_results; + struct addrinfo gai_hint = { +@@ -509,6 +493,21 @@ static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen, + port); + } + ++static void nfs_pp_debug2(const char *str) ++{ ++ if (!verbose) ++ return; ++ ++ if (rpc_createerr.cf_error.re_status == RPC_CANTRECV || ++ rpc_createerr.cf_error.re_status == RPC_CANTSEND) ++ nfs_error(_("%s: portmap query %s%s - %s"), ++ progname, str, clnt_spcreateerror(""), ++ strerror(rpc_createerr.cf_error.re_errno)); ++ else ++ nfs_error(_("%s: portmap query %s%s"), ++ progname, str, clnt_spcreateerror("")); ++} ++ + /* + * Use the portmapper to discover whether or not the service we want is + * available. The lists 'versions' and 'protos' define ordered sequences +@@ -538,9 +537,11 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, + memcpy(saddr, sap, salen); + p_prot = prot ? &prot : protos; + p_vers = vers ? &vers : versions; +- rpc_createerr.cf_stat = 0; + + for (;;) { ++ if (verbose) ++ printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"), ++ progname, prog, *p_vers, *p_prot); + p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot); + if (p_port) { + if (!port || port == p_port) { +@@ -550,28 +551,31 @@ static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen, + if (nfs_rpc_ping(saddr, salen, prog, + *p_vers, *p_prot, NULL)) + goto out_ok; +- } ++ } else ++ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED; + } + if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED && + rpc_createerr.cf_stat != RPC_TIMEDOUT && + rpc_createerr.cf_stat != RPC_CANTRECV && + rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH) +- goto out_bad; ++ break; + + if (!prot) { +- if (*++p_prot) ++ if (*++p_prot) { ++ nfs_pp_debug2("retrying"); + continue; ++ } + p_prot = protos; + } + if (rpc_createerr.cf_stat == RPC_TIMEDOUT || + rpc_createerr.cf_stat == RPC_CANTRECV) +- goto out_bad; ++ break; + + if (vers || !*++p_vers) + break; + } + +-out_bad: ++ nfs_pp_debug2("failed"); + return 0; + + out_ok: +@@ -581,7 +585,7 @@ out_ok: + pmap->pm_prot = *p_prot; + if (!port) + pmap->pm_port = p_port; +- rpc_createerr.cf_stat = 0; ++ nfs_clear_rpc_createerr(); + return 1; + } + +@@ -778,8 +782,8 @@ int start_statd(void) + execl(START_STATD, START_STATD, NULL); + exit(1); + case -1: /* error */ +- nfs_error(_("fork failed: %s"), +- strerror(errno)); ++ nfs_error(_("%s: fork failed: %s"), ++ progname, strerror(errno)); + break; + default: /* parent */ + waitpid(pid, NULL,0); +@@ -844,10 +848,14 @@ int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen, + (xdrproc_t)xdr_dirpath, (caddr_t)argp, + (xdrproc_t)xdr_void, NULL, + timeout); +- if (verbose && res != RPC_SUCCESS) +- nfs_error(_("%s: UMNT call failed: %s"), +- progname, clnt_sperrno(res)); ++ if (res != RPC_SUCCESS) { ++ rpc_createerr.cf_stat = res; ++ CLNT_GETERR(client, &rpc_createerr.cf_error); ++ if (verbose) ++ nfs_error(_("%s: UMNT call failed: %s"), ++ progname, clnt_sperrno(res)); + ++ } + auth_destroy(client->cl_auth); + CLNT_DESTROY(client); + +@@ -1098,7 +1106,7 @@ static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen, + * + * Returns 1 and fills in @buf if successful; otherwise, zero. + */ +-static int nfs_ca_gai(const struct sockaddr *sap, const socklen_t salen, ++static int nfs_ca_gai(const struct sockaddr *sap, + struct sockaddr *buf, socklen_t *buflen) + { + struct addrinfo *gai_results; +@@ -1139,7 +1147,7 @@ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf; + + if (nfs_ca_sockname(sap, salen, buf, buflen) == 0) +- if (nfs_ca_gai(sap, salen, buf, buflen) == 0) ++ if (nfs_ca_gai(sap, buf, buflen) == 0) + goto out_failed; + + /* +@@ -1154,173 +1162,285 @@ int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen, + out_failed: + *buflen = 0; + if (verbose) +- nfs_error(_("%s: failed to construct callback address")); ++ nfs_error(_("%s: failed to construct callback address"), ++ progname); + return 0; +- + } + + /* +- * "nfsprog" is only supported by the legacy mount command. The ++ * "nfsprog" is supported only by the legacy mount command. The + * kernel mount client does not support this option. + * +- * Returns the value set by the nfsprog= option, the value of +- * the RPC NFS program specified in /etc/rpc, or a baked-in +- * default program number, if all fails. ++ * Returns TRUE if @program contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static rpcprog_t nfs_nfs_program(struct mount_options *options) ++static int ++nfs_nfs_program(struct mount_options *options, unsigned long *program) + { + long tmp; + +- if (po_get_numeric(options, "nfsprog", &tmp) == PO_FOUND) +- if (tmp >= 0) +- return tmp; +- return nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl); +-} ++ switch (po_get_numeric(options, "nfsprog", &tmp)) { ++ case PO_NOT_FOUND: ++ break; ++ case PO_FOUND: ++ if (tmp > 0) { ++ *program = tmp; ++ return 1; ++ } ++ case PO_BAD_VALUE: ++ return 0; ++ } + ++ /* ++ * NFS RPC program wasn't specified. The RPC program ++ * cannot be determined via an rpcbind query. ++ */ ++ *program = nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl); ++ return 1; ++} + + /* +- * Returns the RPC version number specified by the given mount +- * options for the NFS service, or zero if all fails. ++ * Returns TRUE if @version contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static rpcvers_t nfs_nfs_version(struct mount_options *options) ++static int ++nfs_nfs_version(struct mount_options *options, unsigned long *version) + { + long tmp; + + switch (po_rightmost(options, nfs_version_opttbl)) { + case 0: /* v2 */ +- return 2; ++ *version = 2; ++ return 1; + case 1: /* v3 */ +- return 3; ++ *version = 3; ++ return 1; + case 2: /* vers */ +- if (po_get_numeric(options, "vers", &tmp) == PO_FOUND) +- if (tmp >= 2 && tmp <= 3) +- return tmp; +- break; ++ switch (po_get_numeric(options, "vers", &tmp)) { ++ case PO_FOUND: ++ if (tmp >= 2 && tmp <= 3) { ++ *version = tmp; ++ return 1; ++ } ++ return 0; ++ case PO_NOT_FOUND: ++ nfs_error(_("%s: option parsing error\n"), ++ progname); ++ case PO_BAD_VALUE: ++ return 0; ++ } + case 3: /* nfsvers */ +- if (po_get_numeric(options, "nfsvers", &tmp) == PO_FOUND) +- if (tmp >= 2 && tmp <= 3) +- return tmp; +- break; ++ switch (po_get_numeric(options, "nfsvers", &tmp)) { ++ case PO_FOUND: ++ if (tmp >= 2 && tmp <= 3) { ++ *version = tmp; ++ return 1; ++ } ++ return 0; ++ case PO_NOT_FOUND: ++ nfs_error(_("%s: option parsing error\n"), ++ progname); ++ case PO_BAD_VALUE: ++ return 0; ++ } + } + +- return 0; ++ /* ++ * NFS version wasn't specified. The pmap version value ++ * will be filled in later by an rpcbind query in this case. ++ */ ++ *version = 0; ++ return 1; + } + + /* +- * Returns the NFS transport protocol specified by the given mount options +- * +- * Returns the IPPROTO_ value specified by the given mount options, or +- * IPPROTO_UDP if all fails. ++ * Returns TRUE if @protocol contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static unsigned short nfs_nfs_protocol(struct mount_options *options) ++static int ++nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol) + { + char *option; + + switch (po_rightmost(options, nfs_transport_opttbl)) { ++ case 0: /* udp */ ++ *protocol = IPPROTO_UDP; ++ return 1; + case 1: /* tcp */ +- return IPPROTO_TCP; ++ *protocol = IPPROTO_TCP; ++ return 1; + case 2: /* proto */ + option = po_get(options, "proto"); + if (option) { +- if (strcmp(option, "tcp") == 0) +- return IPPROTO_TCP; +- if (strcmp(option, "udp") == 0) +- return IPPROTO_UDP; ++ if (strcmp(option, "tcp") == 0) { ++ *protocol = IPPROTO_TCP; ++ return 1; ++ } ++ if (strcmp(option, "udp") == 0) { ++ *protocol = IPPROTO_UDP; ++ return 1; ++ } ++ return 0; + } + } + +- return IPPROTO_UDP; ++ /* ++ * NFS transport protocol wasn't specified. The pmap ++ * protocol value will be filled in later by an rpcbind ++ * query in this case. ++ */ ++ *protocol = 0; ++ return 1; + } + + /* +- * Returns the NFS server's port number specified by the given +- * mount options, or zero if all fails. Zero results in a portmap +- * query to discover the server's mountd service port. +- * +- * port=0 will guarantee an rpcbind request precedes the first +- * NFS RPC so the client can determine the server's port number. ++ * Returns TRUE if @port contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static unsigned short nfs_nfs_port(struct mount_options *options) ++static int ++nfs_nfs_port(struct mount_options *options, unsigned long *port) + { + long tmp; + +- if (po_get_numeric(options, "port", &tmp) == PO_FOUND) +- if (tmp >= 0 && tmp <= 65535) +- return tmp; +- return 0; ++ switch (po_get_numeric(options, "port", &tmp)) { ++ case PO_NOT_FOUND: ++ break; ++ case PO_FOUND: ++ if (tmp >= 1 && tmp <= 65535) { ++ *port = tmp; ++ return 1; ++ } ++ case PO_BAD_VALUE: ++ return 0; ++ } ++ ++ /* ++ * NFS service port wasn't specified. The pmap port value ++ * will be filled in later by an rpcbind query in this case. ++ */ ++ *port = 0; ++ return 1; + } + + /* +- * "mountprog" is only supported by the legacy mount command. The ++ * "mountprog" is supported only by the legacy mount command. The + * kernel mount client does not support this option. + * +- * Returns the value set by the mountprog= option, the value of +- * the RPC mount program specified in /etc/rpc, or a baked-in +- * default program number, if all fails. ++ * Returns TRUE if @program contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static rpcprog_t nfs_mount_program(struct mount_options *options) ++static int ++nfs_mount_program(struct mount_options *options, unsigned long *program) + { + long tmp; + +- if (po_get_numeric(options, "mountprog", &tmp) == PO_FOUND) +- if (tmp >= 0) +- return tmp; +- return nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl); ++ switch (po_get_numeric(options, "mountprog", &tmp)) { ++ case PO_NOT_FOUND: ++ break; ++ case PO_FOUND: ++ if (tmp > 0) { ++ *program = tmp; ++ return 1; ++ } ++ case PO_BAD_VALUE: ++ return 0; ++ } ++ ++ /* ++ * MNT RPC program wasn't specified. The RPC program ++ * cannot be determined via an rpcbind query. ++ */ ++ *program = nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl); ++ return 1; + } + + /* +- * Returns the RPC version number specified by the given mount options, +- * or the version "3" if all fails. ++ * Returns TRUE if @version contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static rpcvers_t nfs_mount_version(struct mount_options *options) ++static int ++nfs_mount_version(struct mount_options *options, unsigned long *version) + { + long tmp; + +- if (po_get_numeric(options, "mountvers", &tmp) == PO_FOUND) +- if (tmp >= 1 && tmp <= 4) +- return tmp; ++ switch (po_get_numeric(options, "mountvers", &tmp)) { ++ case PO_NOT_FOUND: ++ break; ++ case PO_FOUND: ++ if (tmp >= 1 && tmp <= 4) { ++ *version = tmp; ++ return 1; ++ } ++ case PO_BAD_VALUE: ++ return 0; ++ } + +- return nfsvers_to_mnt(nfs_nfs_version(options)); ++ /* ++ * MNT version wasn't specified. The pmap version value ++ * will be filled in later by an rpcbind query in this case. ++ */ ++ *version = 0; ++ return 1; + } + + /* +- * Returns the transport protocol to use for the mount service +- * +- * Returns the IPPROTO_ value specified by the mountproto option, or +- * if that doesn't exist, the IPPROTO_ value specified for NFS +- * itself. ++ * Returns TRUE if @protocol contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static unsigned short nfs_mount_protocol(struct mount_options *options) ++static int ++nfs_mount_protocol(struct mount_options *options, unsigned long *protocol) + { + char *option; + + option = po_get(options, "mountproto"); + if (option) { +- if (strcmp(option, "tcp") == 0) +- return IPPROTO_TCP; +- if (strcmp(option, "udp") == 0) +- return IPPROTO_UDP; ++ if (strcmp(option, "tcp") == 0) { ++ *protocol = IPPROTO_TCP; ++ return 1; ++ } ++ if (strcmp(option, "udp") == 0) { ++ *protocol = IPPROTO_UDP; ++ return 1; ++ } ++ return 0; + } + +- return nfs_nfs_protocol(options); ++ /* ++ * MNT transport protocol wasn't specified. If the NFS ++ * transport protocol was specified, use that; otherwise ++ * set @protocol to zero. The pmap protocol value will ++ * be filled in later by an rpcbind query in this case. ++ */ ++ return nfs_nfs_protocol(options, protocol); + } + + /* +- * Returns the mountd server's port number specified by the given +- * mount options, or zero if all fails. Zero results in a portmap +- * query to discover the server's mountd service port. +- * +- * port=0 will guarantee an rpcbind request precedes the mount +- * RPC so the client can determine the server's port number. ++ * Returns TRUE if @port contains a valid value for this option, ++ * or FALSE if the option was specified with an invalid value. + */ +-static unsigned short nfs_mount_port(struct mount_options *options) ++static int ++nfs_mount_port(struct mount_options *options, unsigned long *port) + { + long tmp; + +- if (po_get_numeric(options, "mountport", &tmp) == PO_FOUND) +- if (tmp >= 0 && tmp <= 65535) +- return tmp; +- return 0; ++ switch (po_get_numeric(options, "mountport", &tmp)) { ++ case PO_NOT_FOUND: ++ break; ++ case PO_FOUND: ++ if (tmp >= 1 && tmp <= 65535) { ++ *port = tmp; ++ return 1; ++ } ++ case PO_BAD_VALUE: ++ return 0; ++ } ++ ++ /* ++ * MNT service port wasn't specified. The pmap port value ++ * will be filled in later by an rpcbind query in this case. ++ */ ++ *port = 0; ++ return 1; + } + + /** +@@ -1329,17 +1449,29 @@ static unsigned short nfs_mount_port(struct mount_options *options) + * @nfs_pmap: OUT: pointer to pmap arguments for NFS server + * @mnt_pmap: OUT: pointer to pmap arguments for mountd server + * ++ * Returns TRUE if the pmap options specified in @options have valid ++ * values; otherwise FALSE is returned. + */ +-void nfs_options2pmap(struct mount_options *options, +- struct pmap *nfs_pmap, struct pmap *mnt_pmap) ++int nfs_options2pmap(struct mount_options *options, ++ struct pmap *nfs_pmap, struct pmap *mnt_pmap) + { +- nfs_pmap->pm_prog = nfs_nfs_program(options); +- nfs_pmap->pm_vers = nfs_nfs_version(options); +- nfs_pmap->pm_prot = nfs_nfs_protocol(options); +- nfs_pmap->pm_port = nfs_nfs_port(options); +- +- mnt_pmap->pm_prog = nfs_mount_program(options); +- mnt_pmap->pm_vers = nfs_mount_version(options); +- mnt_pmap->pm_prot = nfs_mount_protocol(options); +- mnt_pmap->pm_port = nfs_mount_port(options); ++ if (!nfs_nfs_program(options, &nfs_pmap->pm_prog)) ++ return 0; ++ if (!nfs_nfs_version(options, &nfs_pmap->pm_vers)) ++ return 0; ++ if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot)) ++ return 0; ++ if (!nfs_nfs_port(options, &nfs_pmap->pm_port)) ++ return 0; ++ ++ if (!nfs_mount_program(options, &mnt_pmap->pm_prog)) ++ return 0; ++ if (!nfs_mount_version(options, &mnt_pmap->pm_vers)) ++ return 0; ++ if (!nfs_mount_protocol(options, &mnt_pmap->pm_prot)) ++ return 0; ++ if (!nfs_mount_port(options, &mnt_pmap->pm_port)) ++ return 0; ++ ++ return 1; + } +diff --git a/utils/mount/network.h b/utils/mount/network.h +index b3f9bd2..db5134c 100644 +--- a/utils/mount/network.h ++++ b/utils/mount/network.h +@@ -45,8 +45,7 @@ int nfs_probe_bothports(const struct sockaddr *, const socklen_t, + const socklen_t, struct pmap *); + int nfs_gethostbyname(const char *, struct sockaddr_in *); + int nfs_name_to_address(const char *, struct sockaddr *, socklen_t *); +-int nfs_string_to_sockaddr(const char *, const size_t, +- struct sockaddr *, socklen_t *); ++int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *); + int nfs_present_sockaddr(const struct sockaddr *, + const socklen_t, char *, const size_t); + int nfs_callback_address(const struct sockaddr *, const socklen_t, +@@ -57,7 +56,7 @@ int clnt_ping(struct sockaddr_in *, const unsigned long, + + struct mount_options; + +-void nfs_options2pmap(struct mount_options *, ++int nfs_options2pmap(struct mount_options *, + struct pmap *, struct pmap *); + + int start_statd(void); +diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c +index 9b48cc9..f81db14 100644 +--- a/utils/mount/nfsumount.c ++++ b/utils/mount/nfsumount.c +@@ -174,7 +174,10 @@ static int nfs_umount_do_umnt(struct mount_options *options, + socklen_t salen = sizeof(address); + struct pmap nfs_pmap, mnt_pmap; + +- nfs_options2pmap(options, &nfs_pmap, &mnt_pmap); ++ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { ++ nfs_error(_("%s: bad mount options"), progname); ++ return EX_FAIL; ++ } + + *hostname = nfs_umount_hostname(options, *hostname); + if (!*hostname) { +@@ -333,7 +336,7 @@ int nfsumount(int argc, char *argv[]) + char *opt = hasmntopt(&mc->m, "user"); + struct passwd *pw; + char *comma; +- int len; ++ size_t len; + if (!opt) + goto only_root; + if (opt[4] != '=') +diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c +index c0a8e18..c8a58b1 100644 +--- a/utils/mount/parse_dev.c ++++ b/utils/mount/parse_dev.c +@@ -183,8 +183,9 @@ static int nfs_parse_square_bracket(const char *dev, + * with the mount request and failing with a cryptic error message + * later. + */ +-static int nfs_parse_nfs_url(const char *dev, +- char **hostname, char **pathname) ++static int nfs_parse_nfs_url(__attribute__((unused)) const char *dev, ++ __attribute__((unused)) char **hostname, ++ __attribute__((unused)) char **pathname) + { + nfs_error(_("%s: NFS URLs are not supported"), progname); + return 0; +diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c +index ec95b78..a12ace7 100644 +--- a/utils/mount/stropts.c ++++ b/utils/mount/stropts.c +@@ -130,12 +130,14 @@ static int nfs_append_generic_address_option(const struct sockaddr *sap, + { + char address[NI_MAXHOST]; + char new_option[512]; ++ int len; + + if (!nfs_present_sockaddr(sap, salen, address, sizeof(address))) + goto out_err; + +- if (snprintf(new_option, sizeof(new_option), "%s=%s", +- keyword, address) >= sizeof(new_option)) ++ len = snprintf(new_option, sizeof(new_option), "%s=%s", ++ keyword, address); ++ if (len < 0 || (size_t)len >= sizeof(new_option)) + goto out_err; + + if (po_append(options, new_option) != PO_SUCCEEDED) +@@ -283,27 +285,16 @@ static int nfs_validate_options(struct nfsmount_info *mi) + if (!nfs_append_sloppy_option(mi->options)) + return 0; + +- return nfs_append_addr_option(sap, salen, mi->options); +-} ++ if (!nfs_append_addr_option(sap, salen, mi->options)) ++ return 0; + +-/* +- * Distinguish between permanent and temporary errors. +- * +- * Returns 0 if the passed-in error is temporary, thus the +- * mount system call should be retried; returns one if the +- * passed-in error is permanent, thus the mount system call +- * should not be retried. +- */ +-static int nfs_is_permanent_error(int error) +-{ +- switch (error) { +- case ESTALE: +- case ETIMEDOUT: +- case ECONNREFUSED: +- return 0; /* temporary */ +- default: +- return 1; /* permanent */ +- } ++ /* ++ * Update option string to be recorded in /etc/mnttab ++ */ ++ if (po_join(mi->options, mi->extra_opts) == PO_FAILED) ++ return 0; ++ ++ return 1; + } + + /* +@@ -323,16 +314,14 @@ static int nfs_extract_server_addresses(struct mount_options *options, + option = po_get(options, "addr"); + if (option == NULL) + return 0; +- if (!nfs_string_to_sockaddr(option, strlen(option), +- nfs_saddr, nfs_salen)) ++ if (!nfs_string_to_sockaddr(option, nfs_saddr, nfs_salen)) + return 0; + + option = po_get(options, "mountaddr"); + if (option == NULL) { + memcpy(mnt_saddr, nfs_saddr, *nfs_salen); + *mnt_salen = *nfs_salen; +- } else if (!nfs_string_to_sockaddr(option, strlen(option), +- mnt_saddr, mnt_salen)) ++ } else if (!nfs_string_to_sockaddr(option, mnt_saddr, mnt_salen)) + return 0; + + return 1; +@@ -420,206 +409,135 @@ static int nfs_construct_new_options(struct mount_options *options, + * + * To handle version and transport protocol fallback properly, we + * need to parse some of the mount options in order to set up a +- * portmap probe. Mount options that nfs_rewrite_mount_options() ++ * portmap probe. Mount options that nfs_rewrite_pmap_mount_options() + * doesn't recognize are left alone. + * +- * Returns a new group of mount options if successful; otherwise +- * NULL is returned if some failure occurred. ++ * Returns TRUE if rewriting was successful; otherwise ++ * FALSE is returned if some failure occurred. + */ +-static struct mount_options *nfs_rewrite_mount_options(char *str) ++static int ++nfs_rewrite_pmap_mount_options(struct mount_options *options) + { +- struct mount_options *options; + struct sockaddr_storage nfs_address; + struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address; +- socklen_t nfs_salen; ++ socklen_t nfs_salen = sizeof(nfs_address); + struct pmap nfs_pmap; + struct sockaddr_storage mnt_address; + struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address; +- socklen_t mnt_salen; ++ socklen_t mnt_salen = sizeof(mnt_address); + struct pmap mnt_pmap; ++ char *option; + +- options = po_split(str); +- if (!options) { +- errno = EFAULT; +- return NULL; +- } ++ /* ++ * Skip option negotiation for proto=rdma mounts. ++ */ ++ option = po_get(options, "proto"); ++ if (option && strcmp(option, "rdma") == 0) ++ goto out; + ++ /* ++ * Extract just the options needed to contact server. ++ * Bail now if any of these have bad values. ++ */ + if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen, + mnt_saddr, &mnt_salen)) { + errno = EINVAL; +- goto err; ++ return 0; ++ } ++ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) { ++ errno = EINVAL; ++ return 0; + } + +- nfs_options2pmap(options, &nfs_pmap, &mnt_pmap); +- +- /* The kernel NFS client doesn't support changing the RPC program +- * number for these services, so reset these fields before probing +- * the server's ports. */ ++ /* ++ * The kernel NFS client doesn't support changing the RPC ++ * program number for these services, so force the value of ++ * these fields before probing the server's ports. ++ */ + nfs_pmap.pm_prog = NFS_PROGRAM; + mnt_pmap.pm_prog = MOUNTPROG; + ++ /* ++ * If the server's rpcbind service isn't available, we can't ++ * negotiate. Bail now if we can't contact it. ++ */ + if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap, + nfs_saddr, nfs_salen, &nfs_pmap)) { + errno = ESPIPE; +- goto err; ++ return 0; + } + + if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) { + errno = EINVAL; +- goto err; ++ return 0; + } + ++out: + errno = 0; +- return options; +- +-err: +- po_destroy(options); +- return NULL; ++ return 1; + } + + /* + * Do the mount(2) system call. + * +- * Returns 1 if successful, otherwise zero. ++ * Returns TRUE if successful, otherwise FALSE. + * "errno" is set to reflect the individual error. + */ +-static int nfs_sys_mount(const struct nfsmount_info *mi, const char *type, +- const char *options) ++static int nfs_try_mount(struct nfsmount_info *mi) + { ++ char *options = NULL; + int result; + +- result = mount(mi->spec, mi->node, type, +- mi->flags & ~(MS_USER|MS_USERS), options); +- if (verbose && result) { +- int save = errno; +- nfs_error(_("%s: mount(2): %s"), progname, strerror(save)); +- errno = save; +- } +- return !result; +-} +- +-/* +- * Retry an NFS mount that failed because the requested service isn't +- * available on the server. +- * +- * Returns 1 if successful. Otherwise, returns zero. +- * "errno" is set to reflect the individual error. +- * +- * Side effect: If the retry is successful, both 'options' and +- * 'extra_opts' are updated to reflect the mount options that worked. +- * If the retry fails, 'options' and 'extra_opts' are left unchanged. +- */ +-static int nfs_retry_nfs23mount(struct nfsmount_info *mi) +-{ +- struct mount_options *retry_options; +- char *retry_str = NULL; +- char **extra_opts = mi->extra_opts; +- +- retry_options = nfs_rewrite_mount_options(*extra_opts); +- if (!retry_options) +- return 0; +- +- if (po_join(retry_options, &retry_str) == PO_FAILED) { +- po_destroy(retry_options); +- errno = EIO; +- return 0; +- } +- +- if (verbose) +- printf(_("%s: text-based options (retry): '%s'\n"), +- progname, retry_str); +- +- if (!nfs_sys_mount(mi, "nfs", retry_str)) { +- po_destroy(retry_options); +- free(retry_str); +- return 0; ++ if (strncmp(mi->type, "nfs4", 4) != 0) { ++ if (!nfs_rewrite_pmap_mount_options(mi->options)) ++ return 0; + } + +- free(*extra_opts); +- *extra_opts = retry_str; +- po_replace(mi->options, retry_options); +- return 1; +-} +- +-/* +- * Attempt an NFSv2/3 mount via a mount(2) system call. If the kernel +- * claims the requested service isn't supported on the server, probe +- * the server to see what's supported, rewrite the mount options, +- * and retry the request. +- * +- * Returns 1 if successful. Otherwise, returns zero. +- * "errno" is set to reflect the individual error. +- * +- * Side effect: If the retry is successful, both 'options' and +- * 'extra_opts' are updated to reflect the mount options that worked. +- * If the retry fails, 'options' and 'extra_opts' are left unchanged. +- */ +-static int nfs_try_nfs23mount(struct nfsmount_info *mi) +-{ +- char **extra_opts = mi->extra_opts; +- +- if (po_join(mi->options, extra_opts) == PO_FAILED) { ++ if (po_join(mi->options, &options) == PO_FAILED) { + errno = EIO; + return 0; + } + + if (verbose) +- printf(_("%s: text-based options: '%s'\n"), +- progname, *extra_opts); ++ printf(_("%s: trying text-based options '%s'\n"), ++ progname, options); + + if (mi->fake) + return 1; + +- if (nfs_sys_mount(mi, "nfs", *extra_opts)) +- return 1; +- +- /* +- * The kernel returns EOPNOTSUPP if the RPC bind failed, +- * and EPROTONOSUPPORT if the version isn't supported. +- */ +- if (errno != EOPNOTSUPP && errno != EPROTONOSUPPORT) +- return 0; +- +- return nfs_retry_nfs23mount(mi); +-} +- +-/* +- * Attempt an NFS v4 mount via a mount(2) system call. +- * +- * Returns 1 if successful. Otherwise, returns zero. +- * "errno" is set to reflect the individual error. +- */ +-static int nfs_try_nfs4mount(struct nfsmount_info *mi) +-{ +- char **extra_opts = mi->extra_opts; +- +- if (po_join(mi->options, extra_opts) == PO_FAILED) { +- errno = EIO; +- return 0; ++ result = mount(mi->spec, mi->node, mi->type, ++ mi->flags & ~(MS_USER|MS_USERS), options); ++ if (verbose && result) { ++ int save = errno; ++ nfs_error(_("%s: mount(2): %s"), progname, strerror(save)); ++ errno = save; + } +- +- if (verbose) +- printf(_("%s: text-based options: '%s'\n"), +- progname, *extra_opts); +- +- if (mi->fake) +- return 1; +- +- return nfs_sys_mount(mi, "nfs4", *extra_opts); ++ return !result; + } + + /* +- * Perform either an NFSv2/3 mount, or an NFSv4 mount system call. ++ * Distinguish between permanent and temporary errors. + * +- * Returns 1 if successful. Otherwise, returns zero. +- * "errno" is set to reflect the individual error. ++ * Basically, we retry if communication with the server has ++ * failed so far, but fail immediately if there is a local ++ * error (like a bad mount option). ++ * ++ * ESTALE is also a temporary error because some servers ++ * return ESTALE when a share is temporarily offline. ++ * ++ * Returns 1 if we should fail immediately, or 0 if we ++ * should retry. + */ +-static int nfs_try_mount(struct nfsmount_info *mi) ++static int nfs_is_permanent_error(int error) + { +- if (strncmp(mi->type, "nfs4", 4) == 0) +- return nfs_try_nfs4mount(mi); +- else +- return nfs_try_nfs23mount(mi); ++ switch (error) { ++ case ESTALE: ++ case ETIMEDOUT: ++ case ECONNREFUSED: ++ return 0; /* temporary */ ++ default: ++ return 1; /* permanent */ ++ } + } + + /* diff --git a/nfs-utils.spec b/nfs-utils.spec index 4d8dd38..98e0089 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.0 -Release: 6%{?dist} +Release: 7%{?dist} Epoch: 1 # group all 32bit related archs @@ -23,7 +23,7 @@ Patch01: nfs-utils-1.1.0-smnotify-path.patch Patch02: nfs-utils-1.1.0-exp-subtree-warn-off.patch Patch100: nfs-utils-1.2.1-rc1.patch -Patch101: nfs-utils-1.2.1-mydaemon.patch +Patch101: nfs-utils-1.2.1-rc2.patch Patch102: nfs-utils-1.2.0-proots-rel5.patch Group: System Environment/Daemons @@ -247,6 +247,10 @@ fi %attr(4755,root,root) /sbin/umount.nfs4 %changelog +* Wed Jul 15 10:36:20 EDT 2009 +- Added upstream 1.2.1-rc2 patch + - A large number of mount command changes. + * Mon Jul 13 2009 1.2.0-6 - Added NFSD v4 dynamic pseudo root patch which allows NFS v3 exports to be mounted by v4 clients.