0b66620759
- mount: segmentation faults on UDP mounts (bz 485448)
822 lines
23 KiB
Diff
822 lines
23 KiB
Diff
text-based mount command: make po_rightmost() work for N options
|
|
|
|
Sometimes we need to choose the rightmost option among multiple
|
|
different mount options. For example, we want to find the rightmost
|
|
of "proto," "tcp," and "udp". Or, the rightmost of "vers," "nfsvers,"
|
|
"v2," and "v3".
|
|
|
|
Update po_rightmost() to choose among N options instead of just two.
|
|
|
|
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
|
|
---
|
|
|
|
utils/mount/parse_opt.c | 28 ++++++++++++++++------------
|
|
utils/mount/parse_opt.h | 9 ++-------
|
|
utils/mount/stropts.c | 28 +++++++++++++++++++++++-----
|
|
3 files changed, 41 insertions(+), 24 deletions(-)
|
|
|
|
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
|
|
index f61d0dd..4934508 100644
|
|
--- a/utils/mount/parse_opt.c
|
|
+++ b/utils/mount/parse_opt.c
|
|
@@ -421,34 +421,38 @@ po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *va
|
|
#endif /* HAVE_STRTOL */
|
|
|
|
/**
|
|
- * po_rightmost - determine the relative position of two options
|
|
+ * po_rightmost - determine the relative position of several options
|
|
* @options: pointer to mount options
|
|
- * @key1: pointer to a C string containing an option keyword
|
|
- * @key2: pointer to a C string containing another option keyword
|
|
+ * @keys: pointer to an array of C strings containing option keywords
|
|
+ *
|
|
+ * This function can be used to determine which of several similar
|
|
+ * options will be the one to take effect.
|
|
*
|
|
* The kernel parses the mount option string from left to right.
|
|
* If an option is specified more than once (for example, "intr"
|
|
* and "nointr", the rightmost option is the last to be parsed,
|
|
* and it therefore takes precedence over previous similar options.
|
|
*
|
|
- * This function can be used to determine which of two similar
|
|
- * options will be the one to take effect.
|
|
+ * This can also distinguish among multiple synonymous options, such
|
|
+ * as "proto=," "udp" and "tcp."
|
|
+ *
|
|
+ * Returns the index into @keys of the option that is rightmost.
|
|
+ * If none of the options are present, returns zero.
|
|
*/
|
|
-po_rightmost_t po_rightmost(struct mount_options *options,
|
|
- char *key1, char *key2)
|
|
+unsigned int po_rightmost(struct mount_options *options, const char *keys[])
|
|
{
|
|
struct mount_option *option;
|
|
+ unsigned int i;
|
|
|
|
if (options) {
|
|
for (option = options->tail; option; option = option->prev) {
|
|
- if (key2 && strcmp(option->keyword, key2) == 0)
|
|
- return PO_KEY2_RIGHTMOST;
|
|
- if (key1 && strcmp(option->keyword, key1) == 0)
|
|
- return PO_KEY1_RIGHTMOST;
|
|
+ for (i = 0; keys[i] != NULL; i++)
|
|
+ if (strcmp(option->keyword, keys[i]) == 0)
|
|
+ return i;
|
|
}
|
|
}
|
|
|
|
- return PO_NEITHER_FOUND;
|
|
+ return 0;
|
|
}
|
|
|
|
/**
|
|
diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h
|
|
index 199630f..e132b1c 100644
|
|
--- a/utils/mount/parse_opt.h
|
|
+++ b/utils/mount/parse_opt.h
|
|
@@ -35,12 +35,6 @@ typedef enum {
|
|
PO_BAD_VALUE = 2,
|
|
} po_found_t;
|
|
|
|
-typedef enum {
|
|
- PO_KEY1_RIGHTMOST = -1,
|
|
- PO_NEITHER_FOUND = 0,
|
|
- PO_KEY2_RIGHTMOST = 1,
|
|
-} po_rightmost_t;
|
|
-
|
|
struct mount_options;
|
|
|
|
struct mount_options * po_split(char *);
|
|
@@ -53,7 +47,8 @@ po_found_t po_contains(struct mount_options *, char *);
|
|
char * po_get(struct mount_options *, char *);
|
|
po_found_t po_get_numeric(struct mount_options *,
|
|
char *, long *);
|
|
-po_rightmost_t po_rightmost(struct mount_options *, char *, char *);
|
|
+unsigned int po_rightmost(struct mount_options *,
|
|
+ const char *keys[]);
|
|
po_found_t po_remove_all(struct mount_options *, char *);
|
|
void po_destroy(struct mount_options *);
|
|
|
|
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
|
|
index 43791e6..bd127ab 100644
|
|
--- a/utils/mount/stropts.c
|
|
+++ b/utils/mount/stropts.c
|
|
@@ -224,9 +224,15 @@ static int nfs_fix_mounthost_option(const sa_family_t family,
|
|
* Returns zero if the "lock" option is in effect, but statd
|
|
* can't be started. Otherwise, returns 1.
|
|
*/
|
|
+static const char *nfs_lock_opttbl[] = {
|
|
+ "nolock",
|
|
+ "lock",
|
|
+ NULL,
|
|
+};
|
|
+
|
|
static int nfs_verify_lock_option(struct mount_options *options)
|
|
{
|
|
- if (po_rightmost(options, "nolock", "lock") == PO_KEY1_RIGHTMOST)
|
|
+ if (po_rightmost(options, nfs_lock_opttbl) == 1)
|
|
return 1;
|
|
|
|
if (!start_statd()) {
|
|
@@ -316,6 +322,12 @@ static int nfs_is_permanent_error(int error)
|
|
* Returns a new group of mount options if successful; otherwise
|
|
* NULL is returned if some failure occurred.
|
|
*/
|
|
+static const char *nfs_transport_opttbl[] = {
|
|
+ "udp",
|
|
+ "tcp",
|
|
+ NULL,
|
|
+};
|
|
+
|
|
static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
{
|
|
struct mount_options *options;
|
|
@@ -395,12 +407,12 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
po_remove_all(options, "proto");
|
|
}
|
|
}
|
|
- p = po_rightmost(options, "tcp", "udp");
|
|
+ p = po_rightmost(options, nfs_transport_opttbl);
|
|
switch (p) {
|
|
- case PO_KEY2_RIGHTMOST:
|
|
+ case 1:
|
|
nfs_server.pmap.pm_prot = IPPROTO_UDP;
|
|
break;
|
|
- case PO_KEY1_RIGHTMOST:
|
|
+ case 2:
|
|
nfs_server.pmap.pm_prot = IPPROTO_TCP;
|
|
break;
|
|
}
|
|
@@ -722,12 +734,18 @@ static int nfsmount_bg(struct nfsmount_info *mi)
|
|
*
|
|
* Returns a valid mount command exit code.
|
|
*/
|
|
+static const char *nfs_background_opttbl[] = {
|
|
+ "bg",
|
|
+ "fg",
|
|
+ NULL,
|
|
+};
|
|
+
|
|
static int nfsmount_start(struct nfsmount_info *mi)
|
|
{
|
|
if (!nfs_validate_options(mi))
|
|
return EX_FAIL;
|
|
|
|
- if (po_rightmost(mi->options, "bg", "fg") == PO_KEY1_RIGHTMOST)
|
|
+ if (po_rightmost(mi->options, nfs_background_opttbl) == 1)
|
|
return nfsmount_bg(mi);
|
|
else
|
|
return nfsmount_fg(mi);
|
|
|
|
text-based mount command: Function to stuff "struct pmap" from mount options
|
|
|
|
Both the text-based mount.nfs command and the umount.nfs command need
|
|
to fill in a pmap structure based on string mount options. Introduce
|
|
a shared function that can do this.
|
|
|
|
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
|
|
---
|
|
|
|
utils/mount/network.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++++
|
|
utils/mount/network.h | 5 +
|
|
2 files changed, 219 insertions(+), 0 deletions(-)
|
|
|
|
diff --git a/utils/mount/network.c b/utils/mount/network.c
|
|
index d262e94..91a005c 100644
|
|
--- a/utils/mount/network.c
|
|
+++ b/utils/mount/network.c
|
|
@@ -48,6 +48,7 @@
|
|
#include "nfs_mount.h"
|
|
#include "mount_constants.h"
|
|
#include "nfsrpc.h"
|
|
+#include "parse_opt.h"
|
|
#include "network.h"
|
|
|
|
#define PMAP_TIMEOUT (10)
|
|
@@ -67,6 +68,33 @@ static const char *nfs_ns_pgmtbl[] = {
|
|
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,
|
|
@@ -1111,3 +1139,189 @@ out_failed:
|
|
return 0;
|
|
|
|
}
|
|
+
|
|
+/*
|
|
+ * "nfsprog" is only supported 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.
|
|
+ */
|
|
+static rpcprog_t nfs_nfs_program(struct mount_options *options)
|
|
+{
|
|
+ long tmp;
|
|
+
|
|
+ if (po_get_numeric(options, "nfsprog", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 0)
|
|
+ return tmp;
|
|
+ return nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl);
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Returns the RPC version number specified by the given mount
|
|
+ * options for the NFS service, or zero if all fails.
|
|
+ */
|
|
+static rpcvers_t nfs_nfs_version(struct mount_options *options)
|
|
+{
|
|
+ long tmp;
|
|
+
|
|
+ switch (po_rightmost(options, nfs_version_opttbl)) {
|
|
+ case 1: /* v2 */
|
|
+ return 2;
|
|
+ case 2: /* v3 */
|
|
+ return 3;
|
|
+ case 3: /* vers */
|
|
+ if (po_get_numeric(options, "vers", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 2 && tmp <= 3)
|
|
+ return tmp;
|
|
+ break;
|
|
+ case 4: /* nfsvers */
|
|
+ if (po_get_numeric(options, "nfsvers", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 2 && tmp <= 3)
|
|
+ return tmp;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+static unsigned short nfs_nfs_protocol(struct mount_options *options)
|
|
+{
|
|
+ char *option;
|
|
+
|
|
+ switch (po_rightmost(options, nfs_transport_opttbl)) {
|
|
+ case 1: /* udp */
|
|
+ return IPPROTO_UDP;
|
|
+ case 2: /* tcp */
|
|
+ return IPPROTO_TCP;
|
|
+ case 3: /* proto */
|
|
+ option = po_get(options, "proto");
|
|
+ if (option) {
|
|
+ if (strcmp(option, "tcp") == 0)
|
|
+ return IPPROTO_TCP;
|
|
+ if (strcmp(option, "udp") == 0)
|
|
+ return IPPROTO_UDP;
|
|
+ }
|
|
+ }
|
|
+ return IPPROTO_UDP;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+static unsigned short nfs_nfs_port(struct mount_options *options)
|
|
+{
|
|
+ long tmp;
|
|
+
|
|
+ if (po_get_numeric(options, "port", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 0 && tmp <= 65535)
|
|
+ return tmp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * "mountprog" is only supported 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.
|
|
+ */
|
|
+static rpcprog_t nfs_mount_program(struct mount_options *options)
|
|
+{
|
|
+ long tmp;
|
|
+
|
|
+ if (po_get_numeric(options, "mountprog", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 0)
|
|
+ return tmp;
|
|
+ return nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Returns the RPC version number specified by the given mount options,
|
|
+ * or the version "3" if all fails.
|
|
+ */
|
|
+static rpcvers_t nfs_mount_version(struct mount_options *options)
|
|
+{
|
|
+ long tmp;
|
|
+
|
|
+ if (po_get_numeric(options, "mountvers", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 1 && tmp <= 4)
|
|
+ return tmp;
|
|
+
|
|
+ return nfsvers_to_mnt(nfs_nfs_version(options));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+static unsigned short nfs_mount_protocol(struct mount_options *options)
|
|
+{
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ return nfs_nfs_version(options);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * 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.
|
|
+ */
|
|
+static unsigned short nfs_mount_port(struct mount_options *options)
|
|
+{
|
|
+ long tmp;
|
|
+
|
|
+ if (po_get_numeric(options, "mountport", &tmp) == PO_FOUND)
|
|
+ if (tmp >= 0 && tmp <= 65535)
|
|
+ return tmp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * 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
|
|
+ *
|
|
+ */
|
|
+void 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);
|
|
+}
|
|
diff --git a/utils/mount/network.h b/utils/mount/network.h
|
|
index 075093d..25060ab 100644
|
|
--- a/utils/mount/network.h
|
|
+++ b/utils/mount/network.h
|
|
@@ -57,6 +57,11 @@ int clnt_ping(struct sockaddr_in *, const unsigned long,
|
|
const unsigned long, const unsigned int,
|
|
struct sockaddr_in *);
|
|
|
|
+struct mount_options;
|
|
+
|
|
+void nfs_options2pmap(struct mount_options *,
|
|
+ struct pmap *, struct pmap *);
|
|
+
|
|
int start_statd(void);
|
|
|
|
unsigned long nfsvers_to_mnt(const unsigned long);
|
|
|
|
text-based mount options: Use new pmap stuffer when rewriting mount options
|
|
|
|
all nfs_options2pmap() in nfs_rewrite_mount_options() instead of
|
|
open-coding the logic to convert mount options to a pmap struct.
|
|
The new nfs_options2pmap() function is more careful about avoiding
|
|
invalid mount option values, and handles multiply-specified transport
|
|
protocol options correctly.
|
|
|
|
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
|
|
---
|
|
|
|
utils/mount/stropts.c | 68 ++++---------------------------------------------
|
|
1 files changed, 5 insertions(+), 63 deletions(-)
|
|
|
|
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
|
|
index bd127ab..99be0f3 100644
|
|
--- a/utils/mount/stropts.c
|
|
+++ b/utils/mount/stropts.c
|
|
@@ -322,19 +322,12 @@ static int nfs_is_permanent_error(int error)
|
|
* Returns a new group of mount options if successful; otherwise
|
|
* NULL is returned if some failure occurred.
|
|
*/
|
|
-static const char *nfs_transport_opttbl[] = {
|
|
- "udp",
|
|
- "tcp",
|
|
- NULL,
|
|
-};
|
|
-
|
|
static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
{
|
|
struct mount_options *options;
|
|
char *option, new_option[64];
|
|
clnt_addr_t mnt_server = { };
|
|
clnt_addr_t nfs_server = { };
|
|
- int p;
|
|
|
|
options = po_split(str);
|
|
if (!options) {
|
|
@@ -360,64 +353,13 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
memcpy(&mnt_server.saddr, &nfs_server.saddr,
|
|
sizeof(mnt_server.saddr));
|
|
|
|
- option = po_get(options, "mountport");
|
|
- if (option)
|
|
- mnt_server.pmap.pm_port = atoi(option);
|
|
- mnt_server.pmap.pm_prog = MOUNTPROG;
|
|
- option = po_get(options, "mountvers");
|
|
- if (option)
|
|
- mnt_server.pmap.pm_vers = atoi(option);
|
|
- option = po_get(options, "mountproto");
|
|
- if (option) {
|
|
- if (strcmp(option, "tcp") == 0) {
|
|
- mnt_server.pmap.pm_prot = IPPROTO_TCP;
|
|
- po_remove_all(options, "mountproto");
|
|
- }
|
|
- if (strcmp(option, "udp") == 0) {
|
|
- mnt_server.pmap.pm_prot = IPPROTO_UDP;
|
|
- po_remove_all(options, "mountproto");
|
|
- }
|
|
- }
|
|
+ nfs_options2pmap(options, &nfs_server.pmap, &mnt_server.pmap);
|
|
|
|
- option = po_get(options, "port");
|
|
- if (option) {
|
|
- nfs_server.pmap.pm_port = atoi(option);
|
|
- po_remove_all(options, "port");
|
|
- }
|
|
+ /* 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. */
|
|
nfs_server.pmap.pm_prog = NFS_PROGRAM;
|
|
-
|
|
- option = po_get(options, "nfsvers");
|
|
- if (option) {
|
|
- nfs_server.pmap.pm_vers = atoi(option);
|
|
- po_remove_all(options, "nfsvers");
|
|
- }
|
|
- option = po_get(options, "vers");
|
|
- if (option) {
|
|
- nfs_server.pmap.pm_vers = atoi(option);
|
|
- po_remove_all(options, "vers");
|
|
- }
|
|
- option = po_get(options, "proto");
|
|
- if (option) {
|
|
- if (strcmp(option, "tcp") == 0) {
|
|
- nfs_server.pmap.pm_prot = IPPROTO_TCP;
|
|
- po_remove_all(options, "proto");
|
|
- }
|
|
- if (strcmp(option, "udp") == 0) {
|
|
- nfs_server.pmap.pm_prot = IPPROTO_UDP;
|
|
- po_remove_all(options, "proto");
|
|
- }
|
|
- }
|
|
- p = po_rightmost(options, nfs_transport_opttbl);
|
|
- switch (p) {
|
|
- case 1:
|
|
- nfs_server.pmap.pm_prot = IPPROTO_UDP;
|
|
- break;
|
|
- case 2:
|
|
- nfs_server.pmap.pm_prot = IPPROTO_TCP;
|
|
- break;
|
|
- }
|
|
- po_remove_all(options, "tcp");
|
|
- po_remove_all(options, "udp");
|
|
+ mnt_server.pmap.pm_prog = MOUNTPROG;
|
|
|
|
if (!probe_bothports(&mnt_server, &nfs_server)) {
|
|
errno = ESPIPE;
|
|
|
|
text-based mount command: fix mount option rewriting logic
|
|
|
|
Fix a bunch of corner cases in the text-based mount option rewriting logic.
|
|
|
|
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
|
|
---
|
|
|
|
utils/mount/stropts.c | 113 ++++++++++++++++++++++++++++++++++---------------
|
|
1 files changed, 79 insertions(+), 34 deletions(-)
|
|
|
|
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
|
|
index 99be0f3..319be71 100644
|
|
--- a/utils/mount/stropts.c
|
|
+++ b/utils/mount/stropts.c
|
|
@@ -309,6 +309,81 @@ static int nfs_is_permanent_error(int error)
|
|
}
|
|
}
|
|
|
|
+static int nfs_construct_new_options(struct mount_options *options,
|
|
+ struct pmap *nfs_pmap,
|
|
+ struct pmap *mnt_pmap)
|
|
+{
|
|
+ char new_option[64];
|
|
+
|
|
+ po_remove_all(options, "nfsprog");
|
|
+ po_remove_all(options, "mountprog");
|
|
+
|
|
+ po_remove_all(options, "v2");
|
|
+ po_remove_all(options, "v3");
|
|
+ po_remove_all(options, "vers");
|
|
+ po_remove_all(options, "nfsvers");
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "vers=%lu", nfs_pmap->pm_vers);
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+
|
|
+ po_remove_all(options, "proto");
|
|
+ po_remove_all(options, "udp");
|
|
+ po_remove_all(options, "tcp");
|
|
+ switch (nfs_pmap->pm_prot) {
|
|
+ case IPPROTO_TCP:
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "proto=tcp");
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+ break;
|
|
+ case IPPROTO_UDP:
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "proto=udp");
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ po_remove_all(options, "port");
|
|
+ if (nfs_pmap->pm_port != NFS_PORT) {
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "port=%lu", nfs_pmap->pm_port);
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ po_remove_all(options, "mountvers");
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "mountvers=%lu", mnt_pmap->pm_vers);
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+
|
|
+ po_remove_all(options, "mountproto");
|
|
+ switch (mnt_pmap->pm_prot) {
|
|
+ case IPPROTO_TCP:
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "mountproto=tcp");
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+ break;
|
|
+ case IPPROTO_UDP:
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "mountproto=udp");
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ po_remove_all(options, "mountport");
|
|
+ snprintf(new_option, sizeof(new_option) - 1,
|
|
+ "mountport=%lu", mnt_pmap->pm_port);
|
|
+ if (po_append(options, new_option) == PO_FAILED)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
/*
|
|
* Reconstruct the mount option string based on a portmapper probe
|
|
* of the server. Returns one if the server's portmapper returned
|
|
@@ -325,7 +400,7 @@ static int nfs_is_permanent_error(int error)
|
|
static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
{
|
|
struct mount_options *options;
|
|
- char *option, new_option[64];
|
|
+ char *option;
|
|
clnt_addr_t mnt_server = { };
|
|
clnt_addr_t nfs_server = { };
|
|
|
|
@@ -366,42 +441,12 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
goto err;
|
|
}
|
|
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "nfsvers=%lu", nfs_server.pmap.pm_vers);
|
|
- if (po_append(options, new_option) == PO_FAILED)
|
|
+ if (!nfs_construct_new_options(options,
|
|
+ &nfs_server.pmap, &mnt_server.pmap)) {
|
|
+ errno = EINVAL;
|
|
goto err;
|
|
-
|
|
- if (nfs_server.pmap.pm_prot == IPPROTO_TCP)
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "proto=tcp");
|
|
- else
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "proto=udp");
|
|
- if (po_append(options, new_option) == PO_FAILED)
|
|
- goto err;
|
|
-
|
|
- if (nfs_server.pmap.pm_port != NFS_PORT) {
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "port=%lu", nfs_server.pmap.pm_port);
|
|
- if (po_append(options, new_option) == PO_FAILED)
|
|
- goto err;
|
|
-
|
|
}
|
|
|
|
- if (mnt_server.pmap.pm_prot == IPPROTO_TCP)
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "mountproto=tcp");
|
|
- else
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "mountproto=udp");
|
|
- if (po_append(options, new_option) == PO_FAILED)
|
|
- goto err;
|
|
-
|
|
- snprintf(new_option, sizeof(new_option) - 1,
|
|
- "mountport=%lu", mnt_server.pmap.pm_port);
|
|
- if (po_append(options, new_option) == PO_FAILED)
|
|
- goto err;
|
|
-
|
|
errno = 0;
|
|
return options;
|
|
|
|
|
|
text-based mount command: support AF_INET6 in rewrite_mount_options()
|
|
|
|
Now that we have an AF_INET6-capable probe_bothports(), we can support
|
|
AF_INET6 when rewriting text-based NFS mount options. This should be
|
|
adequate to support NFS transport protocol and version negotiation with
|
|
AF_INET6 NFS servers.
|
|
|
|
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
|
|
---
|
|
|
|
utils/mount/stropts.c | 74 ++++++++++++++++++++++++++++++++-----------------
|
|
1 files changed, 49 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
|
|
index 319be71..6d44bb7 100644
|
|
--- a/utils/mount/stropts.c
|
|
+++ b/utils/mount/stropts.c
|
|
@@ -309,6 +309,37 @@ static int nfs_is_permanent_error(int error)
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * Get NFS/mnt server addresses from mount options
|
|
+ *
|
|
+ * Returns 1 and fills in @nfs_saddr, @nfs_salen, @mnt_saddr, and @mnt_salen
|
|
+ * if all goes well; otherwise zero.
|
|
+ */
|
|
+static int nfs_extract_server_addresses(struct mount_options *options,
|
|
+ struct sockaddr *nfs_saddr,
|
|
+ socklen_t *nfs_salen,
|
|
+ struct sockaddr *mnt_saddr,
|
|
+ socklen_t *mnt_salen)
|
|
+{
|
|
+ char *option;
|
|
+
|
|
+ option = po_get(options, "addr");
|
|
+ if (option == NULL)
|
|
+ return 0;
|
|
+ if (!nfs_string_to_sockaddr(option, strlen(option),
|
|
+ nfs_saddr, nfs_salen))
|
|
+ return 0;
|
|
+
|
|
+ option = po_get(options, "mountaddr");
|
|
+ if (option == NULL)
|
|
+ memcpy(mnt_saddr, nfs_saddr, *nfs_salen);
|
|
+ else if (!nfs_string_to_sockaddr(option, strlen(option),
|
|
+ mnt_saddr, mnt_salen))
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
static int nfs_construct_new_options(struct mount_options *options,
|
|
struct pmap *nfs_pmap,
|
|
struct pmap *mnt_pmap)
|
|
@@ -400,9 +431,14 @@ static int nfs_construct_new_options(struct mount_options *options,
|
|
static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
{
|
|
struct mount_options *options;
|
|
- char *option;
|
|
- clnt_addr_t mnt_server = { };
|
|
- clnt_addr_t nfs_server = { };
|
|
+ struct sockaddr_storage nfs_address;
|
|
+ struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address;
|
|
+ socklen_t nfs_salen;
|
|
+ struct pmap nfs_pmap;
|
|
+ struct sockaddr_storage mnt_address;
|
|
+ struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address;
|
|
+ socklen_t mnt_salen;
|
|
+ struct pmap mnt_pmap;
|
|
|
|
options = po_split(str);
|
|
if (!options) {
|
|
@@ -410,39 +446,27 @@ static struct mount_options *nfs_rewrite_mount_options(char *str)
|
|
return NULL;
|
|
}
|
|
|
|
- errno = EINVAL;
|
|
- option = po_get(options, "addr");
|
|
- if (option) {
|
|
- nfs_server.saddr.sin_family = AF_INET;
|
|
- if (!inet_aton((const char *)option, &nfs_server.saddr.sin_addr))
|
|
- goto err;
|
|
- } else
|
|
+ if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen,
|
|
+ mnt_saddr, &mnt_salen)) {
|
|
+ errno = EINVAL;
|
|
goto err;
|
|
+ }
|
|
|
|
- option = po_get(options, "mountaddr");
|
|
- if (option) {
|
|
- mnt_server.saddr.sin_family = AF_INET;
|
|
- if (!inet_aton((const char *)option, &mnt_server.saddr.sin_addr))
|
|
- goto err;
|
|
- } else
|
|
- memcpy(&mnt_server.saddr, &nfs_server.saddr,
|
|
- sizeof(mnt_server.saddr));
|
|
-
|
|
- nfs_options2pmap(options, &nfs_server.pmap, &mnt_server.pmap);
|
|
+ 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. */
|
|
- nfs_server.pmap.pm_prog = NFS_PROGRAM;
|
|
- mnt_server.pmap.pm_prog = MOUNTPROG;
|
|
+ nfs_pmap.pm_prog = NFS_PROGRAM;
|
|
+ mnt_pmap.pm_prog = MOUNTPROG;
|
|
|
|
- if (!probe_bothports(&mnt_server, &nfs_server)) {
|
|
+ if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
|
|
+ nfs_saddr, nfs_salen, &nfs_pmap)) {
|
|
errno = ESPIPE;
|
|
goto err;
|
|
}
|
|
|
|
- if (!nfs_construct_new_options(options,
|
|
- &nfs_server.pmap, &mnt_server.pmap)) {
|
|
+ if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) {
|
|
errno = EINVAL;
|
|
goto err;
|
|
}
|
|
|