#include "nfs_mount.h"
#include "mount_constants.h"
#include "nfsrpc.h"
+#include "parse_opt.h"
#include "network.h"
-/*
- * Earlier versions of glibc's /usr/include/netdb.h exclude these
- * definitions because it was thought they were not part of a stable
- * POSIX standard. However, they are defined by RFC 2553 and 3493
- * and in POSIX 1003.1-2001, so these definitions were added in later
- * versions of netdb.h.
- */
-#ifndef AI_V4MAPPED
-#define AI_V4MAPPED 0x0008 /* IPv4-mapped addresses are acceptable. */
-#endif /* AI_V4MAPPED */
-#ifndef AI_ALL
-#define AI_ALL 0x0010 /* Return both IPv4 and IPv6 addresses. */
-#endif /* AI_ALL */
-#ifndef AI_ADDRCONFIG
-#define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose \
- returned address type. */
-#endif /* AI_ADDRCONFIG */
-
#define PMAP_TIMEOUT (10)
#define CONNECT_TIMEOUT (20)
#define MOUNT_TIMEOUT (30)
NULL,
};
+static const char *nfs_mnt_pgmtbl[] = {
+ "mount",
+ "mountd",
+ NULL,
+};
+
+static const char *nfs_nfs_pgmtbl[] = {
+ "nfs",
+ "nfsprog",
+ NULL,
+};
+
+static const char *nfs_transport_opttbl[] = {
+ "udp",
+ "tcp",
+ "proto",
+ NULL,
+};
+
+static const char *nfs_version_opttbl[] = {
+ "v2",
+ "v3",
+ "vers",
+ "nfsvers",
+ NULL,
+};
+
static const unsigned long nfs_to_mnt[] = {
0,
0,
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__);
- }
-}
-
-/**
- * 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,
- const sa_family_t af_hint,
- struct sockaddr *sap, socklen_t *salen)
+static int nfs_lookup(const char *hostname, const sa_family_t family,
+ struct sockaddr *sap, socklen_t *salen)
{
struct addrinfo *gai_results;
struct addrinfo gai_hint = {
- .ai_family = af_hint,
+#ifdef HAVE_DECL_AI_ADDRCONFIG
.ai_flags = AI_ADDRCONFIG,
+#endif /* HAVE_DECL_AI_ADDRCONFIG */
+ .ai_family = family,
};
socklen_t len = *salen;
int error, ret = 0;
- if (af_hint == AF_INET6)
- gai_hint.ai_flags |= AI_V4MAPPED|AI_ALL;
-
*salen = 0;
error = getaddrinfo(hostname, NULL, &gai_hint, &gai_results);
- if (error) {
+ switch (error) {
+ case 0:
+ break;
+ case EAI_SYSTEM:
nfs_error(_("%s: DNS resolution failed for %s: %s"),
- progname, hostname, (error == EAI_SYSTEM ?
- strerror(errno) : gai_strerror(error)));
+ progname, hostname, strerror(errno));
+ return ret;
+ default:
+ nfs_error(_("%s: DNS resolution failed for %s: %s"),
+ progname, hostname, gai_strerror(error));
return ret;
}
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
- * @saddr: returns an IPv4 address
+ * @sin: returns an IPv4 address
*
* Returns 1 if successful, otherwise zero.
*/
{
socklen_t len = sizeof(*sin);
- return nfs_name_to_address(hostname, AF_INET,
- (struct sockaddr *)sin, &len);
+ return nfs_lookup(hostname, AF_INET, (struct sockaddr *)sin, &len);
}
/**
* 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
* 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 = {
strcat(buf, "unknown host");
}
- fprintf(stderr, _("%s: trying %s prog %ld vers %ld prot %s port %d\n"),
- progname, buf, program, version,
+ fprintf(stderr, _("%s: trying %s prog %lu vers %lu prot %s port %d\n"),
+ progname, buf, (unsigned long)program,
+ (unsigned long)version,
(protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
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
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) {
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:
pmap->pm_prot = *p_prot;
if (!port)
pmap->pm_port = p_port;
- rpc_createerr.cf_stat = 0;
+ nfs_clear_rpc_createerr();
return 1;
}
probe_mnt1_first, probe_udp_only);
}
-/**
- * probe_bothports - discover the RPC endpoints of mountd and NFS server
- * @mnt_server: pointer to address and pmap argument for mountd results
- * @nfs_server: pointer to address and pmap argument for NFS server
+/*
+ * Probe a server's mountd service to determine which versions and
+ * transport protocols are supported. Invoked when the protocol
+ * version is already known for both the NFS and mountd service.
*
- * Returns 1 if successful, otherwise zero if some error occurred.
- * Note that the arguments are both input and output arguments.
+ * Returns 1 and fills in both @pmap structs if the requested service
+ * ports are unambiguous and pingable. Otherwise zero is returned;
+ * rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
+ const socklen_t mnt_salen,
+ struct pmap *mnt_pmap,
+ const struct sockaddr *nfs_saddr,
+ const socklen_t nfs_salen,
+ struct pmap *nfs_pmap)
+{
+ if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap))
+ return 0;
+ return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
+}
+
+/**
+ * nfs_probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_saddr: pointer to socket address of mountd server
+ * @mnt_salen: length of mountd server's address
+ * @mnt_pmap: IN: partially filled-in mountd RPC service tuple;
+ * OUT: fully filled-in mountd RPC service tuple
+ * @nfs_saddr: pointer to socket address of NFS server
+ * @nfs_salen: length of NFS server's address
+ * @nfs_pmap: IN: partially filled-in NFS RPC service tuple;
+ * OUT: fully filled-in NFS RPC service tuple
*
- * A side effect of calling this function is that rpccreateerr is set.
+ * Returns 1 and fills in both @pmap structs if the requested service
+ * ports are unambiguous and pingable. Otherwise zero is returned;
+ * rpccreateerr.cf_stat is set to reflect the nature of the error.
*/
-int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
+int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
+ const socklen_t mnt_salen,
+ struct pmap *mnt_pmap,
+ const struct sockaddr *nfs_saddr,
+ const socklen_t nfs_salen,
+ struct pmap *nfs_pmap)
{
- struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_server->saddr;
- socklen_t nfs_salen = sizeof(nfs_server->saddr);
- struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_server->saddr;
- socklen_t mnt_salen = sizeof(mnt_server->saddr);
- struct pmap *nfs_pmap = &nfs_server->pmap;
- struct pmap *mnt_pmap = &mnt_server->pmap;
struct pmap save_nfs, save_mnt;
const unsigned long *probe_vers;
nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
+
if (nfs_pmap->pm_vers)
- goto version_fixed;
+ return nfs_probe_version_fixed(mnt_saddr, mnt_salen, mnt_pmap,
+ nfs_saddr, nfs_salen, nfs_pmap);
memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
case RPC_PROGNOTREGISTERED:
break;
default:
- goto out_bad;
+ return 0;
}
memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
}
-out_bad:
return 0;
+}
-version_fixed:
- if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap))
- goto out_bad;
- return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
+/**
+ * probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_server: pointer to address and pmap argument for mountd results
+ * @nfs_server: pointer to address and pmap argument for NFS server
+ *
+ * This is the legacy API that takes "clnt_addr_t" for both servers,
+ * but supports only AF_INET addresses.
+ *
+ * Returns 1 and fills in the pmap field in both clnt_addr_t structs
+ * if the requested service ports are unambiguous and pingable.
+ * Otherwise zero is returned; rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ */
+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),
+ &mnt_server->pmap,
+ (struct sockaddr *)&nfs_server->saddr,
+ sizeof(nfs_server->saddr),
+ &nfs_server->pmap);
}
static int nfs_probe_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);
return 0;
}
+/**
+ * nfs_advise_umount - ask the server to remove a share from it's rmtab
+ * @sap: pointer to IP address of server to call
+ * @salen: length of server address
+ * @pmap: partially filled-in mountd RPC service tuple
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason; rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ *
+ * We use a fast timeout since this call is advisory only.
+ */
+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;
+ struct pmap mnt_pmap = *pmap;
+ struct timeval timeout = {
+ .tv_sec = MOUNT_TIMEOUT >> 3,
+ };
+ CLIENT *client;
+ enum clnt_stat res = 0;
+
+ memcpy(saddr, sap, salen);
+ if (nfs_probe_mntport(saddr, salen, &mnt_pmap) == 0) {
+ if (verbose)
+ nfs_error(_("%s: Failed to discover mountd port%s"),
+ progname, clnt_spcreateerror(""));
+ return 0;
+ }
+ nfs_set_port(saddr, mnt_pmap.pm_port);
+
+ client = nfs_get_priv_rpcclient(saddr, salen, mnt_pmap.pm_prot,
+ mnt_pmap.pm_prog, mnt_pmap.pm_vers,
+ &timeout);
+ if (client == NULL) {
+ if (verbose)
+ nfs_error(_("%s: Failed to create RPC client%s"),
+ progname, clnt_spcreateerror(""));
+ return 0;
+ }
+
+ client->cl_auth = authunix_create_default();
+
+ res = CLNT_CALL(client, MOUNTPROC_UMNT,
+ (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+ (xdrproc_t)xdr_void, NULL,
+ timeout);
+ 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);
+
+ if (res != RPC_SUCCESS)
+ return 0;
+ return 1;
+}
+
/**
* nfs_call_umount - ask the server to remove a share from it's rmtab
* @mnt_server: address of RPC MNT program server
*
* 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;
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;
/*
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 supported only by the legacy mount command. The
+ * kernel mount client does not support this option.
+ *
+ * Returns TRUE if @program contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_program(struct mount_options *options, unsigned long *program)
+{
+ long tmp;
+
+ 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 TRUE if @version contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_version(struct mount_options *options, unsigned long *version)
+{
+ long tmp;
+
+ switch (po_rightmost(options, nfs_version_opttbl)) {
+ case 0: /* v2 */
+ *version = 2;
+ return 1;
+ case 1: /* v3 */
+ *version = 3;
+ return 1;
+ case 2: /* vers */
+ 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 */
+ 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;
+ }
+ }
+
+ /*
+ * 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 TRUE if @protocol contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+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 */
+ *protocol = IPPROTO_TCP;
+ return 1;
+ case 2: /* proto */
+ option = po_get(options, "proto");
+ if (option) {
+ if (strcmp(option, "tcp") == 0) {
+ *protocol = IPPROTO_TCP;
+ return 1;
+ }
+ if (strcmp(option, "udp") == 0) {
+ *protocol = IPPROTO_UDP;
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ /*
+ * 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 TRUE if @port contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_port(struct mount_options *options, unsigned long *port)
+{
+ long tmp;
+
+ 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 supported only by the legacy mount command. The
+ * kernel mount client does not support this option.
+ *
+ * Returns TRUE if @program contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_program(struct mount_options *options, unsigned long *program)
+{
+ long tmp;
+
+ 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 TRUE if @version contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_version(struct mount_options *options, unsigned long *version)
+{
+ long 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;
+ }
+
+ /*
+ * 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 TRUE if @protocol contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+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) {
+ *protocol = IPPROTO_TCP;
+ return 1;
+ }
+ if (strcmp(option, "udp") == 0) {
+ *protocol = IPPROTO_UDP;
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * 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 TRUE if @port contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_port(struct mount_options *options, unsigned long *port)
+{
+ long tmp;
+
+ 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;
+}
+
+/**
+ * nfs_options2pmap - set up pmap structs based on mount options
+ * @options: pointer to mount 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.
+ */
+int nfs_options2pmap(struct mount_options *options,
+ struct pmap *nfs_pmap, struct pmap *mnt_pmap)
+{
+ 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;
}