#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
+#include <sys/stat.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
+#include "sockaddr.h"
#include "xcommon.h"
#include "mount.h"
#include "nls.h"
#include "parse_opt.h"
#include "network.h"
#include "conffile.h"
+#include "nfslib.h"
#define PMAP_TIMEOUT (10)
#define CONNECT_TIMEOUT (20)
#define MOUNT_TIMEOUT (30)
-#if SIZEOF_SOCKLEN_T - 0 == 0
-#define socklen_t unsigned int
-#endif
+#define SAFE_SOCKADDR(x) (struct sockaddr *)(char *)(x)
extern int nfs_mount_data_version;
extern char *progname;
static const char *nfs_transport_opttbl[] = {
"udp",
"tcp",
+ "rdma",
"proto",
NULL,
};
{
struct addrinfo *gai_results;
struct addrinfo gai_hint = {
-#ifdef HAVE_DECL_AI_ADDRCONFIG
- .ai_flags = AI_ADDRCONFIG,
-#endif /* HAVE_DECL_AI_ADDRCONFIG */
.ai_family = family,
};
socklen_t len = *salen;
return ret;
}
-/**
- * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
- * @hostname: pointer to C string containing DNS hostname to resolve
- * @sap: pointer to buffer to fill with socket address
- * @len: IN: size of buffer to fill; OUT: size of socket address
- *
- * Returns 1 and places a socket address at @sap if successful;
- * otherwise zero.
- */
-int nfs_name_to_address(const char *hostname,
- struct sockaddr *sap, socklen_t *salen)
-{
-#ifdef IPV6_SUPPORTED
- return nfs_lookup(hostname, AF_UNSPEC, sap, salen);
-#else /* !IPV6_SUPPORTED */
- return nfs_lookup(hostname, AF_INET, sap, salen);
-#endif /* !IPV6_SUPPORTED */
-}
-
/**
* nfs_gethostbyname - resolve a hostname to an IPv4 address
* @hostname: pointer to a C string containing a DNS hostname
* OUT: length of converted socket address
*
* Convert a presentation format address string to a socket address.
- * Similar to nfs_name_to_address(), but the DNS query is squelched,
- * and won't make any noise if the getaddrinfo() call fails.
+ * Similar to nfs_lookup(), but the DNS query is squelched, and it
+ * won't make any noise if the getaddrinfo() call fails.
*
* Returns 1 and fills in @sap and @salen if successful; otherwise zero.
*
if (bindresvport(so, &laddr) < 0)
goto err_bindresvport;
} else {
- cc = bind(so, (struct sockaddr *)&laddr, namelen);
+ cc = bind(so, SAFE_SOCKADDR(&laddr), namelen);
if (cc < 0)
goto err_bind;
}
if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
- cc = connect_to(so, (struct sockaddr *)saddr, namelen,
+ cc = connect_to(so, SAFE_SOCKADDR(saddr), namelen,
timeout);
if (cc < 0)
goto err_connect;
struct pmap *pmap, const unsigned long *versions,
const unsigned int *protos)
{
- struct sockaddr_storage address;
- struct sockaddr *saddr = (struct sockaddr *)&address;
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
const unsigned long prog = pmap->pm_prog, *p_vers;
const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
const u_short port = (u_short) pmap->pm_port;
*/
int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
{
- return nfs_probe_bothports((struct sockaddr *)&mnt_server->saddr,
- sizeof(mnt_server->saddr),
+ struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr);
+ struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr);
+
+ return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr),
&mnt_server->pmap,
- (struct sockaddr *)&nfs_server->saddr,
- sizeof(nfs_server->saddr),
+ nfs_addr, sizeof(nfs_server->saddr),
&nfs_server->pmap);
}
};
rpcprog_t program = nfs_getrpcbyname(NSMPROG, nfs_ns_pgmtbl);
- return nfs_getport_ping((struct sockaddr *)&addr, sizeof(addr),
+ return nfs_getport_ping(SAFE_SOCKADDR(&addr), sizeof(addr),
program, (rpcvers_t)1, IPPROTO_UDP);
}
int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
const struct pmap *pmap, const dirpath *argp)
{
- struct sockaddr_storage address;
- struct sockaddr *saddr = (struct sockaddr *)&address;
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
struct pmap mnt_pmap = *pmap;
struct timeval timeout = {
.tv_sec = MOUNT_TIMEOUT >> 3,
return 0;
}
- client->cl_auth = authunix_create_default();
+ client->cl_auth = nfs_authsys_create();
+ if (client->cl_auth == NULL) {
+ if (verbose)
+ nfs_error(_("%s: Failed to create RPC auth handle"),
+ progname);
+ CLNT_DESTROY(client);
+ return 0;
+ }
res = CLNT_CALL(client, MOUNTPROC_UMNT,
(xdrproc_t)xdr_dirpath, (caddr_t)argp,
*/
int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
{
- struct sockaddr *sap = (struct sockaddr *)&mnt_server->saddr;
+ struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr);
socklen_t salen = sizeof(mnt_server->saddr);
struct pmap *pmap = &mnt_server->pmap;
CLIENT *clnt;
}
if (clnt) {
/* try to mount hostname:dirname */
- clnt->cl_auth = authunix_create_default();
- return clnt;
+ clnt->cl_auth = nfs_authsys_create();
+ if (clnt->cl_auth)
+ return clnt;
+ CLNT_DESTROY(clnt);
}
return NULL;
}
struct sockaddr_in *caddr)
{
CLIENT *clnt = NULL;
- int sock, stat;
+ int sock, status;
static char clnt_res;
struct sockaddr dissolve;
- rpc_createerr.cf_stat = stat = 0;
+ rpc_createerr.cf_stat = status = 0;
sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
if (sock == RPC_ANYSOCK) {
if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
return 0;
}
memset(&clnt_res, 0, sizeof(clnt_res));
- stat = clnt_call(clnt, NULLPROC,
+ status = clnt_call(clnt, NULLPROC,
(xdrproc_t)xdr_void, (caddr_t)NULL,
(xdrproc_t)xdr_void, (caddr_t)&clnt_res,
TIMEOUT);
- if (stat) {
+ if (status) {
clnt_geterr(clnt, &rpc_createerr.cf_error);
- rpc_createerr.cf_stat = stat;
+ rpc_createerr.cf_stat = status;
}
clnt_destroy(clnt);
close(sock);
- if (stat == RPC_SUCCESS)
+ if (status == RPC_SUCCESS)
return 1;
else
return 0;
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_ANY_INIT,
};
- int sock;
+ int sock, result = 0;
sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0)
switch (sap->sa_family) {
case AF_INET:
- if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
- close(sock);
- return 0;
- }
+ if (bind(sock, SAFE_SOCKADDR(&sin), sizeof(sin)) < 0)
+ goto out;
break;
case AF_INET6:
- if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
- close(sock);
- return 0;
- }
+ if (bind(sock, SAFE_SOCKADDR(&sin6), sizeof(sin6)) < 0)
+ goto out;
break;
default:
errno = EAFNOSUPPORT;
- return 0;
+ goto out;
}
- if (connect(sock, sap, salen) < 0) {
- close(sock);
- return 0;
- }
+ if (connect(sock, sap, salen) < 0)
+ goto out;
+
+ result = !getsockname(sock, buf, buflen);
- return !getsockname(sock, buf, buflen);
+out:
+ close(sock);
+ return result;
}
/*
return 1;
}
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'nfsprog=' option"),
+ progname);
return 0;
}
}
return 0;
case PO_NOT_FOUND:
- nfs_error(_("%s: option parsing error\n"),
+ nfs_error(_("%s: parsing error on 'vers=' option\n"),
progname);
+ return 0;
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'vers=' option"),
+ progname);
return 0;
}
case 4: /* nfsvers */
}
return 0;
case PO_NOT_FOUND:
- nfs_error(_("%s: option parsing error\n"),
+ nfs_error(_("%s: parsing error on 'nfsvers=' option\n"),
progname);
+ return 0;
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'nfsvers=' option"),
+ progname);
return 0;
}
}
/*
* Returns TRUE if @protocol contains a valid value for this option,
- * or FALSE if the option was specified with an invalid value.
+ * or FALSE if the option was specified with an invalid value. On
+ * error, errno is set.
*/
int
nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
case 1: /* tcp */
*protocol = IPPROTO_TCP;
return 1;
- case 2: /* proto */
+ case 2: /* rdma */
+ *protocol = NFSPROTO_RDMA;
+ return 1;
+ case 3: /* proto */
option = po_get(options, "proto");
- if (option != NULL)
- return nfs_get_proto(option, &family, protocol);
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &family, protocol)) {
+ errno = EPROTONOSUPPORT;
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ return 0;
+ }
+ return 1;
+ }
}
/*
case PO_NOT_FOUND:
break;
case PO_FOUND:
- if (tmp >= 1 && tmp <= 65535) {
+ if (tmp >= 0 && tmp <= 65535) {
*port = tmp;
return 1;
}
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'port=' option"),
+ progname);
return 0;
}
return 1;
}
+#ifdef IPV6_SUPPORTED
+sa_family_t config_default_family = AF_UNSPEC;
+
+static int
+nfs_verify_family(sa_family_t UNUSED(family))
+{
+ return 1;
+}
+#else /* IPV6_SUPPORTED */
+sa_family_t config_default_family = AF_INET;
+
+static int
+nfs_verify_family(sa_family_t family)
+{
+ if (family != AF_INET)
+ return 0;
+
+ return 1;
+}
+#endif /* IPV6_SUPPORTED */
+
+/*
+ * Returns TRUE and fills in @family if a valid NFS protocol option
+ * is found, or FALSE if the option was specified with an invalid value
+ * or if the protocol family isn't supported. On error, errno is set.
+ */
+int nfs_nfs_proto_family(struct mount_options *options,
+ sa_family_t *family)
+{
+ unsigned long protocol;
+ char *option;
+ sa_family_t tmp_family = config_default_family;
+
+ switch (po_rightmost(options, nfs_transport_opttbl)) {
+ case 0: /* udp */
+ case 1: /* tcp */
+ case 2: /* rdma */
+ /* for compatibility; these are always AF_INET */
+ *family = AF_INET;
+ return 1;
+ case 3: /* proto */
+ option = po_get(options, "proto");
+ if (option != NULL &&
+ !nfs_get_proto(option, &tmp_family, &protocol)) {
+
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ errno = EPROTONOSUPPORT;
+ return 0;
+ }
+ }
+
+ if (!nfs_verify_family(tmp_family))
+ goto out_err;
+ *family = tmp_family;
+ return 1;
+out_err:
+ errno = EAFNOSUPPORT;
+ return 0;
+}
+
/*
* "mountprog" is supported only by the legacy mount command. The
* kernel mount client does not support this option.
return 1;
}
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountprog=' option"),
+ progname);
return 0;
}
return 1;
}
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountvers=' option"),
+ progname);
return 0;
}
/*
* Returns TRUE if @protocol contains a valid value for this option,
- * or FALSE if the option was specified with an invalid value.
+ * or FALSE if the option was specified with an invalid value. On
+ * error, errno is set.
*/
static int
nfs_mount_protocol(struct mount_options *options, unsigned long *protocol)
char *option;
option = po_get(options, "mountproto");
- if (option != NULL)
- return nfs_get_proto(option, &family, protocol);
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &family, protocol)) {
+ errno = EPROTONOSUPPORT;
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ return 0;
+ }
+ return 1;
+ }
/*
* MNT transport protocol wasn't specified. If the NFS
* 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);
+ if (!nfs_nfs_protocol(options, protocol))
+ return 0;
+ if (*protocol == NFSPROTO_RDMA)
+ *protocol = IPPROTO_TCP;
+ return 1;
}
/*
case PO_NOT_FOUND:
break;
case PO_FOUND:
- if (tmp >= 1 && tmp <= 65535) {
+ if (tmp >= 0 && tmp <= 65535) {
*port = tmp;
return 1;
}
case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountport=' option"),
+ progname);
return 0;
}
return 1;
}
+/*
+ * Returns TRUE and fills in @family if a valid MNT protocol option
+ * is found, or FALSE if the option was specified with an invalid value
+ * or if the protocol family isn't supported. On error, errno is set.
+ */
+int nfs_mount_proto_family(struct mount_options *options,
+ sa_family_t *family)
+{
+ unsigned long protocol;
+ char *option;
+ sa_family_t tmp_family = config_default_family;
+
+ option = po_get(options, "mountproto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &tmp_family, &protocol)) {
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ errno = EPROTONOSUPPORT;
+ goto out_err;
+ }
+ if (!nfs_verify_family(tmp_family))
+ goto out_err;
+ *family = tmp_family;
+ return 1;
+ }
+
+ /*
+ * MNT transport protocol wasn't specified. If the NFS
+ * transport protocol was specified, derive the family
+ * from that; otherwise, return the default family for
+ * NFS.
+ */
+ return nfs_nfs_proto_family(options, family);
+out_err:
+ errno = EAFNOSUPPORT;
+ return 0;
+}
+
/**
* nfs_options2pmap - set up pmap structs based on mount options
* @options: pointer to mount options
return 1;
}
+
+/*
+ * Discover mount server's hostname/address by examining mount options
+ *
+ * Returns a pointer to a string that the caller must free, on
+ * success; otherwise NULL is returned.
+ */
+static char *nfs_umount_hostname(struct mount_options *options,
+ char *hostname)
+{
+ char *option;
+
+ option = po_get(options, "mountaddr");
+ if (option)
+ goto out;
+ option = po_get(options, "mounthost");
+ if (option)
+ goto out;
+ option = po_get(options, "addr");
+ if (option)
+ goto out;
+
+ return hostname;
+
+out:
+ free(hostname);
+ return strdup(option);
+}
+
+
+/*
+ * Returns EX_SUCCESS if mount options and device name have been
+ * parsed successfully; otherwise EX_FAIL.
+ */
+int nfs_umount_do_umnt(struct mount_options *options,
+ char **hostname, char **dirname)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *sap = &address.sa;
+ socklen_t salen = sizeof(address);
+ struct pmap nfs_pmap, mnt_pmap;
+ sa_family_t family;
+
+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap))
+ return EX_FAIL;
+
+ /* Skip UMNT call for vers=4 mounts */
+ if (nfs_pmap.pm_vers == 4)
+ return EX_SUCCESS;
+
+ *hostname = nfs_umount_hostname(options, *hostname);
+ if (!*hostname) {
+ nfs_error(_("%s: out of memory"), progname);
+ return EX_FAIL;
+ }
+
+ if (!nfs_mount_proto_family(options, &family))
+ return 0;
+ if (!nfs_lookup(*hostname, family, sap, &salen))
+ /* nfs_lookup reports any errors */
+ return EX_FAIL;
+
+ if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
+ /* nfs_advise_umount reports any errors */
+ return EX_FAIL;
+
+ return EX_SUCCESS;
+}