From 0c834c586066af4c804c38d23b9763d818105f64 Mon Sep 17 00:00:00 2001
From: Jason Wessel <jason.wessel@windriver.com>
Date: Wed, 7 Jan 2009 00:59:33 -0500
Subject: [PATCH] NFS: allow nfs root mount to use alternate rpc ports

Allow an nfs root mount to use alternate RPC ports for mountd and nfsd.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
[forward port to 2.6.33+]
Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com>
---
 fs/nfs/client.c                | 12 ++++++++++--
 fs/nfs/internal.h              |  7 +++++--
 fs/nfs/mount_clnt.c            |  7 ++++---
 fs/nfs/fs_context.c                 | 34 +++++++++++++++++++++++++++++++++-
 include/linux/nfs_fs_sb.h      |  2 ++
 include/uapi/linux/nfs_mount.h |  4 +++-
 6 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 02110a30a49e..a0f0a7d6208a 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -5,7 +5,6 @@
  * Written by David Howells (dhowells@redhat.com)
  */
 
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/sched.h>
@@ -67,7 +66,7 @@ static const struct rpc_version *nfs_version[5] = {
 	[4] = NULL,
 };
 
-const struct rpc_program nfs_program = {
+struct rpc_program nfs_program = {
 	.name			= "nfs",
 	.number			= NFS_PROGRAM,
 	.nrvers			= ARRAY_SIZE(nfs_version),
@@ -158,6 +157,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
 		goto error_dealloc;
 
 	clp->rpc_ops = clp->cl_nfs_mod->rpc_ops;
+	clp->nfs_prog = cl_init->nfs_prog;
 
 	refcount_set(&clp->cl_count, 1);
 	clp->cl_cons_state = NFS_CS_INITING;
@@ -318,6 +318,9 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
 		    test_bit(NFS_CS_DS, &clp->cl_flags))
 			continue;
 
+		if (clp->nfs_prog != data->nfs_prog)
+			continue;
+
 		/* Match the full socket address */
 		if (!rpc_cmp_addr_port(sap, clap))
 			/* Match all xprt_switch full socket addresses */
@@ -529,6 +532,10 @@ int nfs_create_rpc_client(struct nfs_client *clp,
 	if (!IS_ERR(clp->cl_rpcclient))
 		return 0;
 
+	if (clp->nfs_prog)
+		nfs_program.number = clp->nfs_prog;
+	else
+		nfs_program.number = NFS_PROGRAM;
 	clnt = rpc_create(&args);
 	if (IS_ERR(clnt)) {
 		dprintk("%s: cannot create RPC client. Error = %ld\n",
@@ -673,6 +680,7 @@ static int nfs_init_server(struct nfs_server *server,
 		.cred = server->cred,
 		.nconnect = data->nfs_server.nconnect,
 		.init_flags = (1UL << NFS_CS_REUSEPORT),
+                .nfs_prog = data->nfs_prog,
 	};
 	struct nfs_client *clp;
 	int error;
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 24a65da58aa9..0961649ec4dc 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -77,6 +77,7 @@ struct nfs_client_initdata {
 	struct net *net;
 	const struct rpc_timeout *timeparms;
 	const struct cred *cred;
+	int nfs_prog;
 };
 
 /*
@@ -88,6 +89,8 @@ struct nfs_parsed_mount_data {
 	unsigned int		timeo, retrans;
 	unsigned int		acregmin, acregmax,
 				acdirmin, acdirmax;
+	int			nfs_prog;
+	int			mount_prog;
 	unsigned int		namlen;
 	unsigned int		options;
 	unsigned int		bsize;
@@ -145,11 +148,11 @@ struct nfs_mount_info {
 	struct nfs_fh *mntfh;
 };
 
-extern int nfs_mount(struct nfs_mount_request *info);
+extern int nfs_mount(struct nfs_mount_request *info, int prog);
 extern void nfs_umount(const struct nfs_mount_request *info);
 
 /* client.c */
-extern const struct rpc_program nfs_program;
+extern struct rpc_program nfs_program;
 extern void nfs_clients_init(struct net *net);
 extern void nfs_clients_exit(struct net *net);
 extern struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *);
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index cb7c10e9721e..266da4cac411 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -66,7 +66,7 @@ enum {
 	MOUNTPROC3_EXPORT	= 5,
 };
 
-static const struct rpc_program mnt_program;
+static struct rpc_program mnt_program;
 
 /*
  * Defined by OpenGroup XNFS Version 3W, chapter 8
@@ -143,7 +143,7 @@ struct mnt_fhstatus {
  * with the list from the server or a faked-up list if the server didn't
  * provide one.
  */
-int nfs_mount(struct nfs_mount_request *info)
+int nfs_mount(struct nfs_mount_request *info, int m_prog)
 {
 	struct mountres	result = {
 		.fh		= info->fh,
@@ -178,6 +178,7 @@ int nfs_mount(struct nfs_mount_request *info)
 	if (info->noresvport)
 		args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
 
+	mnt_program.number = m_prog;
 	mnt_clnt = rpc_create(&args);
 	if (IS_ERR(mnt_clnt))
 		goto out_clnt_err;
@@ -532,7 +533,7 @@ static const struct rpc_version *mnt_version[] = {
 
 static struct rpc_stat mnt_stats;
 
-static const struct rpc_program mnt_program = {
+static struct rpc_program mnt_program = {
 	.name		= "mount",
 	.number		= NFS_MNT_PROGRAM,
 	.nrvers		= ARRAY_SIZE(mnt_version),
diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index 8d8d04bb9d64..e09f468bcf18 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -105,6 +105,8 @@ enum {
 	Opt_mountport,
 	Opt_mountvers,
 	Opt_minorversion,
+	Opt_mountprog,
+	Opt_nfsprog,
 
 	/* Mount options that take string arguments */
 	Opt_nfsvers,
@@ -172,6 +174,8 @@ static const match_table_t nfs_mount_option_tokens = {
 	{ Opt_mountport, "mountport=%s" },
 	{ Opt_mountvers, "mountvers=%s" },
 	{ Opt_minorversion, "minorversion=%s" },
+	{ Opt_mountprog, "mountprog=%s" },
+	{ Opt_nfsprog, "nfsprog=%s" },
 
 	{ Opt_nfsvers, "nfsvers=%s" },
 	{ Opt_nfsvers, "vers=%s" },
@@ -943,6 +947,8 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
 		data->acregmax		= NFS_DEF_ACREGMAX;
 		data->acdirmin		= NFS_DEF_ACDIRMIN;
 		data->acdirmax		= NFS_DEF_ACDIRMAX;
+		data->nfs_prog		= NFS_PROGRAM;
+		data->mount_prog	= NFS_MNT_PROGRAM;
 		data->mount_server.port	= NFS_UNSPEC_PORT;
 		data->nfs_server.port	= NFS_UNSPEC_PORT;
 		data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
@@ -1405,6 +1411,26 @@ static int nfs_parse_mount_options(char *raw,
 				goto out_invalid_value;
 			mnt->acdirmax = option;
 			break;
+		case Opt_mountprog:
+			string = match_strdup(args);
+			if (string == NULL)
+				goto out_nomem;
+			rc = kstrtoul(string, 10, &option);
+			kfree(string);
+			if (rc != 0)
+				goto out_invalid_value;
+			mnt->mount_prog = option;
+			break;
+		case Opt_nfsprog:
+			string = match_strdup(args);
+			if (string == NULL)
+				goto out_nomem;
+			rc = kstrtoul(string, 10, &option);
+			kfree(string);
+			if (rc != 0)
+			       goto out_invalid_value;
+			mnt->nfs_prog = option;
+			break;
 		case Opt_actimeo:
 			if (nfs_get_option_ul(args, &option))
 				goto out_invalid_value;
@@ -1809,7 +1835,7 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
 	 * Now ask the mount server to map our export path
 	 * to a file handle.
 	 */
-	status = nfs_mount(&request);
+	status = nfs_mount(&request,args->mount_prog);
 	if (status != 0) {
 		dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
 				request.hostname, status);
@@ -2117,6 +2143,9 @@ static int nfs23_validate_mount_data(void *options,
 #endif
 		}
 
+		break;
+	case 7:
+		args->nfs_prog = (data->version >= 7) ? data->nfs_prog : NFS_PROGRAM;
 		break;
 	default:
 		return NFS_TEXT_DATA;
@@ -2180,6 +2209,7 @@ static int nfs_validate_text_mount_data(void *options,
 	int max_namelen = PAGE_SIZE;
 	int max_pathlen = NFS_MAXPATHLEN;
 	struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
+	args->nfs_prog		= NFS_PROGRAM;
 
 	if (nfs_parse_mount_options((char *)options, args) == 0)
 		return -EINVAL;
@@ -2849,6 +2879,8 @@ static int nfs4_validate_mount_data(void *options,
 
 	args->version = 4;
 
+	args->nfs_prog = NFS_PROGRAM;
+
 	switch (data->version) {
 	case 1:
 		if (data->host_addrlen > sizeof(args->nfs_server.address))
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index c176f705bf98..4514af8b8d39 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -63,6 +63,7 @@ struct nfs_client {
 	u32			cl_minorversion;/* NFSv4 minorversion */
 	unsigned int		cl_nconnect;	/* Number of connections */
 	const char *		cl_principal;  /* used for machine cred */
+	int			nfs_prog;
 
 #if IS_ENABLED(CONFIG_NFS_V4)
 	struct list_head	cl_ds_clients; /* auth flavor data servers */
@@ -85,6 +86,7 @@ struct nfs_client {
 	const char *		cl_owner_id;
 
 	u32			cl_cb_ident;	/* v4.0 callback identifier */
+
 	const struct nfs4_minor_version_ops *cl_mvops;
 	unsigned long		cl_mig_gen;
 
diff --git a/include/uapi/linux/nfs_mount.h b/include/uapi/linux/nfs_mount.h
index e3bcfc6aa3b0..ed57fec0beef 100644
--- a/include/uapi/linux/nfs_mount.h
+++ b/include/uapi/linux/nfs_mount.h
@@ -21,7 +21,7 @@
  * mount-to-kernel version compatibility.  Some of these aren't used yet
  * but here they are anyway.
  */
-#define NFS_MOUNT_VERSION	6
+#define NFS_MOUNT_VERSION	7
 #define NFS_MAX_CONTEXT_LEN	256
 
 struct nfs_mount_data {
@@ -44,6 +44,8 @@ struct nfs_mount_data {
 	struct nfs3_fh	root;			/* 4 */
 	int		pseudoflavor;		/* 5 */
 	char		context[NFS_MAX_CONTEXT_LEN + 1];	/* 6 */
+	int		nfs_prog; /* 7 */
+	int		mount_prog; /* 7 */
 };
 
 /* bits in the flags field visible to user space */
-- 
2.19.1

