vfs: Use const for kernel parser table
[safe/jmp/linux-2.6] / fs / nfs / super.c
index ebed63e..ffb6974 100644 (file)
@@ -92,17 +92,19 @@ enum {
        Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
        Opt_addr, Opt_mountaddr, Opt_clientaddr,
 
-       /* Mount options that are ignored */
-       Opt_userspace, Opt_deprecated,
+       /* Special mount options */
+       Opt_userspace, Opt_deprecated, Opt_sloppy,
 
        Opt_err
 };
 
-static match_table_t nfs_mount_option_tokens = {
+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_hard, "hard" },
        { Opt_deprecated, "intr" },
@@ -161,7 +163,7 @@ enum {
        Opt_xprt_err
 };
 
-static match_table_t nfs_xprt_protocol_tokens = {
+static const match_table_t nfs_xprt_protocol_tokens = {
        { Opt_xprt_udp, "udp" },
        { Opt_xprt_tcp, "tcp" },
        { Opt_xprt_rdma, "rdma" },
@@ -178,7 +180,7 @@ enum {
        Opt_sec_err
 };
 
-static match_table_t nfs_secflavor_tokens = {
+static const match_table_t nfs_secflavor_tokens = {
        { Opt_sec_none, "none" },
        { Opt_sec_none, "null" },
        { Opt_sec_sys, "sys" },
@@ -372,8 +374,6 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
        };
        int error;
 
-       lock_kernel();
-
        error = server->nfs_client->rpc_ops->statfs(server, fh, &res);
        if (error < 0)
                goto out_err;
@@ -405,12 +405,10 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
        buf->f_namelen = server->namelen;
 
-       unlock_kernel();
        return 0;
 
  out_err:
        dprintk("%s: statfs error = %d\n", __func__, -error);
-       unlock_kernel();
        return error;
 }
 
@@ -854,14 +852,85 @@ static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)
 }
 
 /*
+ * Parse the value of the 'sec=' option.
+ *
+ * The flavor_len setting is for v4 mounts.
+ */
+static int nfs_parse_security_flavors(char *value,
+                                     struct nfs_parsed_mount_data *mnt)
+{
+       substring_t args[MAX_OPT_ARGS];
+
+       dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
+
+       switch (match_token(value, nfs_secflavor_tokens, args)) {
+       case Opt_sec_none:
+               mnt->auth_flavor_len = 0;
+               mnt->auth_flavors[0] = RPC_AUTH_NULL;
+               break;
+       case Opt_sec_sys:
+               mnt->auth_flavor_len = 0;
+               mnt->auth_flavors[0] = RPC_AUTH_UNIX;
+               break;
+       case Opt_sec_krb5:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
+               break;
+       case Opt_sec_krb5i:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
+               break;
+       case Opt_sec_krb5p:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
+               break;
+       case Opt_sec_lkey:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
+               break;
+       case Opt_sec_lkeyi:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
+               break;
+       case Opt_sec_lkeyp:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
+               break;
+       case Opt_sec_spkm:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
+               break;
+       case Opt_sec_spkmi:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
+               break;
+       case Opt_sec_spkmp:
+               mnt->auth_flavor_len = 1;
+               mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void nfs_parse_invalid_value(const char *option)
+{
+       dfprintk(MOUNT, "NFS:   bad value specified for %s option\n", option);
+}
+
+/*
  * Error-check and convert a string of mount options from user space into
- * a data structure
+ * a data structure.  The whole mount string is processed; bad options are
+ * skipped as they are encountered.  If there were no errors, return 1;
+ * otherwise return 0 (zero).
  */
 static int nfs_parse_mount_options(char *raw,
                                   struct nfs_parsed_mount_data *mnt)
 {
        char *p, *string, *secdata;
-       int rc;
+       int rc, sloppy = 0, errors = 0;
 
        if (!raw) {
                dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
@@ -894,6 +963,10 @@ static int nfs_parse_mount_options(char *raw,
 
                token = match_token(p, nfs_mount_option_tokens, args);
                switch (token) {
+
+               /*
+                * boolean options:  foo/nofoo
+                */
                case Opt_soft:
                        mnt->flags |= NFS_MOUNT_SOFT;
                        break;
@@ -961,165 +1034,144 @@ static int nfs_parse_mount_options(char *raw,
                        mnt->flags |= NFS_MOUNT_UNSHARED;
                        break;
 
+               /*
+                * options that take numeric values
+                */
                case Opt_port:
-                       if (match_int(args, &option))
-                               return 0;
-                       if (option < 0 || option > 65535)
-                               return 0;
-                       mnt->nfs_server.port = option;
+                       if (match_int(args, &option) ||
+                           option < 0 || option > USHORT_MAX) {
+                               errors++;
+                               nfs_parse_invalid_value("port");
+                       } else
+                               mnt->nfs_server.port = option;
                        break;
                case Opt_rsize:
-                       if (match_int(args, &mnt->rsize))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("rsize");
+                       } else
+                               mnt->rsize = option;
                        break;
                case Opt_wsize:
-                       if (match_int(args, &mnt->wsize))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("wsize");
+                       } else
+                               mnt->wsize = option;
                        break;
                case Opt_bsize:
-                       if (match_int(args, &option))
-                               return 0;
-                       if (option < 0)
-                               return 0;
-                       mnt->bsize = option;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("bsize");
+                       } else
+                               mnt->bsize = option;
                        break;
                case Opt_timeo:
-                       if (match_int(args, &mnt->timeo))
-                               return 0;
+                       if (match_int(args, &option) || option <= 0) {
+                               errors++;
+                               nfs_parse_invalid_value("timeo");
+                       } else
+                               mnt->timeo = option;
                        break;
                case Opt_retrans:
-                       if (match_int(args, &mnt->retrans))
-                               return 0;
+                       if (match_int(args, &option) || option <= 0) {
+                               errors++;
+                               nfs_parse_invalid_value("retrans");
+                       } else
+                               mnt->retrans = option;
                        break;
                case Opt_acregmin:
-                       if (match_int(args, &mnt->acregmin))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("acregmin");
+                       } else
+                               mnt->acregmin = option;
                        break;
                case Opt_acregmax:
-                       if (match_int(args, &mnt->acregmax))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("acregmax");
+                       } else
+                               mnt->acregmax = option;
                        break;
                case Opt_acdirmin:
-                       if (match_int(args, &mnt->acdirmin))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("acdirmin");
+                       } else
+                               mnt->acdirmin = option;
                        break;
                case Opt_acdirmax:
-                       if (match_int(args, &mnt->acdirmax))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("acdirmax");
+                       } else
+                               mnt->acdirmax = option;
                        break;
                case Opt_actimeo:
-                       if (match_int(args, &option))
-                               return 0;
-                       if (option < 0)
-                               return 0;
-                       mnt->acregmin =
-                       mnt->acregmax =
-                       mnt->acdirmin =
-                       mnt->acdirmax = option;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("actimeo");
+                       } else
+                               mnt->acregmin = mnt->acregmax =
+                               mnt->acdirmin = mnt->acdirmax = option;
                        break;
                case Opt_namelen:
-                       if (match_int(args, &mnt->namlen))
-                               return 0;
+                       if (match_int(args, &option) || option < 0) {
+                               errors++;
+                               nfs_parse_invalid_value("namlen");
+                       } else
+                               mnt->namlen = option;
                        break;
                case Opt_mountport:
-                       if (match_int(args, &option))
-                               return 0;
-                       if (option < 0 || option > 65535)
-                               return 0;
-                       mnt->mount_server.port = option;
+                       if (match_int(args, &option) ||
+                           option < 0 || option > USHORT_MAX) {
+                               errors++;
+                               nfs_parse_invalid_value("mountport");
+                       } else
+                               mnt->mount_server.port = option;
                        break;
                case Opt_mountvers:
-                       if (match_int(args, &option))
-                               return 0;
-                       if (option < 0)
-                               return 0;
-                       mnt->mount_server.version = option;
+                       if (match_int(args, &option) ||
+                           option < NFS_MNT_VERSION ||
+                           option > NFS_MNT3_VERSION) {
+                               errors++;
+                               nfs_parse_invalid_value("mountvers");
+                       } else
+                               mnt->mount_server.version = option;
                        break;
                case Opt_nfsvers:
-                       if (match_int(args, &option))
-                               return 0;
+                       if (match_int(args, &option)) {
+                               errors++;
+                               nfs_parse_invalid_value("nfsvers");
+                               break;
+                       }
                        switch (option) {
-                       case 2:
+                       case NFS2_VERSION:
                                mnt->flags &= ~NFS_MOUNT_VER3;
                                break;
-                       case 3:
+                       case NFS3_VERSION:
                                mnt->flags |= NFS_MOUNT_VER3;
                                break;
                        default:
-                               goto out_unrec_vers;
+                               errors++;
+                               nfs_parse_invalid_value("nfsvers");
                        }
                        break;
 
+               /*
+                * options that take text values
+                */
                case Opt_sec:
                        string = match_strdup(args);
                        if (string == NULL)
                                goto out_nomem;
-                       token = match_token(string, nfs_secflavor_tokens, args);
+                       rc = nfs_parse_security_flavors(string, mnt);
                        kfree(string);
-
-                       /*
-                        * The flags setting is for v2/v3.  The flavor_len
-                        * setting is for v4.  v2/v3 also need to know the
-                        * difference between NULL and UNIX.
-                        */
-                       switch (token) {
-                       case Opt_sec_none:
-                               mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 0;
-                               mnt->auth_flavors[0] = RPC_AUTH_NULL;
-                               break;
-                       case Opt_sec_sys:
-                               mnt->flags &= ~NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 0;
-                               mnt->auth_flavors[0] = RPC_AUTH_UNIX;
-                               break;
-                       case Opt_sec_krb5:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
-                               break;
-                       case Opt_sec_krb5i:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
-                               break;
-                       case Opt_sec_krb5p:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
-                               break;
-                       case Opt_sec_lkey:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
-                               break;
-                       case Opt_sec_lkeyi:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
-                               break;
-                       case Opt_sec_lkeyp:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
-                               break;
-                       case Opt_sec_spkm:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
-                               break;
-                       case Opt_sec_spkmi:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
-                               break;
-                       case Opt_sec_spkmp:
-                               mnt->flags |= NFS_MOUNT_SECFLAVOUR;
-                               mnt->auth_flavor_len = 1;
-                               mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
-                               break;
-                       default:
-                               goto out_unrec_sec;
+                       if (!rc) {
+                               errors++;
+                               dfprintk(MOUNT, "NFS:   unrecognized "
+                                               "security flavor\n");
                        }
                        break;
                case Opt_proto:
@@ -1145,7 +1197,9 @@ static int nfs_parse_mount_options(char *raw,
                                mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
                                break;
                        default:
-                               goto out_unrec_xprt;
+                               errors++;
+                               dfprintk(MOUNT, "NFS:   unrecognized "
+                                               "transport protocol\n");
                        }
                        break;
                case Opt_mountproto:
@@ -1165,7 +1219,9 @@ static int nfs_parse_mount_options(char *raw,
                                break;
                        case Opt_xprt_rdma: /* not used for side protocols */
                        default:
-                               goto out_unrec_xprt;
+                               errors++;
+                               dfprintk(MOUNT, "NFS:   unrecognized "
+                                               "transport protocol\n");
                        }
                        break;
                case Opt_addr:
@@ -1203,6 +1259,13 @@ static int nfs_parse_mount_options(char *raw,
                        kfree(string);
                        break;
 
+               /*
+                * Special options
+                */
+               case Opt_sloppy:
+                       sloppy = 1;
+                       dfprintk(MOUNT, "NFS:   relaxing parsing rules\n");
+                       break;
                case Opt_userspace:
                case Opt_deprecated:
                        dfprintk(MOUNT, "NFS:   ignoring mount option "
@@ -1210,10 +1273,18 @@ static int nfs_parse_mount_options(char *raw,
                        break;
 
                default:
-                       goto out_unknown;
+                       errors++;
+                       dfprintk(MOUNT, "NFS:   unrecognized mount option "
+                                       "'%s'\n", p);
                }
        }
 
+       if (errors > 0) {
+               dfprintk(MOUNT, "NFS: parsing encountered %d error%s\n",
+                               errors, (errors == 1 ? "" : "s"));
+               if (!sloppy)
+                       return 0;
+       }
        return 1;
 
 out_nomem:
@@ -1223,21 +1294,6 @@ out_security_failure:
        free_secdata(secdata);
        printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
        return 0;
-out_unrec_vers:
-       printk(KERN_INFO "NFS: unrecognized NFS version number\n");
-       return 0;
-
-out_unrec_xprt:
-       printk(KERN_INFO "NFS: unrecognized transport protocol\n");
-       return 0;
-
-out_unrec_sec:
-       printk(KERN_INFO "NFS: unrecognized security flavor\n");
-       return 0;
-
-out_unknown:
-       printk(KERN_INFO "NFS: unknown mount option: %s\n", p);
-       return 0;
 }
 
 /*
@@ -1467,6 +1523,7 @@ static int nfs_validate_mount_data(void *options,
        args->mount_server.port = 0;    /* autobind unless user sets port */
        args->nfs_server.port   = 0;    /* autobind unless user sets port */
        args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+       args->auth_flavors[0]   = RPC_AUTH_UNIX;
 
        switch (data->version) {
        case 1:
@@ -1524,7 +1581,9 @@ static int nfs_validate_mount_data(void *options,
                args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
                args->namlen            = data->namlen;
                args->bsize             = data->bsize;
-               args->auth_flavors[0]   = data->pseudoflavor;
+
+               if (data->flags & NFS_MOUNT_SECFLAVOUR)
+                       args->auth_flavors[0] = data->pseudoflavor;
                if (!args->nfs_server.hostname)
                        goto out_nomem;
 
@@ -1588,9 +1647,6 @@ static int nfs_validate_mount_data(void *options,
                }
        }
 
-       if (!(args->flags & NFS_MOUNT_SECFLAVOUR))
-               args->auth_flavors[0] = RPC_AUTH_UNIX;
-
 #ifndef CONFIG_NFS_V3
        if (args->flags & NFS_MOUNT_VER3)
                goto out_v3_not_compiled;
@@ -1668,9 +1724,9 @@ nfs_remount(struct super_block *sb, int *flags, char *raw_data)
         * ones were explicitly specified. Fall back to legacy behavior and
         * just return success.
         */
-       if ((nfsvers == 4 && options4->version == 1) ||
-           (nfsvers <= 3 && options->version >= 1 &&
-            options->version <= 6))
+       if ((nfsvers == 4 && (!options4 || options4->version == 1)) ||
+           (nfsvers <= 3 && (!options || (options->version >= 1 &&
+                                          options->version <= 6))))
                return 0;
 
        data = kzalloc(sizeof(*data), GFP_KERNEL);
@@ -2124,6 +2180,8 @@ static int nfs4_validate_mount_data(void *options,
        args->acdirmin          = NFS_DEF_ACDIRMIN;
        args->acdirmax          = NFS_DEF_ACDIRMAX;
        args->nfs_server.port   = NFS_PORT; /* 2049 unless user set port= */
+       args->auth_flavors[0]   = RPC_AUTH_UNIX;
+       args->auth_flavor_len   = 0;
 
        switch (data->version) {
        case 1:
@@ -2139,18 +2197,13 @@ static int nfs4_validate_mount_data(void *options,
                                                &args->nfs_server.address))
                        goto out_no_address;
 
-               switch (data->auth_flavourlen) {
-               case 0:
-                       args->auth_flavors[0] = RPC_AUTH_UNIX;
-                       break;
-               case 1:
+               if (data->auth_flavourlen) {
+                       if (data->auth_flavourlen > 1)
+                               goto out_inval_auth;
                        if (copy_from_user(&args->auth_flavors[0],
                                           data->auth_flavours,
                                           sizeof(args->auth_flavors[0])))
                                return -EFAULT;
-                       break;
-               default:
-                       goto out_inval_auth;
                }
 
                c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
@@ -2202,15 +2255,8 @@ static int nfs4_validate_mount_data(void *options,
 
                nfs_validate_transport_protocol(args);
 
-               switch (args->auth_flavor_len) {
-               case 0:
-                       args->auth_flavors[0] = RPC_AUTH_UNIX;
-                       break;
-               case 1:
-                       break;
-               default:
+               if (args->auth_flavor_len > 1)
                        goto out_inval_auth;
-               }
 
                if (args->client_address == NULL)
                        goto out_no_client_address;