Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / fs / cifs / connect.c
index 855d8c0..e0b56d7 100644 (file)
@@ -40,6 +40,8 @@
 #include <linux/module.h>
 #include <keys/user-type.h>
 #include <net/ipv6.h>
+#include <linux/parser.h>
+
 #include "cifspdu.h"
 #include "cifsglob.h"
 #include "cifsproto.h"
@@ -63,6 +65,202 @@ extern mempool_t *cifs_req_poolp;
 #define TLINK_ERROR_EXPIRE     (1 * HZ)
 #define TLINK_IDLE_EXPIRE      (600 * HZ)
 
+enum {
+
+       /* Mount options that take no arguments */
+       Opt_user_xattr, Opt_nouser_xattr,
+       Opt_forceuid, Opt_noforceuid,
+       Opt_noblocksend, Opt_noautotune,
+       Opt_hard, Opt_soft, Opt_perm, Opt_noperm,
+       Opt_mapchars, Opt_nomapchars, Opt_sfu,
+       Opt_nosfu, Opt_nodfs, Opt_posixpaths,
+       Opt_noposixpaths, Opt_nounix,
+       Opt_nocase,
+       Opt_brl, Opt_nobrl,
+       Opt_forcemandatorylock, Opt_setuids,
+       Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
+       Opt_nohard, Opt_nosoft,
+       Opt_nointr, Opt_intr,
+       Opt_nostrictsync, Opt_strictsync,
+       Opt_serverino, Opt_noserverino,
+       Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
+       Opt_acl, Opt_noacl, Opt_locallease,
+       Opt_sign, Opt_seal, Opt_direct,
+       Opt_strictcache, Opt_noac,
+       Opt_fsc, Opt_mfsymlinks,
+       Opt_multiuser, Opt_sloppy,
+
+       /* Mount options which take numeric value */
+       Opt_backupuid, Opt_backupgid, Opt_uid,
+       Opt_cruid, Opt_gid, Opt_file_mode,
+       Opt_dirmode, Opt_port,
+       Opt_rsize, Opt_wsize, Opt_actimeo,
+
+       /* Mount options which take string value */
+       Opt_user, Opt_pass, Opt_ip,
+       Opt_unc, Opt_domain,
+       Opt_srcaddr, Opt_prefixpath,
+       Opt_iocharset, Opt_sockopt,
+       Opt_netbiosname, Opt_servern,
+       Opt_ver, Opt_sec,
+
+       /* Mount options to be ignored */
+       Opt_ignore,
+
+       /* Options which could be blank */
+       Opt_blank_pass,
+       Opt_blank_user,
+       Opt_blank_ip,
+
+       Opt_err
+};
+
+static const match_table_t cifs_mount_option_tokens = {
+
+       { Opt_user_xattr, "user_xattr" },
+       { Opt_nouser_xattr, "nouser_xattr" },
+       { Opt_forceuid, "forceuid" },
+       { Opt_noforceuid, "noforceuid" },
+       { Opt_noblocksend, "noblocksend" },
+       { Opt_noautotune, "noautotune" },
+       { Opt_hard, "hard" },
+       { Opt_soft, "soft" },
+       { Opt_perm, "perm" },
+       { Opt_noperm, "noperm" },
+       { Opt_mapchars, "mapchars" },
+       { Opt_nomapchars, "nomapchars" },
+       { Opt_sfu, "sfu" },
+       { Opt_nosfu, "nosfu" },
+       { Opt_nodfs, "nodfs" },
+       { Opt_posixpaths, "posixpaths" },
+       { Opt_noposixpaths, "noposixpaths" },
+       { Opt_nounix, "nounix" },
+       { Opt_nounix, "nolinux" },
+       { Opt_nocase, "nocase" },
+       { Opt_nocase, "ignorecase" },
+       { Opt_brl, "brl" },
+       { Opt_nobrl, "nobrl" },
+       { Opt_nobrl, "nolock" },
+       { Opt_forcemandatorylock, "forcemandatorylock" },
+       { Opt_forcemandatorylock, "forcemand" },
+       { Opt_setuids, "setuids" },
+       { Opt_nosetuids, "nosetuids" },
+       { Opt_dynperm, "dynperm" },
+       { Opt_nodynperm, "nodynperm" },
+       { Opt_nohard, "nohard" },
+       { Opt_nosoft, "nosoft" },
+       { Opt_nointr, "nointr" },
+       { Opt_intr, "intr" },
+       { Opt_nostrictsync, "nostrictsync" },
+       { Opt_strictsync, "strictsync" },
+       { Opt_serverino, "serverino" },
+       { Opt_noserverino, "noserverino" },
+       { Opt_rwpidforward, "rwpidforward" },
+       { Opt_cifsacl, "cifsacl" },
+       { Opt_nocifsacl, "nocifsacl" },
+       { Opt_acl, "acl" },
+       { Opt_noacl, "noacl" },
+       { Opt_locallease, "locallease" },
+       { Opt_sign, "sign" },
+       { Opt_seal, "seal" },
+       { Opt_direct, "direct" },
+       { Opt_direct, "directio" },
+       { Opt_direct, "forcedirectio" },
+       { Opt_strictcache, "strictcache" },
+       { Opt_noac, "noac" },
+       { Opt_fsc, "fsc" },
+       { Opt_mfsymlinks, "mfsymlinks" },
+       { Opt_multiuser, "multiuser" },
+       { Opt_sloppy, "sloppy" },
+
+       { Opt_backupuid, "backupuid=%s" },
+       { Opt_backupgid, "backupgid=%s" },
+       { Opt_uid, "uid=%s" },
+       { Opt_cruid, "cruid=%s" },
+       { Opt_gid, "gid=%s" },
+       { Opt_file_mode, "file_mode=%s" },
+       { Opt_dirmode, "dirmode=%s" },
+       { Opt_dirmode, "dir_mode=%s" },
+       { Opt_port, "port=%s" },
+       { Opt_rsize, "rsize=%s" },
+       { Opt_wsize, "wsize=%s" },
+       { Opt_actimeo, "actimeo=%s" },
+
+       { Opt_blank_user, "user=" },
+       { Opt_blank_user, "username=" },
+       { Opt_user, "user=%s" },
+       { Opt_user, "username=%s" },
+       { Opt_blank_pass, "pass=" },
+       { Opt_pass, "pass=%s" },
+       { Opt_pass, "password=%s" },
+       { Opt_blank_ip, "ip=" },
+       { Opt_blank_ip, "addr=" },
+       { Opt_ip, "ip=%s" },
+       { Opt_ip, "addr=%s" },
+       { Opt_unc, "unc=%s" },
+       { Opt_unc, "target=%s" },
+       { Opt_unc, "path=%s" },
+       { Opt_domain, "dom=%s" },
+       { Opt_domain, "domain=%s" },
+       { Opt_domain, "workgroup=%s" },
+       { Opt_srcaddr, "srcaddr=%s" },
+       { Opt_prefixpath, "prefixpath=%s" },
+       { Opt_iocharset, "iocharset=%s" },
+       { Opt_sockopt, "sockopt=%s" },
+       { Opt_netbiosname, "netbiosname=%s" },
+       { Opt_servern, "servern=%s" },
+       { Opt_ver, "ver=%s" },
+       { Opt_ver, "vers=%s" },
+       { Opt_ver, "version=%s" },
+       { Opt_sec, "sec=%s" },
+
+       { Opt_ignore, "cred" },
+       { Opt_ignore, "credentials" },
+       { Opt_ignore, "cred=%s" },
+       { Opt_ignore, "credentials=%s" },
+       { Opt_ignore, "guest" },
+       { Opt_ignore, "rw" },
+       { Opt_ignore, "ro" },
+       { Opt_ignore, "suid" },
+       { Opt_ignore, "nosuid" },
+       { Opt_ignore, "exec" },
+       { Opt_ignore, "noexec" },
+       { Opt_ignore, "nodev" },
+       { Opt_ignore, "noauto" },
+       { Opt_ignore, "dev" },
+       { Opt_ignore, "mand" },
+       { Opt_ignore, "nomand" },
+       { Opt_ignore, "_netdev" },
+
+       { Opt_err, NULL }
+};
+
+enum {
+       Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
+       Opt_sec_ntlmsspi, Opt_sec_ntlmssp,
+       Opt_ntlm, Opt_sec_ntlmi, Opt_sec_ntlmv2i,
+       Opt_sec_nontlm, Opt_sec_lanman,
+       Opt_sec_none,
+
+       Opt_sec_err
+};
+
+static const match_table_t cifs_secflavor_tokens = {
+       { Opt_sec_krb5, "krb5" },
+       { Opt_sec_krb5i, "krb5i" },
+       { Opt_sec_krb5p, "krb5p" },
+       { Opt_sec_ntlmsspi, "ntlmsspi" },
+       { Opt_sec_ntlmssp, "ntlmssp" },
+       { Opt_ntlm, "ntlm" },
+       { Opt_sec_ntlmi, "ntlmi" },
+       { Opt_sec_ntlmv2i, "ntlmv2i" },
+       { Opt_sec_nontlm, "nontlm" },
+       { Opt_sec_lanman, "lanman" },
+       { Opt_sec_none, "none" },
+
+       { Opt_sec_err, NULL }
+};
+
 static int ip_connect(struct TCP_Server_Info *server);
 static int generic_ip_connect(struct TCP_Server_Info *server);
 static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
@@ -920,23 +1118,95 @@ extract_hostname(const char *unc)
        return dst;
 }
 
+static int get_option_ul(substring_t args[], unsigned long *option)
+{
+       int rc;
+       char *string;
+
+       string = match_strdup(args);
+       if (string == NULL)
+               return -ENOMEM;
+       rc = kstrtoul(string, 0, option);
+       kfree(string);
+
+       return rc;
+}
+
+
+static int cifs_parse_security_flavors(char *value,
+                                      struct smb_vol *vol)
+{
+
+       substring_t args[MAX_OPT_ARGS];
+
+       switch (match_token(value, cifs_secflavor_tokens, args)) {
+       case Opt_sec_krb5:
+               vol->secFlg |= CIFSSEC_MAY_KRB5;
+               break;
+       case Opt_sec_krb5i:
+               vol->secFlg |= CIFSSEC_MAY_KRB5 | CIFSSEC_MUST_SIGN;
+               break;
+       case Opt_sec_krb5p:
+               /* vol->secFlg |= CIFSSEC_MUST_SEAL | CIFSSEC_MAY_KRB5; */
+               cERROR(1, "Krb5 cifs privacy not supported");
+               break;
+       case Opt_sec_ntlmssp:
+               vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
+               break;
+       case Opt_sec_ntlmsspi:
+               vol->secFlg |= CIFSSEC_MAY_NTLMSSP | CIFSSEC_MUST_SIGN;
+               break;
+       case Opt_ntlm:
+               /* ntlm is default so can be turned off too */
+               vol->secFlg |= CIFSSEC_MAY_NTLM;
+               break;
+       case Opt_sec_ntlmi:
+               vol->secFlg |= CIFSSEC_MAY_NTLM | CIFSSEC_MUST_SIGN;
+               break;
+       case Opt_sec_nontlm:
+               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
+               break;
+       case Opt_sec_ntlmv2i:
+               vol->secFlg |= CIFSSEC_MAY_NTLMV2 | CIFSSEC_MUST_SIGN;
+               break;
+#ifdef CONFIG_CIFS_WEAK_PW_HASH
+       case Opt_sec_lanman:
+               vol->secFlg |= CIFSSEC_MAY_LANMAN;
+               break;
+#endif
+       case Opt_sec_none:
+               vol->nullauth = 1;
+               break;
+       default:
+               cERROR(1, "bad security option: %s", value);
+               return 1;
+       }
+
+       return 0;
+}
+
 static int
 cifs_parse_mount_options(const char *mountdata, const char *devname,
                         struct smb_vol *vol)
 {
-       char *value, *data, *end;
+       char *data, *end;
        char *mountdata_copy = NULL, *options;
-       int err;
        unsigned int  temp_len, i, j;
        char separator[2];
        short int override_uid = -1;
        short int override_gid = -1;
        bool uid_specified = false;
        bool gid_specified = false;
+       bool sloppy = false;
+       char *invalid = NULL;
        char *nodename = utsname()->nodename;
+       char *string = NULL;
+       char *tmp_end, *value;
+       char delim;
 
        separator[0] = ',';
        separator[1] = 0;
+       delim = separator[0];
 
        /*
         * does not have to be perfect mapping since field is
@@ -975,6 +1245,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 
        options = mountdata_copy;
        end = options + strlen(options);
+
        if (strncmp(options, "sep=", 4) == 0) {
                if (options[4] != 0) {
                        separator[0] = options[4];
@@ -987,609 +1258,603 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        vol->backupgid_specified = false; /* no backup intent for a group */
 
        while ((data = strsep(&options, separator)) != NULL) {
+               substring_t args[MAX_OPT_ARGS];
+               unsigned long option;
+               int token;
+
                if (!*data)
                        continue;
-               if ((value = strchr(data, '=')) != NULL)
-                       *value++ = '\0';
 
-               /* Have to parse this before we parse for "user" */
-               if (strnicmp(data, "user_xattr", 10) == 0) {
+               token = match_token(data, cifs_mount_option_tokens, args);
+
+               switch (token) {
+
+               /* Ingnore the following */
+               case Opt_ignore:
+                       break;
+
+               /* Boolean values */
+               case Opt_user_xattr:
                        vol->no_xattr = 0;
-               } else if (strnicmp(data, "nouser_xattr", 12) == 0) {
+                       break;
+               case Opt_nouser_xattr:
                        vol->no_xattr = 1;
-               } else if (strnicmp(data, "user", 4) == 0) {
-                       if (!value) {
-                               printk(KERN_WARNING
-                                      "CIFS: invalid or missing username\n");
-                               goto cifs_parse_mount_err;
-                       } else if (!*value) {
-                               /* null user, ie anonymous, authentication */
-                               vol->nullauth = 1;
-                       }
-                       if (strnlen(value, MAX_USERNAME_SIZE) <
-                                               MAX_USERNAME_SIZE) {
-                               vol->username = kstrdup(value, GFP_KERNEL);
-                               if (!vol->username) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for username\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                       } else {
-                               printk(KERN_WARNING "CIFS: username too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "pass", 4) == 0) {
-                       if (!value) {
-                               vol->password = NULL;
-                               continue;
-                       } else if (value[0] == 0) {
-                               /* check if string begins with double comma
-                                  since that would mean the password really
-                                  does start with a comma, and would not
-                                  indicate an empty string */
-                               if (value[1] != separator[0]) {
-                                       vol->password = NULL;
-                                       continue;
-                               }
-                       }
-                       temp_len = strlen(value);
-                       /* removed password length check, NTLM passwords
-                               can be arbitrarily long */
-
-                       /* if comma in password, the string will be
-                       prematurely null terminated.  Commas in password are
-                       specified across the cifs mount interface by a double
-                       comma ie ,, and a comma used as in other cases ie ','
-                       as a parameter delimiter/separator is single and due
-                       to the strsep above is temporarily zeroed. */
-
-                       /* NB: password legally can have multiple commas and
-                       the only illegal character in a password is null */
-
-                       if ((value[temp_len] == 0) &&
-                           (value + temp_len < end) &&
-                           (value[temp_len+1] == separator[0])) {
-                               /* reinsert comma */
-                               value[temp_len] = separator[0];
-                               temp_len += 2;  /* move after second comma */
-                               while (value[temp_len] != 0)  {
-                                       if (value[temp_len] == separator[0]) {
-                                               if (value[temp_len+1] ==
-                                                    separator[0]) {
-                                               /* skip second comma */
-                                                       temp_len++;
-                                               } else {
-                                               /* single comma indicating start
-                                                        of next parm */
-                                                       break;
-                                               }
-                                       }
-                                       temp_len++;
-                               }
-                               if (value[temp_len] == 0) {
-                                       options = NULL;
-                               } else {
-                                       value[temp_len] = 0;
-                                       /* point option to start of next parm */
-                                       options = value + temp_len + 1;
-                               }
-                               /* go from value to value + temp_len condensing
-                               double commas to singles. Note that this ends up
-                               allocating a few bytes too many, which is ok */
-                               vol->password = kzalloc(temp_len, GFP_KERNEL);
-                               if (vol->password == NULL) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for password\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                               for (i = 0, j = 0; i < temp_len; i++, j++) {
-                                       vol->password[j] = value[i];
-                                       if (value[i] == separator[0]
-                                               && value[i+1] == separator[0]) {
-                                               /* skip second comma */
-                                               i++;
-                                       }
-                               }
-                               vol->password[j] = 0;
-                       } else {
-                               vol->password = kzalloc(temp_len+1, GFP_KERNEL);
-                               if (vol->password == NULL) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for password\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                               strcpy(vol->password, value);
-                       }
-               } else if (!strnicmp(data, "ip", 2) ||
-                          !strnicmp(data, "addr", 4)) {
-                       if (!value || !*value) {
-                               vol->UNCip = NULL;
-                       } else if (strnlen(value, INET6_ADDRSTRLEN) <
-                                                       INET6_ADDRSTRLEN) {
-                               vol->UNCip = kstrdup(value, GFP_KERNEL);
-                               if (!vol->UNCip) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for UNC IP\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                       } else {
-                               printk(KERN_WARNING "CIFS: ip address "
-                                                   "too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "sec", 3) == 0) {
-                       if (!value || !*value) {
-                               cERROR(1, "no security value specified");
-                               continue;
-                       } else if (strnicmp(value, "krb5i", 5) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_KRB5 |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "krb5p", 5) == 0) {
-                               /* vol->secFlg |= CIFSSEC_MUST_SEAL |
-                                       CIFSSEC_MAY_KRB5; */
-                               cERROR(1, "Krb5 cifs privacy not supported");
-                               goto cifs_parse_mount_err;
-                       } else if (strnicmp(value, "krb5", 4) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_KRB5;
-                       } else if (strnicmp(value, "ntlmsspi", 8) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMSSP |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "ntlmssp", 7) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMSSP;
-                       } else if (strnicmp(value, "ntlmv2i", 7) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "ntlmv2", 6) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
-                       } else if (strnicmp(value, "ntlmi", 5) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_NTLM |
-                                       CIFSSEC_MUST_SIGN;
-                       } else if (strnicmp(value, "ntlm", 4) == 0) {
-                               /* ntlm is default so can be turned off too */
-                               vol->secFlg |= CIFSSEC_MAY_NTLM;
-                       } else if (strnicmp(value, "nontlm", 6) == 0) {
-                               /* BB is there a better way to do this? */
-                               vol->secFlg |= CIFSSEC_MAY_NTLMV2;
-#ifdef CONFIG_CIFS_WEAK_PW_HASH
-                       } else if (strnicmp(value, "lanman", 6) == 0) {
-                               vol->secFlg |= CIFSSEC_MAY_LANMAN;
-#endif
-                       } else if (strnicmp(value, "none", 4) == 0) {
-                               vol->nullauth = 1;
-                       } else {
-                               cERROR(1, "bad security option: %s", value);
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "vers", 3) == 0) {
-                       if (!value || !*value) {
-                               cERROR(1, "no protocol version specified"
-                                         " after vers= mount option");
-                       } else if ((strnicmp(value, "cifs", 4) == 0) ||
-                                  (strnicmp(value, "1", 1) == 0)) {
-                               /* this is the default */
-                               continue;
-                       }
-               } else if ((strnicmp(data, "unc", 3) == 0)
-                          || (strnicmp(data, "target", 6) == 0)
-                          || (strnicmp(data, "path", 4) == 0)) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: invalid path to "
-                                                   "network resource\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       if ((temp_len = strnlen(value, 300)) < 300) {
-                               vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
-                               if (vol->UNC == NULL)
-                                       goto cifs_parse_mount_err;
-                               strcpy(vol->UNC, value);
-                               if (strncmp(vol->UNC, "//", 2) == 0) {
-                                       vol->UNC[0] = '\\';
-                                       vol->UNC[1] = '\\';
-                               } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
-                                       printk(KERN_WARNING
-                                              "CIFS: UNC Path does not begin "
-                                              "with // or \\\\ \n");
-                                       goto cifs_parse_mount_err;
-                               }
-                       } else {
-                               printk(KERN_WARNING "CIFS: UNC name too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if ((strnicmp(data, "domain", 3) == 0)
-                          || (strnicmp(data, "workgroup", 5) == 0)) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: invalid domain name\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       /* BB are there cases in which a comma can be valid in
-                       a domain name and need special handling? */
-                       if (strnlen(value, 256) < 256) {
-                               vol->domainname = kstrdup(value, GFP_KERNEL);
-                               if (!vol->domainname) {
-                                       printk(KERN_WARNING "CIFS: no memory "
-                                                           "for domainname\n");
-                                       goto cifs_parse_mount_err;
-                               }
-                               cFYI(1, "Domain name set");
-                       } else {
-                               printk(KERN_WARNING "CIFS: domain name too "
-                                                   "long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "srcaddr", 7) == 0) {
-                       vol->srcaddr.ss_family = AF_UNSPEC;
-
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: srcaddr value"
-                                      " not specified.\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       i = cifs_convert_address((struct sockaddr *)&vol->srcaddr,
-                                                value, strlen(value));
-                       if (i == 0) {
-                               printk(KERN_WARNING "CIFS:  Could not parse"
-                                      " srcaddr: %s\n",
-                                      value);
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "prefixpath", 10) == 0) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING
-                                       "CIFS: invalid path prefix\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       if ((temp_len = strnlen(value, 1024)) < 1024) {
-                               if (value[0] != '/')
-                                       temp_len++;  /* missing leading slash */
-                               vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
-                               if (vol->prepath == NULL)
-                                       goto cifs_parse_mount_err;
-                               if (value[0] != '/') {
-                                       vol->prepath[0] = '/';
-                                       strcpy(vol->prepath+1, value);
-                               } else
-                                       strcpy(vol->prepath, value);
-                               cFYI(1, "prefix path %s", vol->prepath);
-                       } else {
-                               printk(KERN_WARNING "CIFS: prefix too long\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (strnicmp(data, "iocharset", 9) == 0) {
-                       if (!value || !*value) {
-                               printk(KERN_WARNING "CIFS: invalid iocharset "
-                                                   "specified\n");
-                               goto cifs_parse_mount_err;
-                       }
-                       if (strnlen(value, 65) < 65) {
-                               if (strnicmp(value, "default", 7)) {
-                                       vol->iocharset = kstrdup(value,
-                                                                GFP_KERNEL);
-
-                                       if (!vol->iocharset) {
-                                               printk(KERN_WARNING "CIFS: no "
-                                                                  "memory for"
-                                                                  "charset\n");
-                                               goto cifs_parse_mount_err;
-                                       }
-                               }
-                               /* if iocharset not set then load_nls_default
-                                  is used by caller */
-                               cFYI(1, "iocharset set to %s", value);
-                       } else {
-                               printk(KERN_WARNING "CIFS: iocharset name "
-                                                   "too long.\n");
-                               goto cifs_parse_mount_err;
-                       }
-               } else if (!strnicmp(data, "uid", 3) && value && *value) {
-                       vol->linux_uid = simple_strtoul(value, &value, 0);
-                       uid_specified = true;
-               } else if (!strnicmp(data, "cruid", 5) && value && *value) {
-                       vol->cred_uid = simple_strtoul(value, &value, 0);
-               } else if (!strnicmp(data, "forceuid", 8)) {
+                       break;
+               case Opt_forceuid:
                        override_uid = 1;
-               } else if (!strnicmp(data, "noforceuid", 10)) {
+                       break;
+               case Opt_noforceuid:
                        override_uid = 0;
-               } else if (!strnicmp(data, "gid", 3) && value && *value) {
-                       vol->linux_gid = simple_strtoul(value, &value, 0);
-                       gid_specified = true;
-               } else if (!strnicmp(data, "forcegid", 8)) {
-                       override_gid = 1;
-               } else if (!strnicmp(data, "noforcegid", 10)) {
-                       override_gid = 0;
-               } else if (strnicmp(data, "file_mode", 4) == 0) {
-                       if (value && *value) {
-                               vol->file_mode =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "dir_mode", 4) == 0) {
-                       if (value && *value) {
-                               vol->dir_mode =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "dirmode", 4) == 0) {
-                       if (value && *value) {
-                               vol->dir_mode =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "port", 4) == 0) {
-                       if (value && *value) {
-                               vol->port =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "rsize", 5) == 0) {
-                       if (value && *value) {
-                               vol->rsize =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "wsize", 5) == 0) {
-                       if (value && *value) {
-                               vol->wsize =
-                                       simple_strtoul(value, &value, 0);
-                       }
-               } else if (strnicmp(data, "sockopt", 5) == 0) {
-                       if (!value || !*value) {
-                               cERROR(1, "no socket option specified");
-                               continue;
-                       } else if (strnicmp(value, "TCP_NODELAY", 11) == 0) {
-                               vol->sockopt_tcp_nodelay = 1;
-                       }
-               } else if (strnicmp(data, "netbiosname", 4) == 0) {
-                       if (!value || !*value || (*value == ' ')) {
-                               cFYI(1, "invalid (empty) netbiosname");
-                       } else {
-                               memset(vol->source_rfc1001_name, 0x20,
-                                       RFC1001_NAME_LEN);
-                               /*
-                                * FIXME: are there cases in which a comma can
-                                * be valid in workstation netbios name (and
-                                * need special handling)?
-                                */
-                               for (i = 0; i < RFC1001_NAME_LEN; i++) {
-                                       /* don't ucase netbiosname for user */
-                                       if (value[i] == 0)
-                                               break;
-                                       vol->source_rfc1001_name[i] = value[i];
-                               }
-                               /* The string has 16th byte zero still from
-                               set at top of the function  */
-                               if (i == RFC1001_NAME_LEN && value[i] != 0)
-                                       printk(KERN_WARNING "CIFS: netbiosname"
-                                               " longer than 15 truncated.\n");
-                       }
-               } else if (strnicmp(data, "servern", 7) == 0) {
-                       /* servernetbiosname specified override *SMBSERVER */
-                       if (!value || !*value || (*value == ' ')) {
-                               cFYI(1, "empty server netbiosname specified");
-                       } else {
-                               /* last byte, type, is 0x20 for servr type */
-                               memset(vol->target_rfc1001_name, 0x20,
-                                       RFC1001_NAME_LEN_WITH_NULL);
-
-                               for (i = 0; i < 15; i++) {
-                               /* BB are there cases in which a comma can be
-                                  valid in this workstation netbios name
-                                  (and need special handling)? */
-
-                               /* user or mount helper must uppercase
-                                  the netbiosname */
-                                       if (value[i] == 0)
-                                               break;
-                                       else
-                                               vol->target_rfc1001_name[i] =
-                                                               value[i];
-                               }
-                               /* The string has 16th byte zero still from
-                                  set at top of the function  */
-                               if (i == RFC1001_NAME_LEN && value[i] != 0)
-                                       printk(KERN_WARNING "CIFS: server net"
-                                       "biosname longer than 15 truncated.\n");
-                       }
-               } else if (strnicmp(data, "actimeo", 7) == 0) {
-                       if (value && *value) {
-                               vol->actimeo = HZ * simple_strtoul(value,
-                                                                  &value, 0);
-                               if (vol->actimeo > CIFS_MAX_ACTIMEO) {
-                                       cERROR(1, "CIFS: attribute cache"
-                                                       "timeout too large");
-                                       goto cifs_parse_mount_err;
-                               }
-                       }
-               } else if (strnicmp(data, "credentials", 4) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "version", 3) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "guest", 5) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "rw", 2) == 0 && strlen(data) == 2) {
-                       /* ignore */
-               } else if (strnicmp(data, "ro", 2) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "noblocksend", 11) == 0) {
+                       break;
+               case Opt_noblocksend:
                        vol->noblocksnd = 1;
-               } else if (strnicmp(data, "noautotune", 10) == 0) {
+                       break;
+               case Opt_noautotune:
                        vol->noautotune = 1;
-               } else if ((strnicmp(data, "suid", 4) == 0) ||
-                                  (strnicmp(data, "nosuid", 6) == 0) ||
-                                  (strnicmp(data, "exec", 4) == 0) ||
-                                  (strnicmp(data, "noexec", 6) == 0) ||
-                                  (strnicmp(data, "nodev", 5) == 0) ||
-                                  (strnicmp(data, "noauto", 6) == 0) ||
-                                  (strnicmp(data, "dev", 3) == 0)) {
-                       /*  The mount tool or mount.cifs helper (if present)
-                           uses these opts to set flags, and the flags are read
-                           by the kernel vfs layer before we get here (ie
-                           before read super) so there is no point trying to
-                           parse these options again and set anything and it
-                           is ok to just ignore them */
-                       continue;
-               } else if (strnicmp(data, "hard", 4) == 0) {
+                       break;
+               case Opt_hard:
                        vol->retry = 1;
-               } else if (strnicmp(data, "soft", 4) == 0) {
+                       break;
+               case Opt_soft:
                        vol->retry = 0;
-               } else if (strnicmp(data, "perm", 4) == 0) {
+                       break;
+               case Opt_perm:
                        vol->noperm = 0;
-               } else if (strnicmp(data, "noperm", 6) == 0) {
+                       break;
+               case Opt_noperm:
                        vol->noperm = 1;
-               } else if (strnicmp(data, "mapchars", 8) == 0) {
+                       break;
+               case Opt_mapchars:
                        vol->remap = 1;
-               } else if (strnicmp(data, "nomapchars", 10) == 0) {
+                       break;
+               case Opt_nomapchars:
                        vol->remap = 0;
-               } else if (strnicmp(data, "sfu", 3) == 0) {
+                       break;
+               case Opt_sfu:
                        vol->sfu_emul = 1;
-               } else if (strnicmp(data, "nosfu", 5) == 0) {
+                       break;
+               case Opt_nosfu:
                        vol->sfu_emul = 0;
-               } else if (strnicmp(data, "nodfs", 5) == 0) {
+                       break;
+               case Opt_nodfs:
                        vol->nodfs = 1;
-               } else if (strnicmp(data, "posixpaths", 10) == 0) {
+                       break;
+               case Opt_posixpaths:
                        vol->posix_paths = 1;
-               } else if (strnicmp(data, "noposixpaths", 12) == 0) {
+                       break;
+               case Opt_noposixpaths:
                        vol->posix_paths = 0;
-               } else if (strnicmp(data, "nounix", 6) == 0) {
-                       vol->no_linux_ext = 1;
-               } else if (strnicmp(data, "nolinux", 7) == 0) {
+                       break;
+               case Opt_nounix:
                        vol->no_linux_ext = 1;
-               } else if ((strnicmp(data, "nocase", 6) == 0) ||
-                          (strnicmp(data, "ignorecase", 10)  == 0)) {
+                       break;
+               case Opt_nocase:
                        vol->nocase = 1;
-               } else if (strnicmp(data, "mand", 4) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "nomand", 6) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "_netdev", 7) == 0) {
-                       /* ignore */
-               } else if (strnicmp(data, "brl", 3) == 0) {
+                       break;
+               case Opt_brl:
                        vol->nobrl =  0;
-               } else if ((strnicmp(data, "nobrl", 5) == 0) ||
-                          (strnicmp(data, "nolock", 6) == 0)) {
+                       break;
+               case Opt_nobrl:
                        vol->nobrl =  1;
-                       /* turn off mandatory locking in mode
-                       if remote locking is turned off since the
-                       local vfs will do advisory */
+                       /*
+                        * turn off mandatory locking in mode
+                        * if remote locking is turned off since the
+                        * local vfs will do advisory
+                        */
                        if (vol->file_mode ==
                                (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
                                vol->file_mode = S_IALLUGO;
-               } else if (strnicmp(data, "forcemandatorylock", 9) == 0) {
-                       /* will take the shorter form "forcemand" as well */
-                       /* This mount option will force use of mandatory
-                         (DOS/Windows style) byte range locks, instead of
-                         using posix advisory byte range locks, even if the
-                         Unix extensions are available and posix locks would
-                         be supported otherwise. If Unix extensions are not
-                         negotiated this has no effect since mandatory locks
-                         would be used (mandatory locks is all that those
-                         those servers support) */
+                       break;
+               case Opt_forcemandatorylock:
                        vol->mand_lock = 1;
-               } else if (strnicmp(data, "setuids", 7) == 0) {
+                       break;
+               case Opt_setuids:
                        vol->setuids = 1;
-               } else if (strnicmp(data, "nosetuids", 9) == 0) {
+                       break;
+               case Opt_nosetuids:
                        vol->setuids = 0;
-               } else if (strnicmp(data, "dynperm", 7) == 0) {
+                       break;
+               case Opt_dynperm:
                        vol->dynperm = true;
-               } else if (strnicmp(data, "nodynperm", 9) == 0) {
+                       break;
+               case Opt_nodynperm:
                        vol->dynperm = false;
-               } else if (strnicmp(data, "nohard", 6) == 0) {
+                       break;
+               case Opt_nohard:
                        vol->retry = 0;
-               } else if (strnicmp(data, "nosoft", 6) == 0) {
+                       break;
+               case Opt_nosoft:
                        vol->retry = 1;
-               } else if (strnicmp(data, "nointr", 6) == 0) {
+                       break;
+               case Opt_nointr:
                        vol->intr = 0;
-               } else if (strnicmp(data, "intr", 4) == 0) {
+                       break;
+               case Opt_intr:
                        vol->intr = 1;
-               } else if (strnicmp(data, "nostrictsync", 12) == 0) {
+                       break;
+               case Opt_nostrictsync:
                        vol->nostrictsync = 1;
-               } else if (strnicmp(data, "strictsync", 10) == 0) {
+                       break;
+               case Opt_strictsync:
                        vol->nostrictsync = 0;
-               } else if (strnicmp(data, "serverino", 7) == 0) {
+                       break;
+               case Opt_serverino:
                        vol->server_ino = 1;
-               } else if (strnicmp(data, "noserverino", 9) == 0) {
+                       break;
+               case Opt_noserverino:
                        vol->server_ino = 0;
-               } else if (strnicmp(data, "rwpidforward", 12) == 0) {
+                       break;
+               case Opt_rwpidforward:
                        vol->rwpidforward = 1;
-               } else if (strnicmp(data, "cifsacl", 7) == 0) {
+                       break;
+               case Opt_cifsacl:
                        vol->cifs_acl = 1;
-               } else if (strnicmp(data, "nocifsacl", 9) == 0) {
+                       break;
+               case Opt_nocifsacl:
                        vol->cifs_acl = 0;
-               } else if (strnicmp(data, "acl", 3) == 0) {
+                       break;
+               case Opt_acl:
                        vol->no_psx_acl = 0;
-               } else if (strnicmp(data, "noacl", 5) == 0) {
+                       break;
+               case Opt_noacl:
                        vol->no_psx_acl = 1;
-               } else if (strnicmp(data, "locallease", 6) == 0) {
+                       break;
+               case Opt_locallease:
                        vol->local_lease = 1;
-               } else if (strnicmp(data, "sign", 4) == 0) {
+                       break;
+               case Opt_sign:
                        vol->secFlg |= CIFSSEC_MUST_SIGN;
-               } else if (strnicmp(data, "seal", 4) == 0) {
+                       break;
+               case Opt_seal:
                        /* we do not do the following in secFlags because seal
-                          is a per tree connection (mount) not a per socket
-                          or per-smb connection option in the protocol */
-                       /* vol->secFlg |= CIFSSEC_MUST_SEAL; */
+                        * is a per tree connection (mount) not a per socket
+                        * or per-smb connection option in the protocol
+                        * vol->secFlg |= CIFSSEC_MUST_SEAL;
+                        */
                        vol->seal = 1;
-               } else if (strnicmp(data, "direct", 6) == 0) {
-                       vol->direct_io = 1;
-               } else if (strnicmp(data, "forcedirectio", 13) == 0) {
+                       break;
+               case Opt_direct:
                        vol->direct_io = 1;
-               } else if (strnicmp(data, "strictcache", 11) == 0) {
+                       break;
+               case Opt_strictcache:
                        vol->strict_io = 1;
-               } else if (strnicmp(data, "noac", 4) == 0) {
+                       break;
+               case Opt_noac:
                        printk(KERN_WARNING "CIFS: Mount option noac not "
                                "supported. Instead set "
                                "/proc/fs/cifs/LookupCacheEnabled to 0\n");
-               } else if (strnicmp(data, "fsc", 3) == 0) {
+                       break;
+               case Opt_fsc:
 #ifndef CONFIG_CIFS_FSCACHE
                        cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE "
                                  "kernel config option set");
                        goto cifs_parse_mount_err;
 #endif
                        vol->fsc = true;
-               } else if (strnicmp(data, "mfsymlinks", 10) == 0) {
+                       break;
+               case Opt_mfsymlinks:
                        vol->mfsymlinks = true;
-               } else if (strnicmp(data, "multiuser", 8) == 0) {
+                       break;
+               case Opt_multiuser:
                        vol->multiuser = true;
-               } else if (!strnicmp(data, "backupuid", 9) && value && *value) {
-                       err = kstrtouint(value, 0, &vol->backupuid);
-                       if (err < 0) {
+                       break;
+               case Opt_sloppy:
+                       sloppy = true;
+                       break;
+
+               /* Numeric Values */
+               case Opt_backupuid:
+                       if (get_option_ul(args, &option)) {
                                cERROR(1, "%s: Invalid backupuid value",
                                        __func__);
                                goto cifs_parse_mount_err;
                        }
+                       vol->backupuid = option;
                        vol->backupuid_specified = true;
-               } else if (!strnicmp(data, "backupgid", 9) && value && *value) {
-                       err = kstrtouint(value, 0, &vol->backupgid);
-                       if (err < 0) {
+                       break;
+               case Opt_backupgid:
+                       if (get_option_ul(args, &option)) {
                                cERROR(1, "%s: Invalid backupgid value",
                                        __func__);
                                goto cifs_parse_mount_err;
                        }
+                       vol->backupgid = option;
                        vol->backupgid_specified = true;
-               } else
-                       printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
-                                               data);
-       }
-       if (vol->UNC == NULL) {
-               if (devname == NULL) {
-                       printk(KERN_WARNING "CIFS: Missing UNC name for mount "
-                                               "target\n");
-                       goto cifs_parse_mount_err;
-               }
-               if ((temp_len = strnlen(devname, 300)) < 300) {
+                       break;
+               case Opt_uid:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid uid value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->linux_uid = option;
+                       uid_specified = true;
+                       break;
+               case Opt_cruid:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid cruid value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->cred_uid = option;
+                       break;
+               case Opt_gid:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid gid value",
+                                               __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->linux_gid = option;
+                       gid_specified = true;
+                       break;
+               case Opt_file_mode:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid file_mode value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->file_mode = option;
+                       break;
+               case Opt_dirmode:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid dir_mode value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->dir_mode = option;
+                       break;
+               case Opt_port:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid port value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->port = option;
+                       break;
+               case Opt_rsize:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid rsize value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->rsize = option;
+                       break;
+               case Opt_wsize:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid wsize value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->wsize = option;
+                       break;
+               case Opt_actimeo:
+                       if (get_option_ul(args, &option)) {
+                               cERROR(1, "%s: Invalid actimeo value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->actimeo = HZ * option;
+                       if (vol->actimeo > CIFS_MAX_ACTIMEO) {
+                               cERROR(1, "CIFS: attribute cache"
+                                         "timeout too large");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+
+               /* String Arguments */
+
+               case Opt_blank_user:
+                       /* null user, ie. anonymous authentication */
+                       vol->nullauth = 1;
+                       vol->username = NULL;
+                       break;
+               case Opt_user:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (strnlen(string, MAX_USERNAME_SIZE) >
+                                                       MAX_USERNAME_SIZE) {
+                               printk(KERN_WARNING "CIFS: username too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->username = kstrdup(string, GFP_KERNEL);
+                       if (!vol->username) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for username\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_blank_pass:
+                       vol->password = NULL;
+                       break;
+               case Opt_pass:
+                       /* passwords have to be handled differently
+                        * to allow the character used for deliminator
+                        * to be passed within them
+                        */
+
+                       /* Obtain the value string */
+                       value = strchr(data, '=');
+                       value++;
+
+                       /* Set tmp_end to end of the string */
+                       tmp_end = (char *) value + strlen(value);
+
+                       /* Check if following character is the deliminator
+                        * If yes, we have encountered a double deliminator
+                        * reset the NULL character to the deliminator
+                        */
+                       if (tmp_end < end && tmp_end[1] == delim)
+                               tmp_end[0] = delim;
+
+                       /* Keep iterating until we get to a single deliminator
+                        * OR the end
+                        */
+                       while ((tmp_end = strchr(tmp_end, delim)) != NULL &&
+                              (tmp_end[1] == delim)) {
+                               tmp_end = (char *) &tmp_end[2];
+                       }
+
+                       /* Reset var options to point to next element */
+                       if (tmp_end) {
+                               tmp_end[0] = '\0';
+                               options = (char *) &tmp_end[1];
+                       } else
+                               /* Reached the end of the mount option string */
+                               options = end;
+
+                       /* Now build new password string */
+                       temp_len = strlen(value);
+                       vol->password = kzalloc(temp_len+1, GFP_KERNEL);
+                       if (vol->password == NULL) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for password\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       for (i = 0, j = 0; i < temp_len; i++, j++) {
+                               vol->password[j] = value[i];
+                               if ((value[i] == delim) &&
+                                    value[i+1] == delim)
+                                       /* skip the second deliminator */
+                                       i++;
+                       }
+                       vol->password[j] = '\0';
+                       break;
+               case Opt_blank_ip:
+                       vol->UNCip = NULL;
+                       break;
+               case Opt_ip:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (strnlen(string, INET6_ADDRSTRLEN) >
+                                               INET6_ADDRSTRLEN) {
+                               printk(KERN_WARNING "CIFS: ip address "
+                                                   "too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->UNCip = kstrdup(string, GFP_KERNEL);
+                       if (!vol->UNCip) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for UNC IP\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_unc:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       temp_len = strnlen(string, 300);
+                       if (temp_len  == 300) {
+                               printk(KERN_WARNING "CIFS: UNC name too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+
                        vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
-                       if (vol->UNC == NULL)
+                       if (vol->UNC == NULL) {
+                               printk(KERN_WARNING "CIFS: no memory for UNC\n");
                                goto cifs_parse_mount_err;
-                       strcpy(vol->UNC, devname);
-                       if (strncmp(vol->UNC, "//", 2) == 0) {
+                       }
+                       strcpy(vol->UNC, string);
+
+                       if (strncmp(string, "//", 2) == 0) {
                                vol->UNC[0] = '\\';
                                vol->UNC[1] = '\\';
-                       } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
+                       } else if (strncmp(string, "\\\\", 2) != 0) {
                                printk(KERN_WARNING "CIFS: UNC Path does not "
-                                                   "begin with // or \\\\ \n");
+                                                   "begin with // or \\\\\n");
                                goto cifs_parse_mount_err;
                        }
-                       value = strpbrk(vol->UNC+2, "/\\");
-                       if (value)
-                               *value = '\\';
-               } else {
-                       printk(KERN_WARNING "CIFS: UNC name too long\n");
+
+                       break;
+               case Opt_domain:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (strnlen(string, 256) == 256) {
+                               printk(KERN_WARNING "CIFS: domain name too"
+                                                   " long\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       vol->domainname = kstrdup(string, GFP_KERNEL);
+                       if (!vol->domainname) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for domainname\n");
+                               goto cifs_parse_mount_err;
+                       }
+                       cFYI(1, "Domain name set");
+                       break;
+               case Opt_srcaddr:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (!cifs_convert_address(
+                                       (struct sockaddr *)&vol->srcaddr,
+                                       string, strlen(string))) {
+                               printk(KERN_WARNING "CIFS:  Could not parse"
+                                                   " srcaddr: %s\n", string);
+                               goto cifs_parse_mount_err;
+                       }
+                       break;
+               case Opt_prefixpath:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       temp_len = strnlen(string, 1024);
+                       if (string[0] != '/')
+                               temp_len++; /* missing leading slash */
+                       if (temp_len > 1024) {
+                               printk(KERN_WARNING "CIFS: prefix too long\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
+                       if (vol->prepath == NULL) {
+                               printk(KERN_WARNING "CIFS: no memory "
+                                                   "for path prefix\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                       if (string[0] != '/') {
+                               vol->prepath[0] = '/';
+                               strcpy(vol->prepath+1, string);
+                       } else
+                               strcpy(vol->prepath, string);
+
+                       break;
+               case Opt_iocharset:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (strnlen(string, 1024) >= 65) {
+                               printk(KERN_WARNING "CIFS: iocharset name "
+                                                   "too long.\n");
+                               goto cifs_parse_mount_err;
+                       }
+
+                        if (strnicmp(string, "default", 7) != 0) {
+                               vol->iocharset = kstrdup(string,
+                                                        GFP_KERNEL);
+                               if (!vol->iocharset) {
+                                       printk(KERN_WARNING "CIFS: no memory"
+                                                           "for charset\n");
+                                       goto cifs_parse_mount_err;
+                               }
+                       }
+                       /* if iocharset not set then load_nls_default
+                        * is used by caller
+                        */
+                       cFYI(1, "iocharset set to %s", string);
+                       break;
+               case Opt_sockopt:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (strnicmp(string, "TCP_NODELAY", 11) == 0)
+                               vol->sockopt_tcp_nodelay = 1;
+                       break;
+               case Opt_netbiosname:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       memset(vol->source_rfc1001_name, 0x20,
+                               RFC1001_NAME_LEN);
+                       /*
+                        * FIXME: are there cases in which a comma can
+                        * be valid in workstation netbios name (and
+                        * need special handling)?
+                        */
+                       for (i = 0; i < RFC1001_NAME_LEN; i++) {
+                               /* don't ucase netbiosname for user */
+                               if (string[i] == 0)
+                                       break;
+                               vol->source_rfc1001_name[i] = string[i];
+                       }
+                       /* The string has 16th byte zero still from
+                        * set at top of the function
+                        */
+                       if (i == RFC1001_NAME_LEN && string[i] != 0)
+                               printk(KERN_WARNING "CIFS: netbiosname"
+                                      " longer than 15 truncated.\n");
+
+                       break;
+               case Opt_servern:
+                       /* servernetbiosname specified override *SMBSERVER */
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       /* last byte, type, is 0x20 for servr type */
+                       memset(vol->target_rfc1001_name, 0x20,
+                               RFC1001_NAME_LEN_WITH_NULL);
+
+                       /* BB are there cases in which a comma can be
+                          valid in this workstation netbios name
+                          (and need special handling)? */
+
+                       /* user or mount helper must uppercase the
+                          netbios name */
+                       for (i = 0; i < 15; i++) {
+                               if (string[i] == 0)
+                                       break;
+                               vol->target_rfc1001_name[i] = string[i];
+                       }
+                       /* The string has 16th byte zero still from
+                          set at top of the function  */
+                       if (i == RFC1001_NAME_LEN && string[i] != 0)
+                               printk(KERN_WARNING "CIFS: server net"
+                                      "biosname longer than 15 truncated.\n");
+                       break;
+               case Opt_ver:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (strnicmp(string, "cifs", 4) == 0 ||
+                           strnicmp(string, "1", 1) == 0) {
+                               /* This is the default */
+                               break;
+                       }
+                       /* For all other value, error */
+                       printk(KERN_WARNING "CIFS: Invalid version"
+                                           " specified\n");
                        goto cifs_parse_mount_err;
+               case Opt_sec:
+                       string = match_strdup(args);
+                       if (string == NULL)
+                               goto out_nomem;
+
+                       if (cifs_parse_security_flavors(string, vol) != 0)
+                               goto cifs_parse_mount_err;
+                       break;
+               default:
+                       /*
+                        * An option we don't recognize. Save it off for later
+                        * if we haven't already found one
+                        */
+                       if (!invalid)
+                               invalid = data;
+                       break;
                }
+               /* Free up any allocated string */
+               kfree(string);
+               string = NULL;
+       }
+
+       if (!sloppy && invalid) {
+               printk(KERN_ERR "CIFS: Unknown mount option \"%s\"\n", invalid);
+               goto cifs_parse_mount_err;
        }
 
 #ifndef CONFIG_KEYS
@@ -1619,7 +1884,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        kfree(mountdata_copy);
        return 0;
 
+out_nomem:
+       printk(KERN_WARNING "Could not allocate temporary buffer\n");
 cifs_parse_mount_err:
+       kfree(string);
        kfree(mountdata_copy);
        return 1;
 }
@@ -1918,6 +2186,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        tcp_ses->session_estab = false;
        tcp_ses->sequence_number = 0;
        tcp_ses->lstrp = jiffies;
+       spin_lock_init(&tcp_ses->req_lock);
        INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
        INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
        INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
@@ -2963,10 +3232,6 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
 
        cifs_sb->mnt_uid = pvolume_info->linux_uid;
        cifs_sb->mnt_gid = pvolume_info->linux_gid;
-       if (pvolume_info->backupuid_specified)
-               cifs_sb->mnt_backupuid = pvolume_info->backupuid;
-       if (pvolume_info->backupgid_specified)
-               cifs_sb->mnt_backupgid = pvolume_info->backupgid;
        cifs_sb->mnt_file_mode = pvolume_info->file_mode;
        cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
        cFYI(1, "file mode: 0x%hx  dir mode: 0x%hx",
@@ -2997,10 +3262,14 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;
        if (pvolume_info->cifs_acl)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
-       if (pvolume_info->backupuid_specified)
+       if (pvolume_info->backupuid_specified) {
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID;
-       if (pvolume_info->backupgid_specified)
+               cifs_sb->mnt_backupuid = pvolume_info->backupuid;
+       }
+       if (pvolume_info->backupgid_specified) {
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID;
+               cifs_sb->mnt_backupgid = pvolume_info->backupgid;
+       }
        if (pvolume_info->override_uid)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
        if (pvolume_info->override_gid)
@@ -3349,22 +3618,6 @@ cifs_get_volume_info(char *mount_data, const char *devname)
        return volume_info;
 }
 
-/* make sure ra_pages is a multiple of rsize */
-static inline unsigned int
-cifs_ra_pages(struct cifs_sb_info *cifs_sb)
-{
-       unsigned int reads;
-       unsigned int rsize_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;
-
-       if (rsize_pages >= default_backing_dev_info.ra_pages)
-               return default_backing_dev_info.ra_pages;
-       else if (rsize_pages == 0)
-               return rsize_pages;
-
-       reads = default_backing_dev_info.ra_pages / rsize_pages;
-       return reads * rsize_pages;
-}
-
 int
 cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
 {
@@ -3452,7 +3705,7 @@ try_mount_again:
        cifs_sb->rsize = cifs_negotiate_rsize(tcon, volume_info);
 
        /* tune readahead according to rsize */
-       cifs_sb->bdi.ra_pages = cifs_ra_pages(cifs_sb);
+       cifs_sb->bdi.ra_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;
 
 remote_path_check:
 #ifdef CONFIG_CIFS_DFS_UPCALL