NFS: Introduce nfs4_validate_mount_options

Refactor NFSv4 mount processing to break out mount data validation
in the same way it's broken out in the NFSv2/v3 mount path.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index baf75e9..ed3ec44 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -814,6 +814,89 @@
 }
 
 /*
+ * Validate NFSv4 mount options
+ */
+static int nfs4_validate_mount_data(struct nfs4_mount_data **options,
+				    const char *dev_name,
+				    struct sockaddr_in *addr,
+				    rpc_authflavor_t *authflavour,
+				    char **hostname,
+				    char **mntpath,
+				    char **ip_addr)
+{
+	struct nfs4_mount_data *data = *options;
+	char *c;
+
+	if (data == NULL)
+		goto out_no_data;
+
+	switch (data->version) {
+	case 1:
+		if (data->host_addrlen != sizeof(*addr))
+			goto out_no_address;
+		if (copy_from_user(addr, data->host_addr, sizeof(*addr)))
+			return -EFAULT;
+		if (addr->sin_port == 0)
+			addr->sin_port = htons(NFS_PORT);
+		if (!nfs_verify_server_address((struct sockaddr *) addr))
+			goto out_no_address;
+
+		switch (data->auth_flavourlen) {
+		case 0:
+			*authflavour = RPC_AUTH_UNIX;
+			break;
+		case 1:
+			if (copy_from_user(authflavour, data->auth_flavours,
+					   sizeof(*authflavour)))
+				return -EFAULT;
+			break;
+		default:
+			goto out_inval_auth;
+		}
+
+		c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
+		if (IS_ERR(c))
+			return PTR_ERR(c);
+		*hostname = c;
+
+		c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
+		if (IS_ERR(c))
+			return PTR_ERR(c);
+		*mntpath = c;
+		dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *mntpath);
+
+		c = strndup_user(data->client_addr.data, 16);
+		if (IS_ERR(c))
+			return PTR_ERR(c);
+		*ip_addr = c;
+
+		break;
+	default:
+		goto out_bad_version;
+	}
+
+	return 0;
+
+out_no_data:
+	dfprintk(MOUNT, "NFS4: mount program didn't pass any mount data\n");
+	return -EINVAL;
+
+out_inval_auth:
+	dfprintk(MOUNT, "NFS4: Invalid number of RPC auth flavours %d\n",
+		 data->auth_flavourlen);
+	return -EINVAL;
+
+out_no_address:
+	dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
+	return -EINVAL;
+
+out_bad_version:
+	dfprintk(MOUNT, "NFS4: bad nfs_mount_data version %d\n",
+		 data->version);
+	return -EINVAL;
+}
+
+/*
  * Get the superblock for an NFS4 mountpoint
  */
 static int nfs4_get_sb(struct file_system_type *fs_type,
@@ -826,68 +909,14 @@
 	rpc_authflavor_t authflavour;
 	struct nfs_fh mntfh;
 	struct dentry *mntroot;
-	char *p, *mntpath = NULL, *hostname = NULL, *ip_addr = NULL;
+	char *mntpath = NULL, *hostname = NULL, *ip_addr = NULL;
 	int error;
 
-	if (data == NULL) {
-		dprintk("%s: missing data argument\n", __FUNCTION__);
-		return -EINVAL;
-	}
-	if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) {
-		dprintk("%s: bad mount version\n", __FUNCTION__);
-		return -EINVAL;
-	}
-
-	/* We now require that the mount process passes the remote address */
-	if (data->host_addrlen != sizeof(addr))
-		return -EINVAL;
-
-	if (copy_from_user(&addr, data->host_addr, sizeof(addr)))
-		return -EFAULT;
-
-	if (!nfs_verify_server_address((struct sockaddr *) &addr)) {
-		dprintk("%s: mount program didn't pass remote IP address!\n",
-				__FUNCTION__);
-		return -EINVAL;
-	}
-
-	/* RFC3530: The default port for NFS is 2049 */
-	if (addr.sin_port == 0)
-		addr.sin_port = htons(NFS_PORT);
-
-	/* Grab the authentication type */
-	authflavour = RPC_AUTH_UNIX;
-	if (data->auth_flavourlen != 0) {
-		if (data->auth_flavourlen != 1) {
-			dprintk("%s: Invalid number of RPC auth flavours %d.\n",
-					__FUNCTION__, data->auth_flavourlen);
-			error = -EINVAL;
-			goto out;
-		}
-
-		if (copy_from_user(&authflavour, data->auth_flavours,
-				   sizeof(authflavour))) {
-			error = -EFAULT;
-			goto out;
-		}
-	}
-
-	p = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
-	if (IS_ERR(p))
-		goto out_err;
-	hostname = p;
-
-	p = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
-	if (IS_ERR(p))
-		goto out_err;
-	mntpath = p;
-
-	dprintk("MNTPATH: %s\n", mntpath);
-
-	p = strndup_user(data->client_addr.data, 16);
-	if (IS_ERR(p))
-		goto out_err;
-	ip_addr = p;
+	/* Validate the mount data */
+	error = nfs4_validate_mount_data(&data, dev_name, &addr, &authflavour,
+					 &hostname, &mntpath, &ip_addr);
+	if (error < 0)
+		goto out;
 
 	/* Get a volume representation */
 	server = nfs4_create_server(data, hostname, &addr, mntpath, ip_addr,
@@ -932,10 +961,6 @@
 	kfree(hostname);
 	return error;
 
-out_err:
-	error = PTR_ERR(p);
-	goto out;
-
 out_free:
 	nfs_free_server(server);
 	goto out;