cifs: parse the device name into UNC and prepath
Jeff Layton [Mon, 10 Dec 2012 11:10:46 +0000 (06:10 -0500)]
This should fix a regression that was introduced when the new mount
option parser went in. Also, when the unc= and prefixpath= options
are provided, check their values against the ones we parsed from
the device string. If they differ, then throw a warning that tells
the user that we're using the values from the unc= option for now,
but that that will change in 3.10.

Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>

fs/cifs/connect.c

index 94c4484..7635b5d 100644 (file)
@@ -1096,6 +1096,52 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
        return 0;
 }
 
+/*
+ * Parse a devname into substrings and populate the vol->UNC and vol->prepath
+ * fields with the result. Returns 0 on success and an error otherwise.
+ */
+static int
+cifs_parse_devname(const char *devname, struct smb_vol *vol)
+{
+       char *pos;
+       const char *delims = "/\\";
+       size_t len;
+
+       /* make sure we have a valid UNC double delimiter prefix */
+       len = strspn(devname, delims);
+       if (len != 2)
+               return -EINVAL;
+
+       /* find delimiter between host and sharename */
+       pos = strpbrk(devname + 2, delims);
+       if (!pos)
+               return -EINVAL;
+
+       /* skip past delimiter */
+       ++pos;
+
+       /* now go until next delimiter or end of string */
+       len = strcspn(pos, delims);
+
+       /* move "pos" up to delimiter or NULL */
+       pos += len;
+       vol->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
+       if (!vol->UNC)
+               return -ENOMEM;
+
+       convert_delimiter(vol->UNC, '\\');
+
+       /* If pos is NULL, or is a bogus trailing delimiter then no prepath */
+       if (!*pos++ || !*pos)
+               return 0;
+
+       vol->prepath = kstrdup(pos, GFP_KERNEL);
+       if (!vol->prepath)
+               return -ENOMEM;
+
+       return 0;
+}
+
 static int
 cifs_parse_mount_options(const char *mountdata, const char *devname,
                         struct smb_vol *vol)
@@ -1181,6 +1227,16 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        vol->backupuid_specified = false; /* no backup intent for a user */
        vol->backupgid_specified = false; /* no backup intent for a group */
 
+       /*
+        * For now, we ignore -EINVAL errors under the assumption that the
+        * unc= and prefixpath= options will be usable.
+        */
+       if (cifs_parse_devname(devname, vol) == -ENOMEM) {
+               printk(KERN_ERR "CIFS: Unable to allocate memory to parse "
+                               "device string.\n");
+               goto out_nomem;
+       }
+
        while ((data = strsep(&options, separator)) != NULL) {
                substring_t args[MAX_OPT_ARGS];
                unsigned long option;
@@ -1566,18 +1622,31 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        got_ip = true;
                        break;
                case Opt_unc:
-                       kfree(vol->UNC);
+                       string = vol->UNC;
                        vol->UNC = match_strdup(args);
-                       if (vol->UNC == NULL)
+                       if (vol->UNC == NULL) {
+                               kfree(string);
                                goto out_nomem;
+                       }
 
                        convert_delimiter(vol->UNC, '\\');
                        if (vol->UNC[0] != '\\' || vol->UNC[1] != '\\') {
-                               printk(KERN_WARNING "CIFS: UNC Path does not "
+                               kfree(string);
+                               printk(KERN_ERR "CIFS: UNC Path does not "
                                                "begin with // or \\\\\n");
                                goto cifs_parse_mount_err;
                        }
 
+                       /* Compare old unc= option to new one */
+                       if (!string || strcmp(string, vol->UNC))
+                               printk(KERN_WARNING "CIFS: the value of the "
+                                       "unc= mount option does not match the "
+                                       "device string. Using the unc= option "
+                                       "for now. In 3.10, that option will "
+                                       "be ignored and the contents of the "
+                                       "device string will be used "
+                                       "instead. (%s != %s)\n", string,
+                                       vol->UNC);
                        break;
                case Opt_domain:
                        string = match_strdup(args);
@@ -1616,10 +1685,22 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        if (*args[0].from == '/' || *args[0].from == '\\')
                                args[0].from++;
 
-                       kfree(vol->prepath);
+                       string = vol->prepath;
                        vol->prepath = match_strdup(args);
-                       if (vol->prepath == NULL)
+                       if (vol->prepath == NULL) {
+                               kfree(string);
                                goto out_nomem;
+                       }
+                       /* Compare old prefixpath= option to new one */
+                       if (!string || strcmp(string, vol->prepath))
+                               printk(KERN_WARNING "CIFS: the value of the "
+                                       "prefixpath= mount option does not "
+                                       "match the device string. Using the "
+                                       "prefixpath= option for now. In 3.10, "
+                                       "that option will be ignored and the "
+                                       "contents of the device string will be "
+                                       "used instead.(%s != %s)\n", string,
+                                       vol->prepath);
                        break;
                case Opt_iocharset:
                        string = match_strdup(args);
@@ -1777,8 +1858,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
        }
 #endif
        if (!vol->UNC) {
-               cERROR(1, "CIFS mount error: No UNC path (e.g. -o "
-                       "unc=\\\\192.168.1.100\\public) specified");
+               cERROR(1, "CIFS mount error: No usable UNC path provided in "
+                         "device string or in unc= option!");
                goto cifs_parse_mount_err;
        }