NFS: Support raw IPv6 address hostnames during NFS mount operation
authorChuck Lever <chuck.lever@oracle.com>
Mon, 23 Jun 2008 16:36:45 +0000 (12:36 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 9 Jul 2008 16:09:27 +0000 (12:09 -0400)
Traditionally the mount command has looked for a ":" to separate the
server's hostname from the export path in the mounted on device name,
like this:

mount server:/export /mounted/on/dir

The server's hostname is "server" and the export path is "/export".

You can also substitute a specific IPv4 network address for the server
hostname, like this:

mount 192.168.0.55:/export /mounted/on/dir

Raw IPv6 addresses present a problem, however, because they look
something like this:

fe80::200:5aff:fe00:30b

Note the use of colons.

To get around the presence of colons, copy the Solaris convention used for
mounting IPv6 servers by address: wrap a raw IPv6 address with square
brackets.

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

index c4ee8b3..ea4abd2 100644 (file)
@@ -1195,14 +1195,9 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
        return status;
 }
 
-/*
- * Split "dev_name" into "hostname:export_path".
- *
- * Note: caller frees hostname and export path, even on error.
- */
-static int nfs_parse_devname(const char *dev_name,
-                            char **hostname, size_t maxnamlen,
-                            char **export_path, size_t maxpathlen)
+static int nfs_parse_simple_hostname(const char *dev_name,
+                                    char **hostname, size_t maxnamlen,
+                                    char **export_path, size_t maxpathlen)
 {
        size_t len;
        char *colon, *comma;
@@ -1256,6 +1251,85 @@ out_path:
        return -ENAMETOOLONG;
 }
 
+/*
+ * Hostname has square brackets around it because it contains one or
+ * more colons.  We look for the first closing square bracket, and a
+ * colon must follow it.
+ */
+static int nfs_parse_protected_hostname(const char *dev_name,
+                                       char **hostname, size_t maxnamlen,
+                                       char **export_path, size_t maxpathlen)
+{
+       size_t len;
+       char *start, *end;
+
+       start = (char *)(dev_name + 1);
+
+       end = strchr(start, ']');
+       if (end == NULL)
+               goto out_bad_devname;
+       if (*(end + 1) != ':')
+               goto out_bad_devname;
+
+       len = end - start;
+       if (len > maxnamlen)
+               goto out_hostname;
+
+       /* N.B. caller will free nfs_server.hostname in all cases */
+       *hostname = kstrndup(start, len, GFP_KERNEL);
+       if (*hostname == NULL)
+               goto out_nomem;
+
+       end += 2;
+       len = strlen(end);
+       if (len > maxpathlen)
+               goto out_path;
+       *export_path = kstrndup(end, len, GFP_KERNEL);
+       if (!*export_path)
+               goto out_nomem;
+
+       return 0;
+
+out_bad_devname:
+       dfprintk(MOUNT, "NFS: device name not in host:path format\n");
+       return -EINVAL;
+
+out_nomem:
+       dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
+       return -ENOMEM;
+
+out_hostname:
+       dfprintk(MOUNT, "NFS: server hostname too long\n");
+       return -ENAMETOOLONG;
+
+out_path:
+       dfprintk(MOUNT, "NFS: export pathname too long\n");
+       return -ENAMETOOLONG;
+}
+
+/*
+ * Split "dev_name" into "hostname:export_path".
+ *
+ * The leftmost colon demarks the split between the server's hostname
+ * and the export path.  If the hostname starts with a left square
+ * bracket, then it may contain colons.
+ *
+ * Note: caller frees hostname and export path, even on error.
+ */
+static int nfs_parse_devname(const char *dev_name,
+                            char **hostname, size_t maxnamlen,
+                            char **export_path, size_t maxpathlen)
+{
+       if (*dev_name == '[')
+               return nfs_parse_protected_hostname(dev_name,
+                                                   hostname, maxnamlen,
+                                                   export_path, maxpathlen);
+
+       return nfs_parse_simple_hostname(dev_name,
+                                        hostname, maxnamlen,
+                                        export_path, maxpathlen);
+}
+
 /*
  * Validate the NFS2/NFS3 mount data
  * - fills in the mount root filehandle