You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
590 lines
16 KiB
590 lines
16 KiB
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;
|
|
|