]> err.no Git - linux-2.6/blobdiff - fs/nfs/super.c
NFS: Set default port for NFSv4, with support for AF_INET6
[linux-2.6] / fs / nfs / super.c
index 330c3922739f94f5108e4afab12875ba45cc244c..a88697ff19efc1aa460d324a4d7389f89b5ef4c4 100644 (file)
@@ -45,6 +45,8 @@
 #include <linux/nfs_idmap.h>
 #include <linux/vfs.h>
 #include <linux/inet.h>
+#include <linux/in6.h>
+#include <net/ipv6.h>
 #include <linux/nfs_xdr.h>
 #include <linux/magic.h>
 #include <linux/parser.h>
@@ -491,8 +493,9 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
 
        nfs_show_mount_options(m, nfss, 0);
 
-       seq_printf(m, ",addr="NIPQUAD_FMT,
-               NIPQUAD(nfss->nfs_client->cl_addr.sin_addr));
+       seq_printf(m, ",addr=%s",
+                       rpc_peeraddr2str(nfss->nfs_client->cl_rpcclient,
+                                                       RPC_DISPLAY_ADDR));
 
        return 0;
 }
@@ -529,7 +532,7 @@ static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
        seq_printf(m, ",namelen=%d", nfss->namelen);
 
 #ifdef CONFIG_NFS_V4
-       if (nfss->nfs_client->cl_nfsversion == 4) {
+       if (nfss->nfs_client->rpc_ops->version == 4) {
                seq_printf(m, "\n\tnfsv4:\t");
                seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
                seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
@@ -597,16 +600,40 @@ static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags)
 }
 
 /*
- * Sanity-check a server address provided by the mount command
+ * Set the port number in an address.  Be agnostic about the address family.
+ */
+static void nfs_set_port(struct sockaddr *sap, unsigned short port)
+{
+       switch (sap->sa_family) {
+       case AF_INET: {
+               struct sockaddr_in *ap = (struct sockaddr_in *)sap;
+               ap->sin_port = htons(port);
+               break;
+       }
+       case AF_INET6: {
+               struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
+               ap->sin6_port = htons(port);
+               break;
+       }
+       }
+}
+
+/*
+ * Sanity-check a server address provided by the mount command.
+ *
+ * Address family must be initialized, and address must not be
+ * the ANY address for that family.
  */
 static int nfs_verify_server_address(struct sockaddr *addr)
 {
        switch (addr->sa_family) {
        case AF_INET: {
-               struct sockaddr_in *sa = (struct sockaddr_in *) addr;
-               if (sa->sin_addr.s_addr != INADDR_ANY)
-                       return 1;
-               break;
+               struct sockaddr_in *sa = (struct sockaddr_in *)addr;
+               return sa->sin_addr.s_addr != INADDR_ANY;
+       }
+       case AF_INET6: {
+               struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
+               return !ipv6_addr_any(sa);
        }
        }
 
@@ -621,6 +648,7 @@ static int nfs_parse_mount_options(char *raw,
                                   struct nfs_parsed_mount_data *mnt)
 {
        char *p, *string;
+       unsigned short port = 0;
 
        if (!raw) {
                dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
@@ -723,7 +751,7 @@ static int nfs_parse_mount_options(char *raw,
                                return 0;
                        if (option < 0 || option > 65535)
                                return 0;
-                       mnt->nfs_server.address.sin_port = htons(option);
+                       port = option;
                        break;
                case Opt_rsize:
                        if (match_int(args, &mnt->rsize))
@@ -965,6 +993,8 @@ static int nfs_parse_mount_options(char *raw,
                }
        }
 
+       nfs_set_port((struct sockaddr *)&mnt->nfs_server.address, port);
+
        return 1;
 
 out_nomem:
@@ -1015,7 +1045,7 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
        /*
         * autobind will be used if mount_server.port == 0
         */
-       sin.sin_port = htons(args->mount_server.port);
+       nfs_set_port((struct sockaddr *)&sin, args->mount_server.port);
 
        /*
         * Now ask the mount server to map our export path
@@ -1325,15 +1355,50 @@ static int nfs_set_super(struct super_block *s, void *data)
        return ret;
 }
 
+static int nfs_compare_super_address(struct nfs_server *server1,
+                                    struct nfs_server *server2)
+{
+       struct sockaddr *sap1, *sap2;
+
+       sap1 = (struct sockaddr *)&server1->nfs_client->cl_addr;
+       sap2 = (struct sockaddr *)&server2->nfs_client->cl_addr;
+
+       if (sap1->sa_family != sap2->sa_family)
+               return 0;
+
+       switch (sap1->sa_family) {
+       case AF_INET: {
+               struct sockaddr_in *sin1 = (struct sockaddr_in *)sap1;
+               struct sockaddr_in *sin2 = (struct sockaddr_in *)sap2;
+               if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr)
+                       return 0;
+               if (sin1->sin_port != sin2->sin_port)
+                       return 0;
+               break;
+       }
+       case AF_INET6: {
+               struct sockaddr_in6 *sin1 = (struct sockaddr_in6 *)sap1;
+               struct sockaddr_in6 *sin2 = (struct sockaddr_in6 *)sap2;
+               if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr))
+                       return 0;
+               if (sin1->sin6_port != sin2->sin6_port)
+                       return 0;
+               break;
+       }
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
 static int nfs_compare_super(struct super_block *sb, void *data)
 {
        struct nfs_sb_mountdata *sb_mntdata = data;
        struct nfs_server *server = sb_mntdata->server, *old = NFS_SB(sb);
        int mntflags = sb_mntdata->mntflags;
 
-       if (memcmp(&old->nfs_client->cl_addr,
-                               &server->nfs_client->cl_addr,
-                               sizeof(old->nfs_client->cl_addr)) != 0)
+       if (!nfs_compare_super_address(old, server))
                return 0;
        /* Note: NFS_MOUNT_UNSHARED == NFS4_MOUNT_UNSHARED */
        if (old->flags & NFS_MOUNT_UNSHARED)
@@ -1530,6 +1595,28 @@ static void nfs4_fill_super(struct super_block *sb)
        nfs_initialise_sb(sb);
 }
 
+/*
+ * If the user didn't specify a port, set the port number to
+ * the NFS version 4 default port.
+ */
+static void nfs4_default_port(struct sockaddr *sap)
+{
+       switch (sap->sa_family) {
+       case AF_INET: {
+               struct sockaddr_in *ap = (struct sockaddr_in *)sap;
+               if (ap->sin_port == 0)
+                       ap->sin_port = htons(NFS_PORT);
+               break;
+       }
+       case AF_INET6: {
+               struct sockaddr_in6 *ap = (struct sockaddr_in6 *)sap;
+               if (ap->sin6_port == 0)
+                       ap->sin6_port = htons(NFS_PORT);
+               break;
+       }
+       }
+}
+
 /*
  * Validate NFSv4 mount options
  */
@@ -1563,12 +1650,13 @@ static int nfs4_validate_mount_data(void *options,
                                   data->host_addr,
                                   sizeof(args->nfs_server.address)))
                        return -EFAULT;
-               if (args->nfs_server.address.sin_port == 0)
-                       args->nfs_server.address.sin_port = htons(NFS_PORT);
                if (!nfs_verify_server_address((struct sockaddr *)
                                                &args->nfs_server.address))
                        goto out_no_address;
 
+               nfs4_default_port((struct sockaddr *)
+                                 &args->nfs_server.address);
+
                switch (data->auth_flavourlen) {
                case 0:
                        args->auth_flavors[0] = RPC_AUTH_UNIX;
@@ -1622,12 +1710,13 @@ static int nfs4_validate_mount_data(void *options,
                if (nfs_parse_mount_options((char *)options, args) == 0)
                        return -EINVAL;
 
-               if (args->nfs_server.address.sin_port == 0)
-                       args->nfs_server.address.sin_port = htons(NFS_PORT);
                if (!nfs_verify_server_address((struct sockaddr *)
                                                &args->nfs_server.address))
                        return -EINVAL;
 
+               nfs4_default_port((struct sockaddr *)
+                                 &args->nfs_server.address);
+
                switch (args->auth_flavor_len) {
                case 0:
                        args->auth_flavors[0] = RPC_AUTH_UNIX;
@@ -1648,21 +1737,16 @@ static int nfs4_validate_mount_data(void *options,
                len = c - dev_name;
                if (len > NFS4_MAXNAMLEN)
                        return -ENAMETOOLONG;
-               args->nfs_server.hostname = kzalloc(len, GFP_KERNEL);
-               if (args->nfs_server.hostname == NULL)
-                       return -ENOMEM;
-               strncpy(args->nfs_server.hostname, dev_name, len - 1);
+               /* N.B. caller will free nfs_server.hostname in all cases */
+               args->nfs_server.hostname = kstrndup(dev_name, len, GFP_KERNEL);
 
                c++;                    /* step over the ':' */
                len = strlen(c);
                if (len > NFS4_MAXPATHLEN)
                        return -ENAMETOOLONG;
-               args->nfs_server.export_path = kzalloc(len + 1, GFP_KERNEL);
-               if (args->nfs_server.export_path == NULL)
-                       return -ENOMEM;
-               strncpy(args->nfs_server.export_path, c, len);
+               args->nfs_server.export_path = kstrndup(c, len, GFP_KERNEL);
 
-               dprintk("MNTPATH: %s\n", args->nfs_server.export_path);
+               dprintk("NFS: MNTPATH: '%s'\n", args->nfs_server.export_path);
 
                if (args->client_address == NULL)
                        goto out_no_client_address;