OSDN Git Service

NFS: Convert mount option parsing to use functionality from fs_parser.h
authorScott Mayhew <smayhew@redhat.com>
Tue, 10 Dec 2019 12:31:12 +0000 (07:31 -0500)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 15 Jan 2020 15:15:17 +0000 (10:15 -0500)
Split out from commit "NFS: Add fs_context support."

Convert existing mount option definitions to fs_parameter_enum's and
fs_parameter_spec's.  Parse mount options using fs_parse() and
lookup_constant().

Notes:

1) Fixed a typo in the udp6 definition in nfs_xprt_protocol_tokens
from the original commit.

2) fs_parse() expects an fs_context as the first arg so that any
errors can be logged to the fs_context.  We're passing NULL for the
fs_context (this will change in commit "NFS: Add fs_context support.")
which is okay as it will cause logfc() to do a printk() instead.

3) fs_parse() expects an fs_paramter as the third arg.  We're
building an fs_parameter manually in nfs_fs_context_parse_option(),
which will go away in commit "NFS: Add fs_context support.".

Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
fs/nfs/fs_context.c

index 4453144..9a31620 100644 (file)
@@ -11,7 +11,8 @@
 
 #include <linux/module.h>
 #include <linux/fs.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/nfs4_mount.h>
 
 #define NFS_MAX_CONNECTIONS 16
 
-enum {
-       /* Mount options that take no arguments */
-       Opt_soft, Opt_softerr, Opt_hard,
-       Opt_posix, Opt_noposix,
-       Opt_cto, Opt_nocto,
-       Opt_ac, Opt_noac,
-       Opt_lock, Opt_nolock,
-       Opt_udp, Opt_tcp, Opt_rdma,
-       Opt_acl, Opt_noacl,
-       Opt_rdirplus, Opt_nordirplus,
-       Opt_sharecache, Opt_nosharecache,
-       Opt_resvport, Opt_noresvport,
-       Opt_fscache, Opt_nofscache,
-       Opt_migration, Opt_nomigration,
-
-       /* Mount options that take integer arguments */
-       Opt_port,
-       Opt_rsize, Opt_wsize, Opt_bsize,
-       Opt_timeo, Opt_retrans,
-       Opt_acregmin, Opt_acregmax,
-       Opt_acdirmin, Opt_acdirmax,
+enum nfs_param {
+       Opt_ac,
+       Opt_acdirmax,
+       Opt_acdirmin,
+       Opt_acl,
+       Opt_acregmax,
+       Opt_acregmin,
        Opt_actimeo,
-       Opt_namelen,
+       Opt_addr,
+       Opt_bg,
+       Opt_bsize,
+       Opt_clientaddr,
+       Opt_cto,
+       Opt_fg,
+       Opt_fscache,
+       Opt_hard,
+       Opt_intr,
+       Opt_local_lock,
+       Opt_lock,
+       Opt_lookupcache,
+       Opt_migration,
+       Opt_minorversion,
+       Opt_mountaddr,
+       Opt_mounthost,
        Opt_mountport,
+       Opt_mountproto,
        Opt_mountvers,
-       Opt_minorversion,
-
-       /* Mount options that take string arguments */
-       Opt_nfsvers,
-       Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
-       Opt_addr, Opt_mountaddr, Opt_clientaddr,
+       Opt_namelen,
        Opt_nconnect,
-       Opt_lookupcache,
-       Opt_fscache_uniq,
-       Opt_local_lock,
-
-       /* Special mount options */
-       Opt_userspace, Opt_deprecated, Opt_sloppy,
-
-       Opt_err
+       Opt_port,
+       Opt_posix,
+       Opt_proto,
+       Opt_rdirplus,
+       Opt_rdma,
+       Opt_resvport,
+       Opt_retrans,
+       Opt_retry,
+       Opt_rsize,
+       Opt_sec,
+       Opt_sharecache,
+       Opt_sloppy,
+       Opt_soft,
+       Opt_softerr,
+       Opt_source,
+       Opt_tcp,
+       Opt_timeo,
+       Opt_udp,
+       Opt_v,
+       Opt_vers,
+       Opt_wsize,
 };
 
-static const match_table_t nfs_mount_option_tokens = {
-       { Opt_userspace, "bg" },
-       { Opt_userspace, "fg" },
-       { Opt_userspace, "retry=%s" },
-
-       { Opt_sloppy, "sloppy" },
-
-       { Opt_soft, "soft" },
-       { Opt_softerr, "softerr" },
-       { Opt_hard, "hard" },
-       { Opt_deprecated, "intr" },
-       { Opt_deprecated, "nointr" },
-       { Opt_posix, "posix" },
-       { Opt_noposix, "noposix" },
-       { Opt_cto, "cto" },
-       { Opt_nocto, "nocto" },
-       { Opt_ac, "ac" },
-       { Opt_noac, "noac" },
-       { Opt_lock, "lock" },
-       { Opt_nolock, "nolock" },
-       { Opt_udp, "udp" },
-       { Opt_tcp, "tcp" },
-       { Opt_rdma, "rdma" },
-       { Opt_acl, "acl" },
-       { Opt_noacl, "noacl" },
-       { Opt_rdirplus, "rdirplus" },
-       { Opt_nordirplus, "nordirplus" },
-       { Opt_sharecache, "sharecache" },
-       { Opt_nosharecache, "nosharecache" },
-       { Opt_resvport, "resvport" },
-       { Opt_noresvport, "noresvport" },
-       { Opt_fscache, "fsc" },
-       { Opt_nofscache, "nofsc" },
-       { Opt_migration, "migration" },
-       { Opt_nomigration, "nomigration" },
-
-       { Opt_port, "port=%s" },
-       { Opt_rsize, "rsize=%s" },
-       { Opt_wsize, "wsize=%s" },
-       { Opt_bsize, "bsize=%s" },
-       { Opt_timeo, "timeo=%s" },
-       { Opt_retrans, "retrans=%s" },
-       { Opt_acregmin, "acregmin=%s" },
-       { Opt_acregmax, "acregmax=%s" },
-       { Opt_acdirmin, "acdirmin=%s" },
-       { Opt_acdirmax, "acdirmax=%s" },
-       { Opt_actimeo, "actimeo=%s" },
-       { Opt_namelen, "namlen=%s" },
-       { Opt_mountport, "mountport=%s" },
-       { Opt_mountvers, "mountvers=%s" },
-       { Opt_minorversion, "minorversion=%s" },
-
-       { Opt_nfsvers, "nfsvers=%s" },
-       { Opt_nfsvers, "vers=%s" },
-
-       { Opt_sec, "sec=%s" },
-       { Opt_proto, "proto=%s" },
-       { Opt_mountproto, "mountproto=%s" },
-       { Opt_addr, "addr=%s" },
-       { Opt_clientaddr, "clientaddr=%s" },
-       { Opt_mounthost, "mounthost=%s" },
-       { Opt_mountaddr, "mountaddr=%s" },
-
-       { Opt_nconnect, "nconnect=%s" },
-
-       { Opt_lookupcache, "lookupcache=%s" },
-       { Opt_fscache_uniq, "fsc=%s" },
-       { Opt_local_lock, "local_lock=%s" },
-
-       /* The following needs to be listed after all other options */
-       { Opt_nfsvers, "v%s" },
-
-       { Opt_err, NULL }
+static const struct fs_parameter_spec nfs_param_specs[] = {
+       fsparam_flag_no("ac",           Opt_ac),
+       fsparam_u32   ("acdirmax",      Opt_acdirmax),
+       fsparam_u32   ("acdirmin",      Opt_acdirmin),
+       fsparam_flag_no("acl",          Opt_acl),
+       fsparam_u32   ("acregmax",      Opt_acregmax),
+       fsparam_u32   ("acregmin",      Opt_acregmin),
+       fsparam_u32   ("actimeo",       Opt_actimeo),
+       fsparam_string("addr",          Opt_addr),
+       fsparam_flag  ("bg",            Opt_bg),
+       fsparam_u32   ("bsize",         Opt_bsize),
+       fsparam_string("clientaddr",    Opt_clientaddr),
+       fsparam_flag_no("cto",          Opt_cto),
+       fsparam_flag  ("fg",            Opt_fg),
+       __fsparam(fs_param_is_string, "fsc",            Opt_fscache,
+                 fs_param_neg_with_no|fs_param_v_optional),
+       fsparam_flag  ("hard",          Opt_hard),
+       __fsparam(fs_param_is_flag, "intr",             Opt_intr,
+                 fs_param_neg_with_no|fs_param_deprecated),
+       fsparam_enum  ("local_lock",    Opt_local_lock),
+       fsparam_flag_no("lock",         Opt_lock),
+       fsparam_enum  ("lookupcache",   Opt_lookupcache),
+       fsparam_flag_no("migration",    Opt_migration),
+       fsparam_u32   ("minorversion",  Opt_minorversion),
+       fsparam_string("mountaddr",     Opt_mountaddr),
+       fsparam_string("mounthost",     Opt_mounthost),
+       fsparam_u32   ("mountport",     Opt_mountport),
+       fsparam_string("mountproto",    Opt_mountproto),
+       fsparam_u32   ("mountvers",     Opt_mountvers),
+       fsparam_u32   ("namlen",        Opt_namelen),
+       fsparam_u32   ("nconnect",      Opt_nconnect),
+       fsparam_string("nfsvers",       Opt_vers),
+       fsparam_u32   ("port",          Opt_port),
+       fsparam_flag_no("posix",        Opt_posix),
+       fsparam_string("proto",         Opt_proto),
+       fsparam_flag_no("rdirplus",     Opt_rdirplus),
+       fsparam_flag  ("rdma",          Opt_rdma),
+       fsparam_flag_no("resvport",     Opt_resvport),
+       fsparam_u32   ("retrans",       Opt_retrans),
+       fsparam_string("retry",         Opt_retry),
+       fsparam_u32   ("rsize",         Opt_rsize),
+       fsparam_string("sec",           Opt_sec),
+       fsparam_flag_no("sharecache",   Opt_sharecache),
+       fsparam_flag  ("sloppy",        Opt_sloppy),
+       fsparam_flag  ("soft",          Opt_soft),
+       fsparam_flag  ("softerr",       Opt_softerr),
+       fsparam_string("source",        Opt_source),
+       fsparam_flag  ("tcp",           Opt_tcp),
+       fsparam_u32   ("timeo",         Opt_timeo),
+       fsparam_flag  ("udp",           Opt_udp),
+       fsparam_flag  ("v2",            Opt_v),
+       fsparam_flag  ("v3",            Opt_v),
+       fsparam_flag  ("v4",            Opt_v),
+       fsparam_flag  ("v4.0",          Opt_v),
+       fsparam_flag  ("v4.1",          Opt_v),
+       fsparam_flag  ("v4.2",          Opt_v),
+       fsparam_string("vers",          Opt_vers),
+       fsparam_u32   ("wsize",         Opt_wsize),
+       {}
 };
 
 enum {
-       Opt_xprt_udp, Opt_xprt_udp6, Opt_xprt_tcp, Opt_xprt_tcp6, Opt_xprt_rdma,
-       Opt_xprt_rdma6,
-
-       Opt_xprt_err
-};
-
-static const match_table_t nfs_xprt_protocol_tokens = {
-       { Opt_xprt_udp, "udp" },
-       { Opt_xprt_udp6, "udp6" },
-       { Opt_xprt_tcp, "tcp" },
-       { Opt_xprt_tcp6, "tcp6" },
-       { Opt_xprt_rdma, "rdma" },
-       { Opt_xprt_rdma6, "rdma6" },
-
-       { Opt_xprt_err, NULL }
+       Opt_local_lock_all,
+       Opt_local_lock_flock,
+       Opt_local_lock_none,
+       Opt_local_lock_posix,
 };
 
 enum {
-       Opt_sec_none, Opt_sec_sys,
-       Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
-       Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp,
-       Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp,
-
-       Opt_sec_err
+       Opt_lookupcache_all,
+       Opt_lookupcache_none,
+       Opt_lookupcache_positive,
 };
 
-static const match_table_t nfs_secflavor_tokens = {
-       { Opt_sec_none, "none" },
-       { Opt_sec_none, "null" },
-       { Opt_sec_sys, "sys" },
-
-       { Opt_sec_krb5, "krb5" },
-       { Opt_sec_krb5i, "krb5i" },
-       { Opt_sec_krb5p, "krb5p" },
-
-       { Opt_sec_lkey, "lkey" },
-       { Opt_sec_lkeyi, "lkeyi" },
-       { Opt_sec_lkeyp, "lkeyp" },
-
-       { Opt_sec_spkm, "spkm3" },
-       { Opt_sec_spkmi, "spkm3i" },
-       { Opt_sec_spkmp, "spkm3p" },
+static const struct fs_parameter_enum nfs_param_enums[] = {
+       { Opt_local_lock,       "all",          Opt_local_lock_all },
+       { Opt_local_lock,       "flock",        Opt_local_lock_flock },
+       { Opt_local_lock,       "none",         Opt_local_lock_none },
+       { Opt_local_lock,       "posix",        Opt_local_lock_posix },
+       { Opt_lookupcache,      "all",          Opt_lookupcache_all },
+       { Opt_lookupcache,      "none",         Opt_lookupcache_none },
+       { Opt_lookupcache,      "pos",          Opt_lookupcache_positive },
+       { Opt_lookupcache,      "positive",     Opt_lookupcache_positive },
+       {}
+};
 
-       { Opt_sec_err, NULL }
+static const struct fs_parameter_description nfs_fs_parameters = {
+       .name           = "nfs",
+       .specs          = nfs_param_specs,
+       .enums          = nfs_param_enums,
 };
 
 enum {
-       Opt_lookupcache_all, Opt_lookupcache_positive,
-       Opt_lookupcache_none,
-
-       Opt_lookupcache_err
+       Opt_vers_2,
+       Opt_vers_3,
+       Opt_vers_4,
+       Opt_vers_4_0,
+       Opt_vers_4_1,
+       Opt_vers_4_2,
 };
 
-static const match_table_t nfs_lookupcache_tokens = {
-       { Opt_lookupcache_all, "all" },
-       { Opt_lookupcache_positive, "pos" },
-       { Opt_lookupcache_positive, "positive" },
-       { Opt_lookupcache_none, "none" },
-
-       { Opt_lookupcache_err, NULL }
+static const struct constant_table nfs_vers_tokens[] = {
+       { "2",          Opt_vers_2 },
+       { "3",          Opt_vers_3 },
+       { "4",          Opt_vers_4 },
+       { "4.0",        Opt_vers_4_0 },
+       { "4.1",        Opt_vers_4_1 },
+       { "4.2",        Opt_vers_4_2 },
 };
 
 enum {
-       Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix,
-       Opt_local_lock_none,
-
-       Opt_local_lock_err
+       Opt_xprt_rdma,
+       Opt_xprt_rdma6,
+       Opt_xprt_tcp,
+       Opt_xprt_tcp6,
+       Opt_xprt_udp,
+       Opt_xprt_udp6,
+       nr__Opt_xprt
 };
 
-static const match_table_t nfs_local_lock_tokens = {
-       { Opt_local_lock_all, "all" },
-       { Opt_local_lock_flock, "flock" },
-       { Opt_local_lock_posix, "posix" },
-       { Opt_local_lock_none, "none" },
-
-       { Opt_local_lock_err, NULL }
+static const struct constant_table nfs_xprt_protocol_tokens[nr__Opt_xprt] = {
+       { "rdma",       Opt_xprt_rdma },
+       { "rdma6",      Opt_xprt_rdma6 },
+       { "tcp",        Opt_xprt_tcp },
+       { "tcp6",       Opt_xprt_tcp6 },
+       { "udp",        Opt_xprt_udp },
+       { "udp6",       Opt_xprt_udp6 },
 };
 
 enum {
-       Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0,
-       Opt_vers_4_1, Opt_vers_4_2,
-
-       Opt_vers_err
+       Opt_sec_krb5,
+       Opt_sec_krb5i,
+       Opt_sec_krb5p,
+       Opt_sec_lkey,
+       Opt_sec_lkeyi,
+       Opt_sec_lkeyp,
+       Opt_sec_none,
+       Opt_sec_spkm,
+       Opt_sec_spkmi,
+       Opt_sec_spkmp,
+       Opt_sec_sys,
+       nr__Opt_sec
 };
 
-static const match_table_t nfs_vers_tokens = {
-       { Opt_vers_2, "2" },
-       { Opt_vers_3, "3" },
-       { Opt_vers_4, "4" },
-       { Opt_vers_4_0, "4.0" },
-       { Opt_vers_4_1, "4.1" },
-       { Opt_vers_4_2, "4.2" },
-
-       { Opt_vers_err, NULL }
+static const struct constant_table nfs_secflavor_tokens[] = {
+       { "krb5",       Opt_sec_krb5 },
+       { "krb5i",      Opt_sec_krb5i },
+       { "krb5p",      Opt_sec_krb5p },
+       { "lkey",       Opt_sec_lkey },
+       { "lkeyi",      Opt_sec_lkeyi },
+       { "lkeyp",      Opt_sec_lkeyp },
+       { "none",       Opt_sec_none },
+       { "null",       Opt_sec_none },
+       { "spkm3",      Opt_sec_spkm },
+       { "spkm3i",     Opt_sec_spkmi },
+       { "spkm3p",     Opt_sec_spkmp },
+       { "sys",        Opt_sec_sys },
 };
 
 struct nfs_fs_context *nfs_alloc_parsed_mount_data(void)
@@ -368,17 +366,19 @@ static int nfs_auth_info_add(struct nfs_fs_context *ctx,
 /*
  * Parse the value of the 'sec=' option.
  */
-static int nfs_parse_security_flavors(struct nfs_fs_context *ctx, char *value)
+static int nfs_parse_security_flavors(struct nfs_fs_context *ctx,
+                                     struct fs_parameter *param)
 {
-       substring_t args[MAX_OPT_ARGS];
        rpc_authflavor_t pseudoflavor;
-       char *p;
+       char *string = param->string, *p;
        int ret;
 
-       dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
+       dfprintk(MOUNT, "NFS: parsing %s=%s option\n", param->key, param->string);
 
-       while ((p = strsep(&value, ":")) != NULL) {
-               switch (match_token(p, nfs_secflavor_tokens, args)) {
+       while ((p = strsep(&string, ":")) != NULL) {
+               if (!*p)
+                       continue;
+               switch (lookup_constant(nfs_secflavor_tokens, p, -1)) {
                case Opt_sec_none:
                        pseudoflavor = RPC_AUTH_NULL;
                        break;
@@ -427,11 +427,10 @@ static int nfs_parse_security_flavors(struct nfs_fs_context *ctx, char *value)
 }
 
 static int nfs_parse_version_string(struct nfs_fs_context *ctx,
-                                   char *string,
-                                   substring_t *args)
+                                   const char *string)
 {
        ctx->flags &= ~NFS_MOUNT_VER3;
-       switch (match_token(string, nfs_vers_tokens, args)) {
+       switch (lookup_constant(nfs_vers_tokens, string, -1)) {
        case Opt_vers_2:
                ctx->version = 2;
                break;
@@ -465,64 +464,24 @@ static int nfs_parse_version_string(struct nfs_fs_context *ctx,
        return 0;
 }
 
-static int nfs_get_option_str(substring_t args[], char **option)
-{
-       kfree(*option);
-       *option = match_strdup(args);
-       return !*option;
-}
-
-static int nfs_get_option_ui(struct nfs_fs_context *ctx,
-                            substring_t args[], unsigned int *option)
-{
-       match_strlcpy(ctx->buf, args, sizeof(ctx->buf));
-       return kstrtouint(ctx->buf, 10, option);
-}
-
-static int nfs_get_option_ui_bound(struct nfs_fs_context *ctx,
-                                  substring_t args[], unsigned int *option,
-                                  unsigned int l_bound, unsigned u_bound)
-{
-       int ret;
-
-       match_strlcpy(ctx->buf, args, sizeof(ctx->buf));
-       ret = kstrtouint(ctx->buf, 10, option);
-       if (ret < 0)
-               return ret;
-       if (*option < l_bound || *option > u_bound)
-               return -ERANGE;
-       return 0;
-}
-
-static int nfs_get_option_us_bound(struct nfs_fs_context *ctx,
-                                  substring_t args[], unsigned short *option,
-                                  unsigned short l_bound,
-                                  unsigned short u_bound)
-{
-       int ret;
-
-       match_strlcpy(ctx->buf, args, sizeof(ctx->buf));
-       ret = kstrtou16(ctx->buf, 10, option);
-       if (ret < 0)
-               return ret;
-       if (*option < l_bound || *option > u_bound)
-               return -ERANGE;
-       return 0;
-}
-
 /*
- * Parse a single mount option in "key[=val]" form.
+ * Parse a single mount parameter.
  */
-static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
+static int nfs_fs_context_parse_param(struct nfs_fs_context *ctx,
+                                     struct fs_parameter *param)
 {
-       substring_t args[MAX_OPT_ARGS];
-       char *string;
-       int token, ret;
+       struct fs_parse_result result;
+       unsigned short protofamily, mountfamily;
+       unsigned int len;
+       int ret, opt;
+
+       dfprintk(MOUNT, "NFS:   parsing nfs mount option '%s'\n", param->key);
 
-       dfprintk(MOUNT, "NFS:   parsing nfs mount option '%s'\n", p);
+       opt = fs_parse(NULL, &nfs_fs_parameters, param, &result);
+       if (opt < 0)
+               return ctx->sloppy ? 1 : opt;
 
-       token = match_token(p, nfs_mount_option_tokens, args);
-       switch (token) {
+       switch (opt) {
                /*
                 * boolean options:  foo/nofoo
                 */
@@ -538,30 +497,31 @@ static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
                ctx->flags &= ~(NFS_MOUNT_SOFT|NFS_MOUNT_SOFTERR);
                break;
        case Opt_posix:
-               ctx->flags |= NFS_MOUNT_POSIX;
-               break;
-       case Opt_noposix:
-               ctx->flags &= ~NFS_MOUNT_POSIX;
+               if (result.negated)
+                       ctx->flags &= ~NFS_MOUNT_POSIX;
+               else
+                       ctx->flags |= NFS_MOUNT_POSIX;
                break;
        case Opt_cto:
-               ctx->flags &= ~NFS_MOUNT_NOCTO;
-               break;
-       case Opt_nocto:
-               ctx->flags |= NFS_MOUNT_NOCTO;
+               if (result.negated)
+                       ctx->flags |= NFS_MOUNT_NOCTO;
+               else
+                       ctx->flags &= ~NFS_MOUNT_NOCTO;
                break;
        case Opt_ac:
-               ctx->flags &= ~NFS_MOUNT_NOAC;
-               break;
-       case Opt_noac:
-               ctx->flags |= NFS_MOUNT_NOAC;
+               if (result.negated)
+                       ctx->flags |= NFS_MOUNT_NOAC;
+               else
+                       ctx->flags &= ~NFS_MOUNT_NOAC;
                break;
        case Opt_lock:
-               ctx->flags &= ~NFS_MOUNT_NONLM;
-               ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
-               break;
-       case Opt_nolock:
-               ctx->flags |= NFS_MOUNT_NONLM;
-               ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
+               if (result.negated) {
+                       ctx->flags |= NFS_MOUNT_NONLM;
+                       ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
+               } else {
+                       ctx->flags &= ~NFS_MOUNT_NONLM;
+                       ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
+               }
                break;
        case Opt_udp:
                ctx->flags &= ~NFS_MOUNT_TCP;
@@ -574,195 +534,177 @@ static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
        case Opt_rdma:
                ctx->flags |= NFS_MOUNT_TCP; /* for side protocols */
                ctx->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
-               xprt_load_transport(p);
+               xprt_load_transport(param->key);
                break;
        case Opt_acl:
-               ctx->flags &= ~NFS_MOUNT_NOACL;
-               break;
-       case Opt_noacl:
-               ctx->flags |= NFS_MOUNT_NOACL;
+               if (result.negated)
+                       ctx->flags |= NFS_MOUNT_NOACL;
+               else
+                       ctx->flags &= ~NFS_MOUNT_NOACL;
                break;
        case Opt_rdirplus:
-               ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
-               break;
-       case Opt_nordirplus:
-               ctx->flags |= NFS_MOUNT_NORDIRPLUS;
+               if (result.negated)
+                       ctx->flags |= NFS_MOUNT_NORDIRPLUS;
+               else
+                       ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
                break;
        case Opt_sharecache:
-               ctx->flags &= ~NFS_MOUNT_UNSHARED;
-               break;
-       case Opt_nosharecache:
-               ctx->flags |= NFS_MOUNT_UNSHARED;
+               if (result.negated)
+                       ctx->flags |= NFS_MOUNT_UNSHARED;
+               else
+                       ctx->flags &= ~NFS_MOUNT_UNSHARED;
                break;
        case Opt_resvport:
-               ctx->flags &= ~NFS_MOUNT_NORESVPORT;
-               break;
-       case Opt_noresvport:
-               ctx->flags |= NFS_MOUNT_NORESVPORT;
+               if (result.negated)
+                       ctx->flags |= NFS_MOUNT_NORESVPORT;
+               else
+                       ctx->flags &= ~NFS_MOUNT_NORESVPORT;
                break;
        case Opt_fscache:
-               ctx->options |= NFS_OPTION_FSCACHE;
                kfree(ctx->fscache_uniq);
-               ctx->fscache_uniq = NULL;
-               break;
-       case Opt_nofscache:
-               ctx->options &= ~NFS_OPTION_FSCACHE;
-               kfree(ctx->fscache_uniq);
-               ctx->fscache_uniq = NULL;
+               ctx->fscache_uniq = param->string;
+               param->string = NULL;
+               if (result.negated)
+                       ctx->options &= ~NFS_OPTION_FSCACHE;
+               else
+                       ctx->options |= NFS_OPTION_FSCACHE;
                break;
        case Opt_migration:
-               ctx->options |= NFS_OPTION_MIGRATION;
-               break;
-       case Opt_nomigration:
-               ctx->options &= ~NFS_OPTION_MIGRATION;
+               if (result.negated)
+                       ctx->options &= ~NFS_OPTION_MIGRATION;
+               else
+                       ctx->options |= NFS_OPTION_MIGRATION;
                break;
 
                /*
                 * options that take numeric values
                 */
        case Opt_port:
-               if (nfs_get_option_ui_bound(ctx, args, &ctx->nfs_server.port,
-                                           0, USHRT_MAX))
-                       goto out_invalid_value;
+               if (result.uint_32 > USHRT_MAX)
+                       goto out_of_bounds;
+               ctx->nfs_server.port = result.uint_32;
                break;
        case Opt_rsize:
-               if (nfs_get_option_ui(ctx, args, &ctx->rsize))
-                       goto out_invalid_value;
+               ctx->rsize = result.uint_32;
                break;
        case Opt_wsize:
-               if (nfs_get_option_ui(ctx, args, &ctx->wsize))
-                       goto out_invalid_value;
+               ctx->wsize = result.uint_32;
                break;
        case Opt_bsize:
-               if (nfs_get_option_ui(ctx, args, &ctx->bsize))
-                       goto out_invalid_value;
+               ctx->bsize = result.uint_32;
                break;
        case Opt_timeo:
-               if (nfs_get_option_ui_bound(ctx, args, &ctx->timeo, 1, INT_MAX))
-                       goto out_invalid_value;
+               if (result.uint_32 < 1 || result.uint_32 > INT_MAX)
+                       goto out_of_bounds;
+               ctx->timeo = result.uint_32;
                break;
        case Opt_retrans:
-               if (nfs_get_option_ui_bound(ctx, args, &ctx->retrans, 0, INT_MAX))
-                       goto out_invalid_value;
+               if (result.uint_32 > INT_MAX)
+                       goto out_of_bounds;
+               ctx->retrans = result.uint_32;
                break;
        case Opt_acregmin:
-               if (nfs_get_option_ui(ctx, args, &ctx->acregmin))
-                       goto out_invalid_value;
+               ctx->acregmin = result.uint_32;
                break;
        case Opt_acregmax:
-               if (nfs_get_option_ui(ctx, args, &ctx->acregmax))
-                       goto out_invalid_value;
+               ctx->acregmax = result.uint_32;
                break;
        case Opt_acdirmin:
-               if (nfs_get_option_ui(ctx, args, &ctx->acdirmin))
-                       goto out_invalid_value;
+               ctx->acdirmin = result.uint_32;
                break;
        case Opt_acdirmax:
-               if (nfs_get_option_ui(ctx, args, &ctx->acdirmax))
-                       goto out_invalid_value;
+               ctx->acdirmax = result.uint_32;
                break;
        case Opt_actimeo:
-               if (nfs_get_option_ui(ctx, args, &ctx->acdirmax))
-                       goto out_invalid_value;
-               ctx->acregmin = ctx->acregmax =
-                       ctx->acdirmin = ctx->acdirmax;
+               ctx->acregmin = result.uint_32;
+               ctx->acregmax = result.uint_32;
+               ctx->acdirmin = result.uint_32;
+               ctx->acdirmax = result.uint_32;
                break;
        case Opt_namelen:
-               if (nfs_get_option_ui(ctx, args, &ctx->namlen))
-                       goto out_invalid_value;
+               ctx->namlen = result.uint_32;
                break;
        case Opt_mountport:
-               if (nfs_get_option_ui_bound(ctx, args, &ctx->mount_server.port,
-                                           0, USHRT_MAX))
-                       goto out_invalid_value;
+               if (result.uint_32 > USHRT_MAX)
+                       goto out_of_bounds;
+               ctx->mount_server.port = result.uint_32;
                break;
        case Opt_mountvers:
-               if (nfs_get_option_ui_bound(ctx, args, &ctx->mount_server.version,
-                                           NFS_MNT_VERSION, NFS_MNT3_VERSION))
-                       goto out_invalid_value;
+               if (result.uint_32 < NFS_MNT_VERSION ||
+                   result.uint_32 > NFS_MNT3_VERSION)
+                       goto out_of_bounds;
+               ctx->mount_server.version = result.uint_32;
                break;
        case Opt_minorversion:
-               if (nfs_get_option_ui_bound(ctx, args, &ctx->minorversion,
-                                           0, NFS4_MAX_MINOR_VERSION))
-                       goto out_invalid_value;
+               if (result.uint_32 > NFS4_MAX_MINOR_VERSION)
+                       goto out_of_bounds;
+               ctx->minorversion = result.uint_32;
                break;
 
                /*
                 * options that take text values
                 */
-       case Opt_nfsvers:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               ret = nfs_parse_version_string(ctx, string, args);
-               kfree(string);
+       case Opt_v:
+               ret = nfs_parse_version_string(ctx, param->key + 1);
+               if (ret < 0)
+                       return ret;
+               break;
+       case Opt_vers:
+               ret = nfs_parse_version_string(ctx, param->string);
                if (ret < 0)
                        return ret;
                break;
        case Opt_sec:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               ret = nfs_parse_security_flavors(ctx, string);
-               kfree(string);
+               ret = nfs_parse_security_flavors(ctx, param);
                if (ret < 0)
                        return ret;
                break;
-       case Opt_proto:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               token = match_token(string, nfs_xprt_protocol_tokens, args);
 
-               ctx->protofamily = AF_INET;
-               switch (token) {
+       case Opt_proto:
+               protofamily = AF_INET;
+               switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
                case Opt_xprt_udp6:
-                       ctx->protofamily = AF_INET6;
+                       protofamily = AF_INET6;
                        /* fall through */
                case Opt_xprt_udp:
                        ctx->flags &= ~NFS_MOUNT_TCP;
                        ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
                        break;
                case Opt_xprt_tcp6:
-                       ctx->protofamily = AF_INET6;
+                       protofamily = AF_INET6;
                        /* fall through */
                case Opt_xprt_tcp:
                        ctx->flags |= NFS_MOUNT_TCP;
                        ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
                        break;
                case Opt_xprt_rdma6:
-                       ctx->protofamily = AF_INET6;
+                       protofamily = AF_INET6;
                        /* fall through */
                case Opt_xprt_rdma:
                        /* vector side protocols to TCP */
                        ctx->flags |= NFS_MOUNT_TCP;
                        ctx->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
-                       xprt_load_transport(string);
+                       xprt_load_transport(param->string);
                        break;
                default:
-                       kfree(string);
                        dfprintk(MOUNT, "NFS:   unrecognized transport protocol\n");
                        return -EINVAL;
                }
-               kfree(string);
+
+               ctx->protofamily = protofamily;
                break;
-       case Opt_mountproto:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               token = match_token(string, nfs_xprt_protocol_tokens, args);
-               kfree(string);
 
-               ctx->mountfamily = AF_INET;
-               switch (token) {
+       case Opt_mountproto:
+               mountfamily = AF_INET;
+               switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
                case Opt_xprt_udp6:
-                       ctx->mountfamily = AF_INET6;
+                       mountfamily = AF_INET6;
                        /* fall through */
                case Opt_xprt_udp:
                        ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
                        break;
                case Opt_xprt_tcp6:
-                       ctx->mountfamily = AF_INET6;
+                       mountfamily = AF_INET6;
                        /* fall through */
                case Opt_xprt_tcp:
                        ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
@@ -772,51 +714,42 @@ static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
                        dfprintk(MOUNT, "NFS:   unrecognized transport protocol\n");
                        return -EINVAL;
                }
+               ctx->mountfamily = mountfamily;
                break;
+
        case Opt_addr:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               ctx->nfs_server.addrlen =
-                       rpc_pton(ctx->net, string, strlen(string),
-                                &ctx->nfs_server.address,
-                                sizeof(ctx->nfs_server._address));
-               kfree(string);
-               if (ctx->nfs_server.addrlen == 0)
+               len = rpc_pton(ctx->net, param->string, param->size,
+                              &ctx->nfs_server.address,
+                              sizeof(ctx->nfs_server._address));
+               if (len == 0)
                        goto out_invalid_address;
+               ctx->nfs_server.addrlen = len;
                break;
        case Opt_clientaddr:
-               if (nfs_get_option_str(args, &ctx->client_address))
-                       goto out_nomem;
+               kfree(ctx->client_address);
+               ctx->client_address = param->string;
+               param->string = NULL;
                break;
        case Opt_mounthost:
-               if (nfs_get_option_str(args, &ctx->mount_server.hostname))
-                       goto out_nomem;
+               kfree(ctx->mount_server.hostname);
+               ctx->mount_server.hostname = param->string;
+               param->string = NULL;
                break;
        case Opt_mountaddr:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               ctx->mount_server.addrlen =
-                       rpc_pton(ctx->net, string, strlen(string),
-                                &ctx->mount_server.address,
-                                sizeof(ctx->mount_server._address));
-               kfree(string);
-               if (ctx->mount_server.addrlen == 0)
+               len = rpc_pton(ctx->net, param->string, param->size,
+                              &ctx->mount_server.address,
+                              sizeof(ctx->mount_server._address));
+               if (len == 0)
                        goto out_invalid_address;
+               ctx->mount_server.addrlen = len;
                break;
        case Opt_nconnect:
-               if (nfs_get_option_us_bound(ctx, args, &ctx->nfs_server.nconnect,
-                                           1, NFS_MAX_CONNECTIONS))
-                       goto out_invalid_value;
+               if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_CONNECTIONS)
+                       goto out_of_bounds;
+               ctx->nfs_server.nconnect = result.uint_32;
                break;
        case Opt_lookupcache:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               token = match_token(string, nfs_lookupcache_tokens, args);
-               kfree(string);
-               switch (token) {
+               switch (result.uint_32) {
                case Opt_lookupcache_all:
                        ctx->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
                        break;
@@ -828,22 +761,11 @@ static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
                        ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
                        break;
                default:
-                       dfprintk(MOUNT, "NFS:   invalid lookupcache argument\n");
-                       return -EINVAL;
+                       goto out_invalid_value;
                }
                break;
-       case Opt_fscache_uniq:
-               if (nfs_get_option_str(args, &ctx->fscache_uniq))
-                       goto out_nomem;
-               ctx->options |= NFS_OPTION_FSCACHE;
-               break;
        case Opt_local_lock:
-               string = match_strdup(args);
-               if (string == NULL)
-                       goto out_nomem;
-               token = match_token(string, nfs_local_lock_tokens, args);
-               kfree(string);
-               switch (token) {
+               switch (result.uint_32) {
                case Opt_local_lock_all:
                        ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK |
                                       NFS_MOUNT_LOCAL_FCNTL);
@@ -859,8 +781,7 @@ static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
                                        NFS_MOUNT_LOCAL_FCNTL);
                        break;
                default:
-                       dfprintk(MOUNT, "NFS:   invalid local_lock argument\n");
-                       return -EINVAL;
+                       goto out_invalid_value;
                }
                break;
 
@@ -868,30 +789,50 @@ static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
                 * Special options
                 */
        case Opt_sloppy:
-               ctx->sloppy = 1;
+               ctx->sloppy = true;
                dfprintk(MOUNT, "NFS:   relaxing parsing rules\n");
                break;
-       case Opt_userspace:
-       case Opt_deprecated:
-               dfprintk(MOUNT, "NFS:   ignoring mount option '%s'\n", p);
-               break;
-
-       default:
-               dfprintk(MOUNT, "NFS:   unrecognized mount option '%s'\n", p);
-               return -EINVAL;
        }
 
        return 0;
 
-out_invalid_address:
-       printk(KERN_INFO "NFS: bad IP address specified: %s\n", p);
-       return -EINVAL;
 out_invalid_value:
-       printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p);
+       printk(KERN_INFO "NFS: Bad mount option value specified\n");
        return -EINVAL;
-out_nomem:
-       printk(KERN_INFO "NFS: not enough memory to parse option\n");
-       return -ENOMEM;
+out_invalid_address:
+       printk(KERN_INFO "NFS: Bad IP address specified\n");
+       return -EINVAL;
+out_of_bounds:
+       printk(KERN_INFO "NFS: Value for '%s' out of range\n", param->key);
+       return -ERANGE;
+}
+
+/* cribbed from generic_parse_monolithic and vfs_parse_fs_string */
+static int nfs_fs_context_parse_option(struct nfs_fs_context *ctx, char *p)
+{
+       int ret;
+       char *key = p, *value;
+       size_t v_size = 0;
+       struct fs_parameter param;
+
+       memset(&param, 0, sizeof(param));
+       value = strchr(key, '=');
+       if (value && value != key) {
+               *value++ = 0;
+               v_size = strlen(value);
+       }
+       param.key = key;
+       param.type = fs_value_is_flag;
+       param.size = v_size;
+       if (v_size > 0) {
+               param.type = fs_value_is_string;
+               param.string = kmemdup_nul(value, v_size, GFP_KERNEL);
+               if (!param.string)
+                       return -ENOMEM;
+       }
+       ret = nfs_fs_context_parse_param(ctx, &param);
+       kfree(param.string);
+       return ret;
 }
 
 /*