Blame SOURCES/nfs-utils-1.3.0-mount-default-v42.patch

e19a30
diff --git a/configure.ac b/configure.ac
e19a30
index 56f7f3e..802fd58 100644
e19a30
--- a/configure.ac
e19a30
+++ b/configure.ac
e19a30
@@ -172,10 +172,12 @@ AC_ARG_ENABLE(ipv6,
e19a30
 if test "$enable_mount" = yes; then
e19a30
 	AC_ARG_ENABLE(mountconfig,
e19a30
 	[AC_HELP_STRING([--enable-mountconfig],
e19a30
-                        [enable mount to use a configuration file])],
e19a30
+        [enable mount to use a configuration file @<:@default=yes@:>@])],
e19a30
 	mountconfig=$enableval,
e19a30
-	mountconfig=no)
e19a30
-	if test "$enable_mountconfig" = yes; then
e19a30
+	mountconfig=yes)
e19a30
+	if test "$enable_mountconfig" = no; then
e19a30
+		enable_mountconfig=
e19a30
+	else
e19a30
 		AC_DEFINE(MOUNT_CONFIG, 1, 
e19a30
 			[Define this if you want mount to read a configuration file])
e19a30
 		AC_ARG_WITH(mountfile,
e19a30
@@ -187,8 +189,6 @@ if test "$enable_mount" = yes; then
e19a30
 		AC_SUBST(mountfile)
e19a30
 		AC_DEFINE_UNQUOTED(MOUNTOPTS_CONFFILE, "$mountfile", 
e19a30
 			[This defines the location of the NFS mount configuration file])
e19a30
-	else
e19a30
-		enable_mountconfig=
e19a30
 	fi
e19a30
 	AC_SUBST(enable_mountconfig)
e19a30
 	AM_CONDITIONAL(MOUNT_CONFIG, [test "$enable_mountconfig" = "yes"])
e19a30
diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c
e19a30
index 39d3741..0a4cc04 100644
e19a30
--- a/utils/mount/configfile.c
e19a30
+++ b/utils/mount/configfile.c
e19a30
@@ -228,37 +228,8 @@ void free_all(void)
e19a30
 		free(entry);
e19a30
 	}
e19a30
 }
e19a30
-static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL};
e19a30
-static int 
e19a30
-check_vers(char *mopt, char *field)
e19a30
-{
e19a30
-	int i, found=0;
e19a30
-
e19a30
-	/*
e19a30
-	 * First check to see if the config setting is one 
e19a30
-	 * of the many version settings
e19a30
-	 */
e19a30
-	for (i=0; versions[i]; i++) { 
e19a30
-		if (strcasestr(field, versions[i]) != NULL) {
e19a30
-			found++;
e19a30
-			break;
e19a30
-		}
e19a30
-	}
e19a30
-	if (!found)
e19a30
-		return 0;
e19a30
-	/*
e19a30
-	 * It appears the version is being set, now see
e19a30
-	 * if the version appears on the command 
e19a30
-	 */
e19a30
-	for (i=0; versions[i]; i++)  {
e19a30
-		if (strcasestr(mopt, versions[i]) != NULL)
e19a30
-			return 1;
e19a30
-	}
e19a30
-
e19a30
-	return 0;
e19a30
-}
e19a30
 
e19a30
-unsigned long config_default_vers;
e19a30
+struct nfs_version config_default_vers;
e19a30
 unsigned long config_default_proto;
e19a30
 extern sa_family_t config_default_family;
e19a30
 
e19a30
@@ -331,11 +302,6 @@ conf_parse_mntopts(char *section, char *arg, char *opts)
e19a30
 		snprintf(buf, BUFSIZ, "%s=", node->field);
e19a30
 		if (opts && strcasestr(opts, buf) != NULL)
e19a30
 			continue;
e19a30
-		/* 
e19a30
-		 * Protocol verions can be set in a number of ways
e19a30
-		 */
e19a30
-		if (opts && check_vers(opts, node->field))
e19a30
-			continue;
e19a30
 
e19a30
 		if (lookup_entry(node->field) != NULL)
e19a30
 			continue;
e19a30
diff --git a/utils/mount/network.c b/utils/mount/network.c
e19a30
index 515249b..088caa1 100644
e19a30
--- a/utils/mount/network.c
e19a30
+++ b/utils/mount/network.c
e19a30
@@ -92,9 +92,6 @@ static const char *nfs_version_opttbl[] = {
e19a30
 	"v4",
e19a30
 	"vers",
e19a30
 	"nfsvers",
e19a30
-	"v4.0",
e19a30
-	"v4.1",
e19a30
-	"v4.2",
e19a30
 	NULL,
e19a30
 };
e19a30
 
e19a30
@@ -1249,71 +1246,69 @@ nfs_nfs_program(struct mount_options *options, unsigned long *program)
e19a30
  * or FALSE if the option was specified with an invalid value.
e19a30
  */
e19a30
 int
e19a30
-nfs_nfs_version(struct mount_options *options, unsigned long *version)
e19a30
+nfs_nfs_version(struct mount_options *options, struct nfs_version *version)
e19a30
 {
e19a30
-	long tmp;
e19a30
+	char *version_key, *version_val, *cptr;
e19a30
+	int i, found = 0;
e19a30
 
e19a30
-	switch (po_rightmost(options, nfs_version_opttbl)) {
e19a30
-	case 0:	/* v2 */
e19a30
-		*version = 2;
e19a30
-		return 1;
e19a30
-	case 1: /* v3 */
e19a30
-		*version = 3;
e19a30
-		return 1;
e19a30
-	case 2: /* v4 */
e19a30
-		*version = 4;
e19a30
-		return 1;
e19a30
-	case 3:	/* vers */
e19a30
-		switch (po_get_numeric(options, "vers", &tmp)) {
e19a30
-		case PO_FOUND:
e19a30
-			if (tmp >= 2 && tmp <= 4) {
e19a30
-				*version = tmp;
e19a30
-				return 1;
e19a30
-			}
e19a30
-			nfs_error(_("%s: parsing error on 'vers=' option\n"),
e19a30
-					progname);
e19a30
-			return 0;
e19a30
-		case PO_NOT_FOUND:
e19a30
-			nfs_error(_("%s: parsing error on 'vers=' option\n"),
e19a30
-					progname);
e19a30
-			return 0;
e19a30
-		case PO_BAD_VALUE:
e19a30
-			nfs_error(_("%s: invalid value for 'vers=' option"),
e19a30
-					progname);
e19a30
-			return 0;
e19a30
-		}
e19a30
-	case 4: /* nfsvers */
e19a30
-		switch (po_get_numeric(options, "nfsvers", &tmp)) {
e19a30
-		case PO_FOUND:
e19a30
-			if (tmp >= 2 && tmp <= 4) {
e19a30
-				*version = tmp;
e19a30
-				return 1;
e19a30
-			}
e19a30
-			nfs_error(_("%s: parsing error on 'nfsvers=' option\n"),
e19a30
-					progname);
e19a30
-			return 0;
e19a30
-		case PO_NOT_FOUND:
e19a30
-			nfs_error(_("%s: parsing error on 'nfsvers=' option\n"),
e19a30
-					progname);
e19a30
-			return 0;
e19a30
-		case PO_BAD_VALUE:
e19a30
-			nfs_error(_("%s: invalid value for 'nfsvers=' option"),
e19a30
-					progname);
e19a30
-			return 0;
e19a30
+	version->v_mode = V_DEFAULT;
e19a30
+
e19a30
+	for (i = 0; nfs_version_opttbl[i]; i++) {
e19a30
+		if (po_contains_prefix(options, nfs_version_opttbl[i],
e19a30
+								&version_key) == PO_FOUND) {
e19a30
+			found++;
e19a30
+			break;
e19a30
 		}
e19a30
-	case 5: /* v4.0 */
e19a30
-	case 6: /* v4.1 */
e19a30
-	case 7: /* v4.2 */
e19a30
-		*version = 4;
e19a30
+	}
e19a30
+
e19a30
+	if (!found)
e19a30
 		return 1;
e19a30
+
e19a30
+	if (i <= 2 ) {
e19a30
+		/* v2, v3, v4 */
e19a30
+		version_val = version_key + 1;
e19a30
+		version->v_mode = V_SPECIFIC;
e19a30
+	} else if (i > 2 ) {
e19a30
+		/* vers=, nfsvers= */
e19a30
+		version_val = po_get(options, version_key);
e19a30
 	}
e19a30
 
e19a30
-	/*
e19a30
-	 * NFS version wasn't specified.  The pmap version value
e19a30
-	 * will be filled in later by an rpcbind query in this case.
e19a30
-	 */
e19a30
-	*version = 0;
e19a30
+	if (!version_val)
e19a30
+		goto ret_error;
e19a30
+
e19a30
+	if (!(version->major = strtol(version_val, &cptr, 10)))
e19a30
+		goto ret_error;
e19a30
+
e19a30
+	if (version->major < 4)
e19a30
+		version->v_mode = V_SPECIFIC;
e19a30
+
e19a30
+	if (*cptr == '.') {
e19a30
+		version_val = ++cptr;
e19a30
+		if (!(version->minor = strtol(version_val, &cptr, 10)) && cptr == version_val)
e19a30
+			goto ret_error;
e19a30
+		version->v_mode = V_SPECIFIC;
e19a30
+	} else if (version->major > 3 && *cptr == '\0')
e19a30
+		version->v_mode = V_GENERAL;
e19a30
+
e19a30
+	if (*cptr != '\0')
e19a30
+		goto ret_error;
e19a30
+
e19a30
 	return 1;
e19a30
+
e19a30
+ret_error:
e19a30
+	if (i <= 2 ) {
e19a30
+		nfs_error(_("%s: parsing error on 'v' option"),
e19a30
+			progname);
e19a30
+	} else if (i == 3 ) {
e19a30
+		nfs_error(_("%s: parsing error on 'vers=' option"),
e19a30
+			progname);
e19a30
+	} else if (i == 4) {
e19a30
+		nfs_error(_("%s: parsing error on 'nfsvers=' option"),
e19a30
+			progname);
e19a30
+	}
e19a30
+	version->v_mode = V_PARSE_ERR;
e19a30
+	errno = EINVAL;
e19a30
+	return 0;
e19a30
 }
e19a30
 
e19a30
 /*
e19a30
@@ -1632,10 +1627,13 @@ out_err:
e19a30
 int nfs_options2pmap(struct mount_options *options,
e19a30
 		     struct pmap *nfs_pmap, struct pmap *mnt_pmap)
e19a30
 {
e19a30
+	struct nfs_version version;
e19a30
+
e19a30
 	if (!nfs_nfs_program(options, &nfs_pmap->pm_prog))
e19a30
 		return 0;
e19a30
-	if (!nfs_nfs_version(options, &nfs_pmap->pm_vers))
e19a30
+	if (!nfs_nfs_version(options, &version))
e19a30
 		return 0;
e19a30
+	nfs_pmap->pm_vers = version.major;
e19a30
 	if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot))
e19a30
 		return 0;
e19a30
 	if (!nfs_nfs_port(options, &nfs_pmap->pm_port))
e19a30
diff --git a/utils/mount/network.h b/utils/mount/network.h
e19a30
index d7636d7..9cc5dec 100644
e19a30
--- a/utils/mount/network.h
e19a30
+++ b/utils/mount/network.h
e19a30
@@ -57,9 +57,22 @@ int clnt_ping(struct sockaddr_in *, const unsigned long,
e19a30
 
e19a30
 struct mount_options;
e19a30
 
e19a30
+enum {
e19a30
+	V_DEFAULT = 0,
e19a30
+	V_GENERAL,
e19a30
+	V_SPECIFIC,
e19a30
+	V_PARSE_ERR,
e19a30
+};
e19a30
+
e19a30
+struct nfs_version {
e19a30
+	unsigned long major;
e19a30
+	unsigned long minor;
e19a30
+	int v_mode;
e19a30
+};
e19a30
+
e19a30
 int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family);
e19a30
 int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family);
e19a30
-int nfs_nfs_version(struct mount_options *options, unsigned long *version);
e19a30
+int nfs_nfs_version(struct mount_options *options, struct nfs_version *version);
e19a30
 int  nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol);
e19a30
 
e19a30
 int nfs_options2pmap(struct mount_options *,
e19a30
diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
e19a30
index 3538d88..de284f2 100644
e19a30
--- a/utils/mount/nfsumount.c
e19a30
+++ b/utils/mount/nfsumount.c
e19a30
@@ -179,10 +179,10 @@ static int nfs_umount_is_vers4(const struct mntentchn *mc)
e19a30
 
e19a30
 		options = po_split(pmc->m.mnt_opts);
e19a30
 		if (options != NULL) {
e19a30
-			unsigned long version;
e19a30
+			struct nfs_version version;
e19a30
 			int rc = nfs_nfs_version(options, &version);
e19a30
 			po_destroy(options);
e19a30
-			if (rc && version == 4)
e19a30
+			if (rc && version.major == 4)
e19a30
 				goto out_nfs4;
e19a30
 		}
e19a30
 
e19a30
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
e19a30
index 75a0daa..7ba61c4 100644
e19a30
--- a/utils/mount/parse_opt.c
e19a30
+++ b/utils/mount/parse_opt.c
e19a30
@@ -391,7 +391,7 @@ po_return_t po_append(struct mount_options *options, char *str)
e19a30
 }
e19a30
 
e19a30
 /**
e19a30
- * po_contains - check for presense of an option in a group
e19a30
+ * po_contains - check for presence of an option in a group
e19a30
  * @options: pointer to mount options
e19a30
  * @keyword: pointer to a C string containing option keyword for which to search
e19a30
  *
e19a30
@@ -410,6 +410,30 @@ po_found_t po_contains(struct mount_options *options, char *keyword)
e19a30
 }
e19a30
 
e19a30
 /**
e19a30
+ * po_contains_prefix - check for presence of an option matching a prefix
e19a30
+ * @options: pointer to mount options
e19a30
+ * @prefix: pointer to prefix to match against a keyword
e19a30
+ * @keyword: pointer to a C string containing the option keyword if found
e19a30
+ *
e19a30
+ * On success, *keyword contains the pointer of the matching option's keyword.
e19a30
+ */
e19a30
+po_found_t po_contains_prefix(struct mount_options *options,
e19a30
+								const char *prefix, char **keyword)
e19a30
+{
e19a30
+	struct mount_option *option;
e19a30
+
e19a30
+	if (options && prefix) {
e19a30
+		for (option = options->head; option; option = option->next)
e19a30
+			if (strncmp(option->keyword, prefix, strlen(prefix)) == 0) {
e19a30
+				*keyword = option->keyword;
e19a30
+				return PO_FOUND;
e19a30
+			}
e19a30
+	}
e19a30
+
e19a30
+	return PO_NOT_FOUND;
e19a30
+}
e19a30
+
e19a30
+/**
e19a30
  * po_get - return the value of the rightmost instance of an option
e19a30
  * @options: pointer to mount options
e19a30
  * @keyword: pointer to a C string containing option keyword for which to search
e19a30
diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h
e19a30
index 5037207..0745e0f 100644
e19a30
--- a/utils/mount/parse_opt.h
e19a30
+++ b/utils/mount/parse_opt.h
e19a30
@@ -45,6 +45,8 @@ po_return_t		po_join(struct mount_options *, char **);
e19a30
 
e19a30
 po_return_t		po_append(struct mount_options *, char *);
e19a30
 po_found_t		po_contains(struct mount_options *, char *);
e19a30
+po_found_t		po_contains_prefix(struct mount_options *options,
e19a30
+						const char *prefix, char **keyword);
e19a30
 char *			po_get(struct mount_options *, char *);
e19a30
 po_found_t		po_get_numeric(struct mount_options *,
e19a30
 					char *, long *);
e19a30
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
e19a30
index 5d80ed7..207a476 100644
e19a30
--- a/utils/mount/stropts.c
e19a30
+++ b/utils/mount/stropts.c
e19a30
@@ -88,30 +88,50 @@ struct nfsmount_info {
e19a30
 	struct mount_options	*options;	/* parsed mount options */
e19a30
 	char			**extra_opts;	/* string for /etc/mtab */
e19a30
 
e19a30
-	unsigned long		version;	/* NFS version */
e19a30
+	struct nfs_version	version;	/* NFS version */
e19a30
 	int			flags,		/* MS_ flags */
e19a30
 				fake,		/* actually do the mount? */
e19a30
 				child;		/* forked bg child? */
e19a30
 };
e19a30
 
e19a30
-#ifdef MOUNT_CONFIG
e19a30
-static void nfs_default_version(struct nfsmount_info *mi);
e19a30
 
e19a30
 static void nfs_default_version(struct nfsmount_info *mi)
e19a30
 {
e19a30
-	extern unsigned long config_default_vers;
e19a30
+#ifdef MOUNT_CONFIG
e19a30
+	extern struct nfs_version config_default_vers;
e19a30
 	/*
e19a30
 	 * Use the default value set in the config file when
e19a30
 	 * the version has not been explicitly set.
e19a30
 	 */
e19a30
-	if (mi->version == 0 && config_default_vers) {
e19a30
-		if (config_default_vers < 4)
e19a30
-			mi->version = config_default_vers;
e19a30
+	if (config_default_vers.v_mode == V_PARSE_ERR) {
e19a30
+		mi->version.v_mode = V_PARSE_ERR;
e19a30
+		return;
e19a30
 	}
e19a30
-}
e19a30
-#else
e19a30
-inline void nfs_default_version(__attribute__ ((unused)) struct nfsmount_info *mi) {}
e19a30
+
e19a30
+	if (mi->version.v_mode == V_GENERAL &&
e19a30
+		config_default_vers.v_mode == V_DEFAULT) {
e19a30
+		mi->version.v_mode = V_SPECIFIC;
e19a30
+		return;
e19a30
+	}
e19a30
+
e19a30
+	if (mi->version.v_mode == V_DEFAULT &&
e19a30
+		config_default_vers.v_mode != V_DEFAULT) {
e19a30
+		mi->version.major = config_default_vers.major;
e19a30
+		mi->version.minor = config_default_vers.minor;
e19a30
+		return;
e19a30
+	}
e19a30
+
e19a30
+	if (mi->version.v_mode == V_GENERAL &&
e19a30
+		config_default_vers.v_mode != V_DEFAULT) {
e19a30
+		if (mi->version.major == config_default_vers.major)
e19a30
+			mi->version.minor = config_default_vers.minor;
e19a30
+		return;
e19a30
+	}
e19a30
+
e19a30
 #endif /* MOUNT_CONFIG */
e19a30
+	mi->version.major = 4;
e19a30
+	mi->version.minor = 2;
e19a30
+}
e19a30
 
e19a30
 /*
e19a30
  * Obtain a retry timeout value based on the value of the "retry=" option.
e19a30
@@ -300,7 +320,7 @@ static int nfs_set_version(struct nfsmount_info *mi)
e19a30
 		return 0;
e19a30
 
e19a30
 	if (strncmp(mi->type, "nfs4", 4) == 0)
e19a30
-		mi->version = 4;
e19a30
+		mi->version.major = 4;
e19a30
 
e19a30
 	/*
e19a30
 	 * Before 2.6.32, the kernel NFS client didn't
e19a30
@@ -308,28 +328,44 @@ static int nfs_set_version(struct nfsmount_info *mi)
e19a30
 	 * 4 cannot be included when autonegotiating
e19a30
 	 * while running on those kernels.
e19a30
 	 */
e19a30
-	if (mi->version == 0 &&
e19a30
-	    linux_version_code() <= MAKE_VERSION(2, 6, 31))
e19a30
-		mi->version = 3;
e19a30
+	if (mi->version.v_mode == V_DEFAULT &&
e19a30
+	    linux_version_code() <= MAKE_VERSION(2, 6, 31)) {
e19a30
+		mi->version.major = 3;
e19a30
+		mi->version.v_mode = V_SPECIFIC;
e19a30
+	}
e19a30
 
e19a30
 	/*
e19a30
 	 * If we still don't know, check for version-specific
e19a30
 	 * mount options.
e19a30
 	 */
e19a30
-	if (mi->version == 0) {
e19a30
+	if (mi->version.v_mode == V_DEFAULT) {
e19a30
 		if (po_contains(mi->options, "mounthost") ||
e19a30
 		    po_contains(mi->options, "mountaddr") ||
e19a30
 		    po_contains(mi->options, "mountvers") ||
e19a30
-		    po_contains(mi->options, "mountproto"))
e19a30
-			mi->version = 3;
e19a30
+		    po_contains(mi->options, "mountproto")) {
e19a30
+			mi->version.major = 3;
e19a30
+			mi->version.v_mode = V_SPECIFIC;
e19a30
+		}
e19a30
 	}
e19a30
 
e19a30
 	/*
e19a30
 	 * If enabled, see if the default version was
e19a30
 	 * set in the config file
e19a30
 	 */
e19a30
-	nfs_default_version(mi);
e19a30
-	
e19a30
+	if (mi->version.v_mode != V_SPECIFIC) {
e19a30
+		nfs_default_version(mi);
e19a30
+		/*
e19a30
+		 * If the version was not specifically set, it will
e19a30
+		 * be set by autonegotiation later, so remove it now:
e19a30
+		 */
e19a30
+		po_remove_all(mi->options, "v4");
e19a30
+		po_remove_all(mi->options, "vers");
e19a30
+		po_remove_all(mi->options, "nfsvers");
e19a30
+	}
e19a30
+
e19a30
+	if (mi->version.v_mode == V_PARSE_ERR)
e19a30
+		return 0;
e19a30
+
e19a30
 	return 1;
e19a30
 }
e19a30
 
e19a30
@@ -693,6 +729,7 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi,
e19a30
 {
e19a30
 	struct mount_options *options = po_dup(mi->options);
e19a30
 	int result = 0;
e19a30
+	char version_opt[16];
e19a30
 	char *extra_opts = NULL;
e19a30
 
e19a30
 	if (!options) {
e19a30
@@ -700,20 +737,24 @@ static int nfs_do_mount_v4(struct nfsmount_info *mi,
e19a30
 		return result;
e19a30
 	}
e19a30
 
e19a30
-	if (mi->version == 0) {
e19a30
-		if (po_contains(options, "mounthost") ||
e19a30
-			po_contains(options, "mountaddr") ||
e19a30
-			po_contains(options, "mountvers") ||
e19a30
-			po_contains(options, "mountproto")) {
e19a30
-		/*
e19a30
-		 * Since these mountd options are set assume version 3
e19a30
-		 * is wanted so error out with EPROTONOSUPPORT so the
e19a30
-		 * protocol negation starts with v3.
e19a30
-		 */
e19a30
-			errno = EPROTONOSUPPORT;
e19a30
-			goto out_fail;
e19a30
-		}
e19a30
-		if (po_append(options, "vers=4") == PO_FAILED) {
e19a30
+	if (po_contains(options, "mounthost") ||
e19a30
+		po_contains(options, "mountaddr") ||
e19a30
+		po_contains(options, "mountvers") ||
e19a30
+		po_contains(options, "mountproto")) {
e19a30
+	/*
e19a30
+	 * Since these mountd options are set assume version 3
e19a30
+	 * is wanted so error out with EPROTONOSUPPORT so the
e19a30
+	 * protocol negation starts with v3.
e19a30
+	 */
e19a30
+		errno = EPROTONOSUPPORT;
e19a30
+		goto out_fail;
e19a30
+	}
e19a30
+
e19a30
+	if (mi->version.v_mode != V_SPECIFIC) {
e19a30
+		snprintf(version_opt, sizeof(version_opt) - 1,
e19a30
+			"vers=%lu.%lu", mi->version.major, mi->version.minor);
e19a30
+
e19a30
+		if (po_append(options, version_opt) == PO_FAILED) {
e19a30
 			errno = EINVAL;
e19a30
 			goto out_fail;
e19a30
 		}
e19a30
@@ -801,14 +842,28 @@ static int nfs_autonegotiate(struct nfsmount_info *mi)
e19a30
 	int result;
e19a30
 
e19a30
 	result = nfs_try_mount_v4(mi);
e19a30
+check_result:
e19a30
 	if (result)
e19a30
 		return result;
e19a30
 
e19a30
-check_errno:
e19a30
 	switch (errno) {
e19a30
 	case EPROTONOSUPPORT:
e19a30
 		/* A clear indication that the server or our
e19a30
-		 * client does not support NFS version 4. */
e19a30
+		 * client does not support NFS version 4 and minor */
e19a30
+	case EINVAL:
e19a30
+		/* A less clear indication that our client
e19a30
+		 * does not support NFSv4 minor version. */
e19a30
+		if (mi->version.v_mode == V_GENERAL &&
e19a30
+			mi->version.minor == 0)
e19a30
+				return result;
e19a30
+		if (mi->version.v_mode != V_SPECIFIC) {
e19a30
+			if (mi->version.minor > 0) {
e19a30
+				mi->version.minor--;
e19a30
+				result = nfs_try_mount_v4(mi);
e19a30
+				goto check_result;
e19a30
+			}
e19a30
+		}
e19a30
+
e19a30
 		goto fall_back;
e19a30
 	case ENOENT:
e19a30
 		/* Legacy Linux servers don't export an NFS
e19a30
@@ -827,7 +882,7 @@ check_errno:
e19a30
 			/* v4 server seems to be registered now. */
e19a30
 			result = nfs_try_mount_v4(mi);
e19a30
 			if (result == 0 && errno != ECONNREFUSED)
e19a30
-				goto check_errno;
e19a30
+				goto check_result;
e19a30
 		}
e19a30
 		return result;
e19a30
 	default:
e19a30
@@ -848,19 +903,19 @@ static int nfs_try_mount(struct nfsmount_info *mi)
e19a30
 {
e19a30
 	int result = 0;
e19a30
 
e19a30
-	switch (mi->version) {
e19a30
-	case 0:
e19a30
-		result = nfs_autonegotiate(mi);
e19a30
-		break;
e19a30
-	case 2:
e19a30
-	case 3:
e19a30
-		result = nfs_try_mount_v3v2(mi, FALSE);
e19a30
-		break;
e19a30
-	case 4:
e19a30
-		result = nfs_try_mount_v4(mi);
e19a30
-		break;
e19a30
-	default:
e19a30
-		errno = EIO;
e19a30
+	switch (mi->version.major) {
e19a30
+		case 2:
e19a30
+		case 3:
e19a30
+			result = nfs_try_mount_v3v2(mi, FALSE);
e19a30
+			break;
e19a30
+		case 4:
e19a30
+			if (mi->version.v_mode != V_SPECIFIC)
e19a30
+				result = nfs_autonegotiate(mi);
e19a30
+			else
e19a30
+				result = nfs_try_mount_v4(mi);
e19a30
+			break;
e19a30
+		default:
e19a30
+			errno = EIO;
e19a30
 	}
e19a30
 
e19a30
 	return result;