| 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; |