[PATCH 2/7] mount: report error if multiple version specifiers are given.

NeilBrown
2020-12-16T04:44:29Z

The NFS version can be requested with multiple different options:
v2 v3 v4 v4.x vers=x nfsvers=

If multiple versions are given with different options, the choice of
which wins is quite ideosyncratic. It certainly isn't simple "last one
wins" as with some other options.

Rather than providing a coherent rule, simply make multiple version
specifiers illegal.

This requires enhancing po_contains_prefix() to be able to look beyond
the first match, it see if there are multiple matches with the same
prefix, as well as checking all prefixes to see if more than one
matches.

Signed-off-by: NeilBrown <neilb@suse.de>
---
utils/mount/network.c | 36 ++++++++++++++++++++++--------------
utils/mount/parse_opt.c | 12 +++++++++---
utils/mount/parse_opt.h | 3 ++-
3 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/utils/mount/network.c b/utils/mount/network.c
index d9c0b513101d..e803dbbe5a2c 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -1269,27 +1269,31 @@ int
nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version)
{
char *version_key, *version_val = NULL, *cptr;
- int i, found = 0;
+ int i, found = -1;

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;
+ &version_key, 0) == PO_FOUND) {
+ if (found >= 0)
+ goto ret_error_multiple;
+ if (po_contains_prefix(options, nfs_version_opttbl[i],
+ NULL, 1) == PO_FOUND)
+ goto ret_error_multiple;
+ found = i;
}
}

- if (!found && strcmp(type, "nfs4") == 0)
+ if (found < 0 && strcmp(type, "nfs4") == 0)
version_val = type + 3;
- else if (!found)
+ else if (found < 0)
return 1;
- else if (i <= 2 ) {
+ else if (found <= 2 ) {
/* v2, v3, v4 */
version_val = version_key + 1;
version->v_mode = V_SPECIFIC;
- } else if (i > 2 ) {
+ } else if (found > 2 ) {
/* vers=, nfsvers= */
version_val = po_get(options, version_key);
}
@@ -1303,7 +1307,7 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v
if (version->major == 4 && *cptr != '.' &&
(version_val = po_get(options, "minorversion")) != NULL) {
version->minor = strtol(version_val, &cptr, 10);
- i = -1;
+ found = -1;
if (*cptr)
goto ret_error;
version->v_mode = V_SPECIFIC;
@@ -1319,7 +1323,7 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v
if (version_val != NULL) {
version->minor = strtol(version_val, &cptr, 10);
version->v_mode = V_SPECIFIC;
- } else
+ } else
version->v_mode = V_GENERAL;
}
if (*cptr != '\0')
@@ -1327,17 +1331,21 @@ nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *v

return 1;

+ret_error_multiple:
+ nfs_error(_("%s: multiple version options not permitted"),
+ progname);
+ found = 10; /* avoid other errors */
ret_error:
- if (i < 0) {
+ if (found < 0) {
nfs_error(_("%s: parsing error on 'minorversion=' option"),
progname);
- } else if (i <= 2 ) {
+ } else if (found <= 2 ) {
nfs_error(_("%s: parsing error on 'v' option"),
progname);
- } else if (i == 3 ) {
+ } else if (found == 3 ) {
nfs_error(_("%s: parsing error on 'vers=' option"),
progname);
- } else if (i == 4) {
+ } else if (found == 4) {
nfs_error(_("%s: parsing error on 'nfsvers=' option"),
progname);
}
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
index 7ba61c4e52d8..b6065cad2888 100644
--- a/utils/mount/parse_opt.c
+++ b/utils/mount/parse_opt.c
@@ -414,19 +414,25 @@ po_found_t po_contains(struct mount_options *options, char *keyword)
* @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
+ * @n: number of instances to skip, so '0' returns the first.
*
* 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)
+ const char *prefix, char **keyword, int n)
{
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;
+ if (n > 0) {
+ n -= 1;
+ } else {
+ if (keyword)
+ *keyword = option->keyword;
+ return PO_FOUND;
+ }
}
}

diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h
index 0745e0f0833e..0a153768d434 100644
--- a/utils/mount/parse_opt.h
+++ b/utils/mount/parse_opt.h
@@ -46,7 +46,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);
+ const char *prefix, char **keyword,
+ int n);
char * po_get(struct mount_options *, char *);
po_found_t po_get_numeric(struct mount_options *,
char *, long *);