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 */ + } } /*