NFS: Add enums and match tables for mount option parsing
authorChuck Lever <chuck.lever@oracle.com>
Sun, 1 Jul 2007 16:13:44 +0000 (12:13 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 11 Jul 2007 03:40:47 +0000 (23:40 -0400)
This generic infrastructure works for both NFS and NFSv4 mounts.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/super.c

index ed3ec44..7e56411 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/inet.h>
 #include <linux/nfs_xdr.h>
 #include <linux/magic.h>
+#include <linux/parser.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
+
+struct nfs_parsed_mount_data {
+       int                     flags;
+       int                     rsize, wsize;
+       int                     timeo, retrans;
+       int                     acregmin, acregmax,
+                               acdirmin, acdirmax;
+       int                     namlen;
+       unsigned int            bsize;
+       unsigned int            auth_flavor_len;
+       rpc_authflavor_t        auth_flavors[1];
+       char                    *client_address;
+
+       struct {
+               struct sockaddr_in      address;
+               unsigned int            program;
+               unsigned int            version;
+               unsigned short          port;
+               int                     protocol;
+       } mount_server;
+
+       struct {
+               struct sockaddr_in      address;
+               char                    *hostname;
+               char                    *export_path;
+               unsigned int            program;
+               int                     protocol;
+       } nfs_server;
+};
+
+enum {
+       /* Mount options that take no arguments */
+       Opt_soft, Opt_hard,
+       Opt_intr, Opt_nointr,
+       Opt_posix, Opt_noposix,
+       Opt_cto, Opt_nocto,
+       Opt_ac, Opt_noac,
+       Opt_lock, Opt_nolock,
+       Opt_v2, Opt_v3,
+       Opt_udp, Opt_tcp,
+       Opt_acl, Opt_noacl,
+       Opt_rdirplus, Opt_nordirplus,
+
+       /* 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,
+       Opt_actimeo,
+       Opt_namelen,
+       Opt_mountport,
+       Opt_mountprog, Opt_mountvers,
+       Opt_nfsprog, Opt_nfsvers,
+
+       /* Mount options that take string arguments */
+       Opt_sec, Opt_proto, Opt_mountproto,
+       Opt_addr, Opt_mounthost, Opt_clientaddr,
+
+       /* Mount options that are ignored */
+       Opt_userspace, Opt_deprecated,
+
+       Opt_err
+};
+
+static match_table_t nfs_mount_option_tokens = {
+       { Opt_userspace, "bg" },
+       { Opt_userspace, "fg" },
+       { Opt_soft, "soft" },
+       { Opt_hard, "hard" },
+       { Opt_intr, "intr" },
+       { Opt_nointr, "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_v2, "v2" },
+       { Opt_v3, "v3" },
+       { Opt_udp, "udp" },
+       { Opt_tcp, "tcp" },
+       { Opt_acl, "acl" },
+       { Opt_noacl, "noacl" },
+       { Opt_rdirplus, "rdirplus" },
+       { Opt_nordirplus, "nordirplus" },
+
+       { Opt_port, "port=%u" },
+       { Opt_rsize, "rsize=%u" },
+       { Opt_wsize, "wsize=%u" },
+       { Opt_bsize, "bsize=%u" },
+       { Opt_timeo, "timeo=%u" },
+       { Opt_retrans, "retrans=%u" },
+       { Opt_acregmin, "acregmin=%u" },
+       { Opt_acregmax, "acregmax=%u" },
+       { Opt_acdirmin, "acdirmin=%u" },
+       { Opt_acdirmax, "acdirmax=%u" },
+       { Opt_actimeo, "actimeo=%u" },
+       { Opt_userspace, "retry=%u" },
+       { Opt_namelen, "namlen=%u" },
+       { Opt_mountport, "mountport=%u" },
+       { Opt_mountprog, "mountprog=%u" },
+       { Opt_mountvers, "mountvers=%u" },
+       { Opt_nfsprog, "nfsprog=%u" },
+       { Opt_nfsvers, "nfsvers=%u" },
+       { Opt_nfsvers, "vers=%u" },
+
+       { 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_err, NULL }
+};
+
+enum {
+       Opt_xprt_udp, Opt_xprt_tcp,
+
+       Opt_xprt_err
+};
+
+static match_table_t nfs_xprt_protocol_tokens = {
+       { Opt_xprt_udp, "udp" },
+       { Opt_xprt_tcp, "tcp" },
+
+       { Opt_xprt_err, NULL }
+};
+
+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
+};
+
+static 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_err, NULL }
+};
+
+
 static void nfs_umount_begin(struct vfsmount *, int);
 static int  nfs_statfs(struct dentry *, struct kstatfs *);
 static int  nfs_show_options(struct seq_file *, struct vfsmount *);
@@ -463,6 +622,375 @@ static int nfs_verify_server_address(struct sockaddr *addr)
        return 0;
 }
 
+/*
+ * Error-check and convert a string of mount options from user space into
+ * a data structure
+ */
+static int nfs_parse_mount_options(char *raw,
+                                  struct nfs_parsed_mount_data *mnt)
+{
+       char *p, *string;
+
+       if (!raw) {
+               dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
+               return 1;
+       }
+       dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw);
+
+       while ((p = strsep(&raw, ",")) != NULL) {
+               substring_t args[MAX_OPT_ARGS];
+               int option, token;
+
+               if (!*p)
+                       continue;
+
+               dfprintk(MOUNT, "NFS:   parsing nfs mount option '%s'\n", p);
+
+               token = match_token(p, nfs_mount_option_tokens, args);
+               switch (token) {
+               case Opt_soft:
+                       mnt->flags |= NFS_MOUNT_SOFT;
+                       break;
+               case Opt_hard:
+                       mnt->flags &= ~NFS_MOUNT_SOFT;
+                       break;
+               case Opt_intr:
+                       mnt->flags |= NFS_MOUNT_INTR;
+                       break;
+               case Opt_nointr:
+                       mnt->flags &= ~NFS_MOUNT_INTR;
+                       break;
+               case Opt_posix:
+                       mnt->flags |= NFS_MOUNT_POSIX;
+                       break;
+               case Opt_noposix:
+                       mnt->flags &= ~NFS_MOUNT_POSIX;
+                       break;
+               case Opt_cto:
+                       mnt->flags &= ~NFS_MOUNT_NOCTO;
+                       break;
+               case Opt_nocto:
+                       mnt->flags |= NFS_MOUNT_NOCTO;
+                       break;
+               case Opt_ac:
+                       mnt->flags &= ~NFS_MOUNT_NOAC;
+                       break;
+               case Opt_noac:
+                       mnt->flags |= NFS_MOUNT_NOAC;
+                       break;
+               case Opt_lock:
+                       mnt->flags &= ~NFS_MOUNT_NONLM;
+                       break;
+               case Opt_nolock:
+                       mnt->flags |= NFS_MOUNT_NONLM;
+                       break;
+               case Opt_v2:
+                       mnt->flags &= ~NFS_MOUNT_VER3;
+                       break;
+               case Opt_v3:
+                       mnt->flags |= NFS_MOUNT_VER3;
+                       break;
+               case Opt_udp:
+                       mnt->flags &= ~NFS_MOUNT_TCP;
+                       mnt->nfs_server.protocol = IPPROTO_UDP;
+                       mnt->timeo = 7;
+                       mnt->retrans = 5;
+                       break;
+               case Opt_tcp:
+                       mnt->flags |= NFS_MOUNT_TCP;
+                       mnt->nfs_server.protocol = IPPROTO_TCP;
+                       mnt->timeo = 600;
+                       mnt->retrans = 2;
+                       break;
+               case Opt_acl:
+                       mnt->flags &= ~NFS_MOUNT_NOACL;
+                       break;
+               case Opt_noacl:
+                       mnt->flags |= NFS_MOUNT_NOACL;
+                       break;
+               case Opt_rdirplus:
+                       mnt->flags &= ~NFS_MOUNT_NORDIRPLUS;
+                       break;
+               case Opt_nordirplus:
+                       mnt->flags |= NFS_MOUNT_NORDIRPLUS;
+                       break;
+
+               case Opt_port:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0 || option > 65535)
+                               return 0;
+                       mnt->nfs_server.address.sin_port = htonl(option);
+                       break;
+               case Opt_rsize:
+                       if (match_int(args, &mnt->rsize))
+                               return 0;
+                       break;
+               case Opt_wsize:
+                       if (match_int(args, &mnt->wsize))
+                               return 0;
+                       break;
+               case Opt_bsize:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0)
+                               return 0;
+                       mnt->bsize = option;
+                       break;
+               case Opt_timeo:
+                       if (match_int(args, &mnt->timeo))
+                               return 0;
+                       break;
+               case Opt_retrans:
+                       if (match_int(args, &mnt->retrans))
+                               return 0;
+                       break;
+               case Opt_acregmin:
+                       if (match_int(args, &mnt->acregmin))
+                               return 0;
+                       break;
+               case Opt_acregmax:
+                       if (match_int(args, &mnt->acregmax))
+                               return 0;
+                       break;
+               case Opt_acdirmin:
+                       if (match_int(args, &mnt->acdirmin))
+                               return 0;
+                       break;
+               case Opt_acdirmax:
+                       if (match_int(args, &mnt->acdirmax))
+                               return 0;
+                       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;
+                       break;
+               case Opt_namelen:
+                       if (match_int(args, &mnt->namlen))
+                               return 0;
+                       break;
+               case Opt_mountport:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0 || option > 65535)
+                               return 0;
+                       mnt->mount_server.port = option;
+                       break;
+               case Opt_mountprog:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0)
+                               return 0;
+                       mnt->mount_server.program = option;
+                       break;
+               case Opt_mountvers:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0)
+                               return 0;
+                       mnt->mount_server.version = option;
+                       break;
+               case Opt_nfsprog:
+                       if (match_int(args, &option))
+                               return 0;
+                       if (option < 0)
+                               return 0;
+                       mnt->nfs_server.program = option;
+                       break;
+               case Opt_nfsvers:
+                       if (match_int(args, &option))
+                               return 0;
+                       switch (option) {
+                       case 2:
+                               mnt->flags &= ~NFS_MOUNT_VER3;
+                               break;
+                       case 3:
+                               mnt->flags |= NFS_MOUNT_VER3;
+                               break;
+                       default:
+                               goto out_unrec_vers;
+                       }
+                       break;
+
+               case Opt_sec:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       token = match_token(string, nfs_secflavor_tokens, args);
+                       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;
+                       }
+                       break;
+               case Opt_proto:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       token = match_token(string,
+                                           nfs_xprt_protocol_tokens, args);
+                       kfree(string);
+
+                       switch (token) {
+                       case Opt_udp:
+                               mnt->flags &= ~NFS_MOUNT_TCP;
+                               mnt->nfs_server.protocol = IPPROTO_UDP;
+                               mnt->timeo = 7;
+                               mnt->retrans = 5;
+                               break;
+                       case Opt_tcp:
+                               mnt->flags |= NFS_MOUNT_TCP;
+                               mnt->nfs_server.protocol = IPPROTO_TCP;
+                               mnt->timeo = 600;
+                               mnt->retrans = 2;
+                               break;
+                       default:
+                               goto out_unrec_xprt;
+                       }
+                       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);
+
+                       switch (token) {
+                       case Opt_udp:
+                               mnt->mount_server.protocol = IPPROTO_UDP;
+                               break;
+                       case Opt_tcp:
+                               mnt->mount_server.protocol = IPPROTO_TCP;
+                               break;
+                       default:
+                               goto out_unrec_xprt;
+                       }
+                       break;
+               case Opt_addr:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       mnt->nfs_server.address.sin_family = AF_INET;
+                       mnt->nfs_server.address.sin_addr.s_addr =
+                                                       in_aton(string);
+                       kfree(string);
+                       break;
+               case Opt_clientaddr:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       mnt->client_address = string;
+                       break;
+               case Opt_mounthost:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+                       mnt->mount_server.address.sin_family = AF_INET;
+                       mnt->mount_server.address.sin_addr.s_addr =
+                                                       in_aton(string);
+                       kfree(string);
+                       break;
+
+               case Opt_userspace:
+               case Opt_deprecated:
+                       break;
+
+               default:
+                       goto out_unknown;
+               }
+       }
+
+       return 1;
+
+out_nomem:
+       printk(KERN_INFO "NFS: not enough memory to parse option\n");
+       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;
+}
+
 /*
  * Validate the NFS2/NFS3 mount data
  * - fills in the mount root filehandle