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