xref: /openbmc/linux/fs/nfs/fs_context.c (revision 3a21409a0b4bce3171001b168ca42285004c873c)
19954bf92SDavid Howells // SPDX-License-Identifier: GPL-2.0-only
29954bf92SDavid Howells /*
39954bf92SDavid Howells  * linux/fs/nfs/fs_context.c
49954bf92SDavid Howells  *
59954bf92SDavid Howells  * Copyright (C) 1992 Rick Sladkey
6f2aedb71SDavid Howells  * Conversion to new mount api Copyright (C) David Howells
79954bf92SDavid Howells  *
89954bf92SDavid Howells  * NFS mount handling.
99954bf92SDavid Howells  *
109954bf92SDavid Howells  * Split from fs/nfs/super.c by David Howells <dhowells@redhat.com>
119954bf92SDavid Howells  */
129954bf92SDavid Howells 
139954bf92SDavid Howells #include <linux/module.h>
149954bf92SDavid Howells #include <linux/fs.h>
15e38bb238SScott Mayhew #include <linux/fs_context.h>
16e38bb238SScott Mayhew #include <linux/fs_parser.h>
179954bf92SDavid Howells #include <linux/nfs_fs.h>
189954bf92SDavid Howells #include <linux/nfs_mount.h>
199954bf92SDavid Howells #include <linux/nfs4_mount.h>
209954bf92SDavid Howells #include "nfs.h"
219954bf92SDavid Howells #include "internal.h"
229954bf92SDavid Howells 
239954bf92SDavid Howells #define NFSDBG_FACILITY		NFSDBG_MOUNT
249954bf92SDavid Howells 
259954bf92SDavid Howells #if IS_ENABLED(CONFIG_NFS_V3)
269954bf92SDavid Howells #define NFS_DEFAULT_VERSION 3
279954bf92SDavid Howells #else
289954bf92SDavid Howells #define NFS_DEFAULT_VERSION 2
299954bf92SDavid Howells #endif
309954bf92SDavid Howells 
319954bf92SDavid Howells #define NFS_MAX_CONNECTIONS 16
329954bf92SDavid Howells 
33e38bb238SScott Mayhew enum nfs_param {
34e38bb238SScott Mayhew 	Opt_ac,
35e38bb238SScott Mayhew 	Opt_acdirmax,
36e38bb238SScott Mayhew 	Opt_acdirmin,
37e38bb238SScott Mayhew 	Opt_acl,
38e38bb238SScott Mayhew 	Opt_acregmax,
39e38bb238SScott Mayhew 	Opt_acregmin,
409954bf92SDavid Howells 	Opt_actimeo,
41e38bb238SScott Mayhew 	Opt_addr,
42e38bb238SScott Mayhew 	Opt_bg,
43e38bb238SScott Mayhew 	Opt_bsize,
44e38bb238SScott Mayhew 	Opt_clientaddr,
45e38bb238SScott Mayhew 	Opt_cto,
46e38bb238SScott Mayhew 	Opt_fg,
47e38bb238SScott Mayhew 	Opt_fscache,
48e38bb238SScott Mayhew 	Opt_hard,
49e38bb238SScott Mayhew 	Opt_intr,
509954bf92SDavid Howells 	Opt_local_lock,
51e38bb238SScott Mayhew 	Opt_lock,
52e38bb238SScott Mayhew 	Opt_lookupcache,
53e38bb238SScott Mayhew 	Opt_migration,
54e38bb238SScott Mayhew 	Opt_minorversion,
55e38bb238SScott Mayhew 	Opt_mountaddr,
56e38bb238SScott Mayhew 	Opt_mounthost,
57e38bb238SScott Mayhew 	Opt_mountport,
58e38bb238SScott Mayhew 	Opt_mountproto,
59e38bb238SScott Mayhew 	Opt_mountvers,
60e38bb238SScott Mayhew 	Opt_namelen,
61e38bb238SScott Mayhew 	Opt_nconnect,
62e38bb238SScott Mayhew 	Opt_port,
63e38bb238SScott Mayhew 	Opt_posix,
64e38bb238SScott Mayhew 	Opt_proto,
65e38bb238SScott Mayhew 	Opt_rdirplus,
66e38bb238SScott Mayhew 	Opt_rdma,
67e38bb238SScott Mayhew 	Opt_resvport,
68e38bb238SScott Mayhew 	Opt_retrans,
69e38bb238SScott Mayhew 	Opt_retry,
70e38bb238SScott Mayhew 	Opt_rsize,
71e38bb238SScott Mayhew 	Opt_sec,
72e38bb238SScott Mayhew 	Opt_sharecache,
73e38bb238SScott Mayhew 	Opt_sloppy,
74e38bb238SScott Mayhew 	Opt_soft,
75e38bb238SScott Mayhew 	Opt_softerr,
76c74dfe97STrond Myklebust 	Opt_softreval,
77e38bb238SScott Mayhew 	Opt_source,
78e38bb238SScott Mayhew 	Opt_tcp,
79e38bb238SScott Mayhew 	Opt_timeo,
80e38bb238SScott Mayhew 	Opt_udp,
81e38bb238SScott Mayhew 	Opt_v,
82e38bb238SScott Mayhew 	Opt_vers,
83e38bb238SScott Mayhew 	Opt_wsize,
849954bf92SDavid Howells };
859954bf92SDavid Howells 
86e38bb238SScott Mayhew static const struct fs_parameter_spec nfs_param_specs[] = {
87e38bb238SScott Mayhew 	fsparam_flag_no("ac",		Opt_ac),
88e38bb238SScott Mayhew 	fsparam_u32   ("acdirmax",	Opt_acdirmax),
89e38bb238SScott Mayhew 	fsparam_u32   ("acdirmin",	Opt_acdirmin),
90e38bb238SScott Mayhew 	fsparam_flag_no("acl",		Opt_acl),
91e38bb238SScott Mayhew 	fsparam_u32   ("acregmax",	Opt_acregmax),
92e38bb238SScott Mayhew 	fsparam_u32   ("acregmin",	Opt_acregmin),
93e38bb238SScott Mayhew 	fsparam_u32   ("actimeo",	Opt_actimeo),
94e38bb238SScott Mayhew 	fsparam_string("addr",		Opt_addr),
95e38bb238SScott Mayhew 	fsparam_flag  ("bg",		Opt_bg),
96e38bb238SScott Mayhew 	fsparam_u32   ("bsize",		Opt_bsize),
97e38bb238SScott Mayhew 	fsparam_string("clientaddr",	Opt_clientaddr),
98e38bb238SScott Mayhew 	fsparam_flag_no("cto",		Opt_cto),
99e38bb238SScott Mayhew 	fsparam_flag  ("fg",		Opt_fg),
100e38bb238SScott Mayhew 	__fsparam(fs_param_is_string, "fsc",		Opt_fscache,
101e38bb238SScott Mayhew 		  fs_param_neg_with_no|fs_param_v_optional),
102e38bb238SScott Mayhew 	fsparam_flag  ("hard",		Opt_hard),
103e38bb238SScott Mayhew 	__fsparam(fs_param_is_flag, "intr",		Opt_intr,
104e38bb238SScott Mayhew 		  fs_param_neg_with_no|fs_param_deprecated),
105e38bb238SScott Mayhew 	fsparam_enum  ("local_lock",	Opt_local_lock),
106e38bb238SScott Mayhew 	fsparam_flag_no("lock",		Opt_lock),
107e38bb238SScott Mayhew 	fsparam_enum  ("lookupcache",	Opt_lookupcache),
108e38bb238SScott Mayhew 	fsparam_flag_no("migration",	Opt_migration),
109e38bb238SScott Mayhew 	fsparam_u32   ("minorversion",	Opt_minorversion),
110e38bb238SScott Mayhew 	fsparam_string("mountaddr",	Opt_mountaddr),
111e38bb238SScott Mayhew 	fsparam_string("mounthost",	Opt_mounthost),
112e38bb238SScott Mayhew 	fsparam_u32   ("mountport",	Opt_mountport),
113e38bb238SScott Mayhew 	fsparam_string("mountproto",	Opt_mountproto),
114e38bb238SScott Mayhew 	fsparam_u32   ("mountvers",	Opt_mountvers),
115e38bb238SScott Mayhew 	fsparam_u32   ("namlen",	Opt_namelen),
116e38bb238SScott Mayhew 	fsparam_u32   ("nconnect",	Opt_nconnect),
117e38bb238SScott Mayhew 	fsparam_string("nfsvers",	Opt_vers),
118e38bb238SScott Mayhew 	fsparam_u32   ("port",		Opt_port),
119e38bb238SScott Mayhew 	fsparam_flag_no("posix",	Opt_posix),
120e38bb238SScott Mayhew 	fsparam_string("proto",		Opt_proto),
121e38bb238SScott Mayhew 	fsparam_flag_no("rdirplus",	Opt_rdirplus),
122e38bb238SScott Mayhew 	fsparam_flag  ("rdma",		Opt_rdma),
123e38bb238SScott Mayhew 	fsparam_flag_no("resvport",	Opt_resvport),
124e38bb238SScott Mayhew 	fsparam_u32   ("retrans",	Opt_retrans),
125e38bb238SScott Mayhew 	fsparam_string("retry",		Opt_retry),
126e38bb238SScott Mayhew 	fsparam_u32   ("rsize",		Opt_rsize),
127e38bb238SScott Mayhew 	fsparam_string("sec",		Opt_sec),
128e38bb238SScott Mayhew 	fsparam_flag_no("sharecache",	Opt_sharecache),
129e38bb238SScott Mayhew 	fsparam_flag  ("sloppy",	Opt_sloppy),
130e38bb238SScott Mayhew 	fsparam_flag  ("soft",		Opt_soft),
131e38bb238SScott Mayhew 	fsparam_flag  ("softerr",	Opt_softerr),
132c74dfe97STrond Myklebust 	fsparam_flag  ("softreval",	Opt_softreval),
133e38bb238SScott Mayhew 	fsparam_string("source",	Opt_source),
134e38bb238SScott Mayhew 	fsparam_flag  ("tcp",		Opt_tcp),
135e38bb238SScott Mayhew 	fsparam_u32   ("timeo",		Opt_timeo),
136e38bb238SScott Mayhew 	fsparam_flag  ("udp",		Opt_udp),
137e38bb238SScott Mayhew 	fsparam_flag  ("v2",		Opt_v),
138e38bb238SScott Mayhew 	fsparam_flag  ("v3",		Opt_v),
139e38bb238SScott Mayhew 	fsparam_flag  ("v4",		Opt_v),
140e38bb238SScott Mayhew 	fsparam_flag  ("v4.0",		Opt_v),
141e38bb238SScott Mayhew 	fsparam_flag  ("v4.1",		Opt_v),
142e38bb238SScott Mayhew 	fsparam_flag  ("v4.2",		Opt_v),
143e38bb238SScott Mayhew 	fsparam_string("vers",		Opt_vers),
144e38bb238SScott Mayhew 	fsparam_u32   ("wsize",		Opt_wsize),
145e38bb238SScott Mayhew 	{}
1469954bf92SDavid Howells };
1479954bf92SDavid Howells 
1489954bf92SDavid Howells enum {
149e38bb238SScott Mayhew 	Opt_local_lock_all,
150e38bb238SScott Mayhew 	Opt_local_lock_flock,
1519954bf92SDavid Howells 	Opt_local_lock_none,
152e38bb238SScott Mayhew 	Opt_local_lock_posix,
1539954bf92SDavid Howells };
1549954bf92SDavid Howells 
1559954bf92SDavid Howells enum {
156e38bb238SScott Mayhew 	Opt_lookupcache_all,
157e38bb238SScott Mayhew 	Opt_lookupcache_none,
158e38bb238SScott Mayhew 	Opt_lookupcache_positive,
1599954bf92SDavid Howells };
1609954bf92SDavid Howells 
161e38bb238SScott Mayhew static const struct fs_parameter_enum nfs_param_enums[] = {
162e38bb238SScott Mayhew 	{ Opt_local_lock,	"all",		Opt_local_lock_all },
163e38bb238SScott Mayhew 	{ Opt_local_lock,	"flock",	Opt_local_lock_flock },
164e38bb238SScott Mayhew 	{ Opt_local_lock,	"none",		Opt_local_lock_none },
165e38bb238SScott Mayhew 	{ Opt_local_lock,	"posix",	Opt_local_lock_posix },
166e38bb238SScott Mayhew 	{ Opt_lookupcache,	"all",		Opt_lookupcache_all },
167e38bb238SScott Mayhew 	{ Opt_lookupcache,	"none",		Opt_lookupcache_none },
168e38bb238SScott Mayhew 	{ Opt_lookupcache,	"pos",		Opt_lookupcache_positive },
169e38bb238SScott Mayhew 	{ Opt_lookupcache,	"positive",	Opt_lookupcache_positive },
170e38bb238SScott Mayhew 	{}
171e38bb238SScott Mayhew };
1729954bf92SDavid Howells 
173e38bb238SScott Mayhew static const struct fs_parameter_description nfs_fs_parameters = {
174e38bb238SScott Mayhew 	.name		= "nfs",
175e38bb238SScott Mayhew 	.specs		= nfs_param_specs,
176e38bb238SScott Mayhew 	.enums		= nfs_param_enums,
177e38bb238SScott Mayhew };
178e38bb238SScott Mayhew 
179e38bb238SScott Mayhew enum {
180e38bb238SScott Mayhew 	Opt_vers_2,
181e38bb238SScott Mayhew 	Opt_vers_3,
182e38bb238SScott Mayhew 	Opt_vers_4,
183e38bb238SScott Mayhew 	Opt_vers_4_0,
184e38bb238SScott Mayhew 	Opt_vers_4_1,
185e38bb238SScott Mayhew 	Opt_vers_4_2,
186e38bb238SScott Mayhew };
187e38bb238SScott Mayhew 
188e38bb238SScott Mayhew static const struct constant_table nfs_vers_tokens[] = {
189e38bb238SScott Mayhew 	{ "2",		Opt_vers_2 },
190e38bb238SScott Mayhew 	{ "3",		Opt_vers_3 },
191e38bb238SScott Mayhew 	{ "4",		Opt_vers_4 },
192e38bb238SScott Mayhew 	{ "4.0",	Opt_vers_4_0 },
193e38bb238SScott Mayhew 	{ "4.1",	Opt_vers_4_1 },
194e38bb238SScott Mayhew 	{ "4.2",	Opt_vers_4_2 },
195e38bb238SScott Mayhew };
196e38bb238SScott Mayhew 
197e38bb238SScott Mayhew enum {
198e38bb238SScott Mayhew 	Opt_xprt_rdma,
199e38bb238SScott Mayhew 	Opt_xprt_rdma6,
200e38bb238SScott Mayhew 	Opt_xprt_tcp,
201e38bb238SScott Mayhew 	Opt_xprt_tcp6,
202e38bb238SScott Mayhew 	Opt_xprt_udp,
203e38bb238SScott Mayhew 	Opt_xprt_udp6,
204e38bb238SScott Mayhew 	nr__Opt_xprt
205e38bb238SScott Mayhew };
206e38bb238SScott Mayhew 
207e38bb238SScott Mayhew static const struct constant_table nfs_xprt_protocol_tokens[nr__Opt_xprt] = {
208e38bb238SScott Mayhew 	{ "rdma",	Opt_xprt_rdma },
209e38bb238SScott Mayhew 	{ "rdma6",	Opt_xprt_rdma6 },
210e38bb238SScott Mayhew 	{ "tcp",	Opt_xprt_tcp },
211e38bb238SScott Mayhew 	{ "tcp6",	Opt_xprt_tcp6 },
212e38bb238SScott Mayhew 	{ "udp",	Opt_xprt_udp },
213e38bb238SScott Mayhew 	{ "udp6",	Opt_xprt_udp6 },
214e38bb238SScott Mayhew };
215e38bb238SScott Mayhew 
216e38bb238SScott Mayhew enum {
217e38bb238SScott Mayhew 	Opt_sec_krb5,
218e38bb238SScott Mayhew 	Opt_sec_krb5i,
219e38bb238SScott Mayhew 	Opt_sec_krb5p,
220e38bb238SScott Mayhew 	Opt_sec_lkey,
221e38bb238SScott Mayhew 	Opt_sec_lkeyi,
222e38bb238SScott Mayhew 	Opt_sec_lkeyp,
223e38bb238SScott Mayhew 	Opt_sec_none,
224e38bb238SScott Mayhew 	Opt_sec_spkm,
225e38bb238SScott Mayhew 	Opt_sec_spkmi,
226e38bb238SScott Mayhew 	Opt_sec_spkmp,
227e38bb238SScott Mayhew 	Opt_sec_sys,
228e38bb238SScott Mayhew 	nr__Opt_sec
229e38bb238SScott Mayhew };
230e38bb238SScott Mayhew 
231e38bb238SScott Mayhew static const struct constant_table nfs_secflavor_tokens[] = {
232e38bb238SScott Mayhew 	{ "krb5",	Opt_sec_krb5 },
233e38bb238SScott Mayhew 	{ "krb5i",	Opt_sec_krb5i },
234e38bb238SScott Mayhew 	{ "krb5p",	Opt_sec_krb5p },
235e38bb238SScott Mayhew 	{ "lkey",	Opt_sec_lkey },
236e38bb238SScott Mayhew 	{ "lkeyi",	Opt_sec_lkeyi },
237e38bb238SScott Mayhew 	{ "lkeyp",	Opt_sec_lkeyp },
238e38bb238SScott Mayhew 	{ "none",	Opt_sec_none },
239e38bb238SScott Mayhew 	{ "null",	Opt_sec_none },
240e38bb238SScott Mayhew 	{ "spkm3",	Opt_sec_spkm },
241e38bb238SScott Mayhew 	{ "spkm3i",	Opt_sec_spkmi },
242e38bb238SScott Mayhew 	{ "spkm3p",	Opt_sec_spkmp },
243e38bb238SScott Mayhew 	{ "sys",	Opt_sec_sys },
2449954bf92SDavid Howells };
2459954bf92SDavid Howells 
2469954bf92SDavid Howells /*
2479954bf92SDavid Howells  * Sanity-check a server address provided by the mount command.
2489954bf92SDavid Howells  *
2499954bf92SDavid Howells  * Address family must be initialized, and address must not be
2509954bf92SDavid Howells  * the ANY address for that family.
2519954bf92SDavid Howells  */
2529954bf92SDavid Howells static int nfs_verify_server_address(struct sockaddr *addr)
2539954bf92SDavid Howells {
2549954bf92SDavid Howells 	switch (addr->sa_family) {
2559954bf92SDavid Howells 	case AF_INET: {
2569954bf92SDavid Howells 		struct sockaddr_in *sa = (struct sockaddr_in *)addr;
2579954bf92SDavid Howells 		return sa->sin_addr.s_addr != htonl(INADDR_ANY);
2589954bf92SDavid Howells 	}
2599954bf92SDavid Howells 	case AF_INET6: {
2609954bf92SDavid Howells 		struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
2619954bf92SDavid Howells 		return !ipv6_addr_any(sa);
2629954bf92SDavid Howells 	}
2639954bf92SDavid Howells 	}
2649954bf92SDavid Howells 
2659954bf92SDavid Howells 	dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
2669954bf92SDavid Howells 	return 0;
2679954bf92SDavid Howells }
2689954bf92SDavid Howells 
2699954bf92SDavid Howells /*
2709954bf92SDavid Howells  * Sanity check the NFS transport protocol.
2719954bf92SDavid Howells  *
2729954bf92SDavid Howells  */
2735eb005caSDavid Howells static void nfs_validate_transport_protocol(struct nfs_fs_context *ctx)
2749954bf92SDavid Howells {
2755eb005caSDavid Howells 	switch (ctx->nfs_server.protocol) {
2769954bf92SDavid Howells 	case XPRT_TRANSPORT_UDP:
2779954bf92SDavid Howells 	case XPRT_TRANSPORT_TCP:
2789954bf92SDavid Howells 	case XPRT_TRANSPORT_RDMA:
2799954bf92SDavid Howells 		break;
2809954bf92SDavid Howells 	default:
2815eb005caSDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
2829954bf92SDavid Howells 	}
2839954bf92SDavid Howells }
2849954bf92SDavid Howells 
2859954bf92SDavid Howells /*
2869954bf92SDavid Howells  * For text based NFSv2/v3 mounts, the mount protocol transport default
2879954bf92SDavid Howells  * settings should depend upon the specified NFS transport.
2889954bf92SDavid Howells  */
2895eb005caSDavid Howells static void nfs_set_mount_transport_protocol(struct nfs_fs_context *ctx)
2909954bf92SDavid Howells {
2915eb005caSDavid Howells 	nfs_validate_transport_protocol(ctx);
2929954bf92SDavid Howells 
2935eb005caSDavid Howells 	if (ctx->mount_server.protocol == XPRT_TRANSPORT_UDP ||
2945eb005caSDavid Howells 	    ctx->mount_server.protocol == XPRT_TRANSPORT_TCP)
2959954bf92SDavid Howells 			return;
2965eb005caSDavid Howells 	switch (ctx->nfs_server.protocol) {
2979954bf92SDavid Howells 	case XPRT_TRANSPORT_UDP:
2985eb005caSDavid Howells 		ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
2999954bf92SDavid Howells 		break;
3009954bf92SDavid Howells 	case XPRT_TRANSPORT_TCP:
3019954bf92SDavid Howells 	case XPRT_TRANSPORT_RDMA:
3025eb005caSDavid Howells 		ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
3039954bf92SDavid Howells 	}
3049954bf92SDavid Howells }
3059954bf92SDavid Howells 
3069954bf92SDavid Howells /*
3079954bf92SDavid Howells  * Add 'flavor' to 'auth_info' if not already present.
3089954bf92SDavid Howells  * Returns true if 'flavor' ends up in the list, false otherwise
3099954bf92SDavid Howells  */
31062a55d08SScott Mayhew static int nfs_auth_info_add(struct fs_context *fc,
311e558100fSDavid Howells 			     struct nfs_auth_info *auth_info,
3129954bf92SDavid Howells 			     rpc_authflavor_t flavor)
3139954bf92SDavid Howells {
3149954bf92SDavid Howells 	unsigned int i;
3159954bf92SDavid Howells 	unsigned int max_flavor_len = ARRAY_SIZE(auth_info->flavors);
3169954bf92SDavid Howells 
3179954bf92SDavid Howells 	/* make sure this flavor isn't already in the list */
3189954bf92SDavid Howells 	for (i = 0; i < auth_info->flavor_len; i++) {
3199954bf92SDavid Howells 		if (flavor == auth_info->flavors[i])
320e558100fSDavid Howells 			return 0;
3219954bf92SDavid Howells 	}
3229954bf92SDavid Howells 
323ce8866f0SScott Mayhew 	if (auth_info->flavor_len + 1 >= max_flavor_len)
324ce8866f0SScott Mayhew 		return nfs_invalf(fc, "NFS: too many sec= flavors");
3259954bf92SDavid Howells 
3269954bf92SDavid Howells 	auth_info->flavors[auth_info->flavor_len++] = flavor;
327e558100fSDavid Howells 	return 0;
3289954bf92SDavid Howells }
3299954bf92SDavid Howells 
3309954bf92SDavid Howells /*
3319954bf92SDavid Howells  * Parse the value of the 'sec=' option.
3329954bf92SDavid Howells  */
33362a55d08SScott Mayhew static int nfs_parse_security_flavors(struct fs_context *fc,
334e38bb238SScott Mayhew 				      struct fs_parameter *param)
3359954bf92SDavid Howells {
33662a55d08SScott Mayhew 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
3379954bf92SDavid Howells 	rpc_authflavor_t pseudoflavor;
338e38bb238SScott Mayhew 	char *string = param->string, *p;
339e558100fSDavid Howells 	int ret;
3409954bf92SDavid Howells 
341e38bb238SScott Mayhew 	dfprintk(MOUNT, "NFS: parsing %s=%s option\n", param->key, param->string);
3429954bf92SDavid Howells 
343e38bb238SScott Mayhew 	while ((p = strsep(&string, ":")) != NULL) {
344e38bb238SScott Mayhew 		if (!*p)
345e38bb238SScott Mayhew 			continue;
346e38bb238SScott Mayhew 		switch (lookup_constant(nfs_secflavor_tokens, p, -1)) {
3479954bf92SDavid Howells 		case Opt_sec_none:
3489954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_NULL;
3499954bf92SDavid Howells 			break;
3509954bf92SDavid Howells 		case Opt_sec_sys:
3519954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_UNIX;
3529954bf92SDavid Howells 			break;
3539954bf92SDavid Howells 		case Opt_sec_krb5:
3549954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_KRB5;
3559954bf92SDavid Howells 			break;
3569954bf92SDavid Howells 		case Opt_sec_krb5i:
3579954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_KRB5I;
3589954bf92SDavid Howells 			break;
3599954bf92SDavid Howells 		case Opt_sec_krb5p:
3609954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_KRB5P;
3619954bf92SDavid Howells 			break;
3629954bf92SDavid Howells 		case Opt_sec_lkey:
3639954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_LKEY;
3649954bf92SDavid Howells 			break;
3659954bf92SDavid Howells 		case Opt_sec_lkeyi:
3669954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_LKEYI;
3679954bf92SDavid Howells 			break;
3689954bf92SDavid Howells 		case Opt_sec_lkeyp:
3699954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_LKEYP;
3709954bf92SDavid Howells 			break;
3719954bf92SDavid Howells 		case Opt_sec_spkm:
3729954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_SPKM;
3739954bf92SDavid Howells 			break;
3749954bf92SDavid Howells 		case Opt_sec_spkmi:
3759954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_SPKMI;
3769954bf92SDavid Howells 			break;
3779954bf92SDavid Howells 		case Opt_sec_spkmp:
3789954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_SPKMP;
3799954bf92SDavid Howells 			break;
3809954bf92SDavid Howells 		default:
381ce8866f0SScott Mayhew 			return nfs_invalf(fc, "NFS: sec=%s option not recognized", p);
382e558100fSDavid Howells 		}
383e558100fSDavid Howells 
38462a55d08SScott Mayhew 		ret = nfs_auth_info_add(fc, &ctx->auth_info, pseudoflavor);
385e558100fSDavid Howells 		if (ret < 0)
386e558100fSDavid Howells 			return ret;
387e558100fSDavid Howells 	}
388e558100fSDavid Howells 
3899954bf92SDavid Howells 	return 0;
3909954bf92SDavid Howells }
3919954bf92SDavid Howells 
39262a55d08SScott Mayhew static int nfs_parse_version_string(struct fs_context *fc,
393e38bb238SScott Mayhew 				    const char *string)
3949954bf92SDavid Howells {
39562a55d08SScott Mayhew 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
39662a55d08SScott Mayhew 
3975eb005caSDavid Howells 	ctx->flags &= ~NFS_MOUNT_VER3;
398e38bb238SScott Mayhew 	switch (lookup_constant(nfs_vers_tokens, string, -1)) {
3999954bf92SDavid Howells 	case Opt_vers_2:
4005eb005caSDavid Howells 		ctx->version = 2;
4019954bf92SDavid Howells 		break;
4029954bf92SDavid Howells 	case Opt_vers_3:
4035eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_VER3;
4045eb005caSDavid Howells 		ctx->version = 3;
4059954bf92SDavid Howells 		break;
4069954bf92SDavid Howells 	case Opt_vers_4:
4079954bf92SDavid Howells 		/* Backward compatibility option. In future,
4089954bf92SDavid Howells 		 * the mount program should always supply
4099954bf92SDavid Howells 		 * a NFSv4 minor version number.
4109954bf92SDavid Howells 		 */
4115eb005caSDavid Howells 		ctx->version = 4;
4129954bf92SDavid Howells 		break;
4139954bf92SDavid Howells 	case Opt_vers_4_0:
4145eb005caSDavid Howells 		ctx->version = 4;
4155eb005caSDavid Howells 		ctx->minorversion = 0;
4169954bf92SDavid Howells 		break;
4179954bf92SDavid Howells 	case Opt_vers_4_1:
4185eb005caSDavid Howells 		ctx->version = 4;
4195eb005caSDavid Howells 		ctx->minorversion = 1;
4209954bf92SDavid Howells 		break;
4219954bf92SDavid Howells 	case Opt_vers_4_2:
4225eb005caSDavid Howells 		ctx->version = 4;
4235eb005caSDavid Howells 		ctx->minorversion = 2;
4249954bf92SDavid Howells 		break;
4259954bf92SDavid Howells 	default:
426ce8866f0SScott Mayhew 		return nfs_invalf(fc, "NFS: Unsupported NFS version");
4279954bf92SDavid Howells 	}
428e558100fSDavid Howells 	return 0;
4299954bf92SDavid Howells }
4309954bf92SDavid Howells 
4319954bf92SDavid Howells /*
432e38bb238SScott Mayhew  * Parse a single mount parameter.
4339954bf92SDavid Howells  */
434f2aedb71SDavid Howells static int nfs_fs_context_parse_param(struct fs_context *fc,
435e38bb238SScott Mayhew 				      struct fs_parameter *param)
4369954bf92SDavid Howells {
437e38bb238SScott Mayhew 	struct fs_parse_result result;
438f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
439e38bb238SScott Mayhew 	unsigned short protofamily, mountfamily;
440e38bb238SScott Mayhew 	unsigned int len;
441e38bb238SScott Mayhew 	int ret, opt;
4429954bf92SDavid Howells 
443e38bb238SScott Mayhew 	dfprintk(MOUNT, "NFS:   parsing nfs mount option '%s'\n", param->key);
4449954bf92SDavid Howells 
445f2aedb71SDavid Howells 	opt = fs_parse(fc, &nfs_fs_parameters, param, &result);
446e38bb238SScott Mayhew 	if (opt < 0)
447e38bb238SScott Mayhew 		return ctx->sloppy ? 1 : opt;
448e38bb238SScott Mayhew 
449e38bb238SScott Mayhew 	switch (opt) {
450f2aedb71SDavid Howells 	case Opt_source:
451ce8866f0SScott Mayhew 		if (fc->source)
452ce8866f0SScott Mayhew 			return nfs_invalf(fc, "NFS: Multiple sources not supported");
453f2aedb71SDavid Howells 		fc->source = param->string;
454f2aedb71SDavid Howells 		param->string = NULL;
455f2aedb71SDavid Howells 		break;
456f2aedb71SDavid Howells 
4579954bf92SDavid Howells 		/*
4589954bf92SDavid Howells 		 * boolean options:  foo/nofoo
4599954bf92SDavid Howells 		 */
4609954bf92SDavid Howells 	case Opt_soft:
4615eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_SOFT;
4625eb005caSDavid Howells 		ctx->flags &= ~NFS_MOUNT_SOFTERR;
4639954bf92SDavid Howells 		break;
4649954bf92SDavid Howells 	case Opt_softerr:
465c74dfe97STrond Myklebust 		ctx->flags |= NFS_MOUNT_SOFTERR | NFS_MOUNT_SOFTREVAL;
4665eb005caSDavid Howells 		ctx->flags &= ~NFS_MOUNT_SOFT;
4679954bf92SDavid Howells 		break;
4689954bf92SDavid Howells 	case Opt_hard:
469c74dfe97STrond Myklebust 		ctx->flags &= ~(NFS_MOUNT_SOFT |
470c74dfe97STrond Myklebust 				NFS_MOUNT_SOFTERR |
471c74dfe97STrond Myklebust 				NFS_MOUNT_SOFTREVAL);
472c74dfe97STrond Myklebust 		break;
473c74dfe97STrond Myklebust 	case Opt_softreval:
474c74dfe97STrond Myklebust 		if (result.negated)
475c74dfe97STrond Myklebust 			ctx->flags &= ~NFS_MOUNT_SOFTREVAL;
476c74dfe97STrond Myklebust 		else
477c74dfe97STrond Myklebust 			ctx->flags &= NFS_MOUNT_SOFTREVAL;
4789954bf92SDavid Howells 		break;
4799954bf92SDavid Howells 	case Opt_posix:
480e38bb238SScott Mayhew 		if (result.negated)
481e38bb238SScott Mayhew 			ctx->flags &= ~NFS_MOUNT_POSIX;
482e38bb238SScott Mayhew 		else
4835eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_POSIX;
4849954bf92SDavid Howells 		break;
4859954bf92SDavid Howells 	case Opt_cto:
486e38bb238SScott Mayhew 		if (result.negated)
487e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NOCTO;
488e38bb238SScott Mayhew 		else
4895eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NOCTO;
4909954bf92SDavid Howells 		break;
4919954bf92SDavid Howells 	case Opt_ac:
492e38bb238SScott Mayhew 		if (result.negated)
493e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NOAC;
494e38bb238SScott Mayhew 		else
4955eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NOAC;
4969954bf92SDavid Howells 		break;
4979954bf92SDavid Howells 	case Opt_lock:
498e38bb238SScott Mayhew 		if (result.negated) {
4995eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_NONLM;
500e558100fSDavid Howells 			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
501e38bb238SScott Mayhew 		} else {
502e38bb238SScott Mayhew 			ctx->flags &= ~NFS_MOUNT_NONLM;
503e38bb238SScott Mayhew 			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
504e38bb238SScott Mayhew 		}
5059954bf92SDavid Howells 		break;
5069954bf92SDavid Howells 	case Opt_udp:
5075eb005caSDavid Howells 		ctx->flags &= ~NFS_MOUNT_TCP;
5085eb005caSDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
5099954bf92SDavid Howells 		break;
5109954bf92SDavid Howells 	case Opt_tcp:
5115eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_TCP;
5125eb005caSDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
5139954bf92SDavid Howells 		break;
5149954bf92SDavid Howells 	case Opt_rdma:
5155eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_TCP; /* for side protocols */
5165eb005caSDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
517e38bb238SScott Mayhew 		xprt_load_transport(param->key);
5189954bf92SDavid Howells 		break;
5199954bf92SDavid Howells 	case Opt_acl:
520e38bb238SScott Mayhew 		if (result.negated)
521e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NOACL;
522e38bb238SScott Mayhew 		else
5235eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NOACL;
5249954bf92SDavid Howells 		break;
5259954bf92SDavid Howells 	case Opt_rdirplus:
526e38bb238SScott Mayhew 		if (result.negated)
527e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NORDIRPLUS;
528e38bb238SScott Mayhew 		else
5295eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
5309954bf92SDavid Howells 		break;
5319954bf92SDavid Howells 	case Opt_sharecache:
532e38bb238SScott Mayhew 		if (result.negated)
533e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_UNSHARED;
534e38bb238SScott Mayhew 		else
5355eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_UNSHARED;
5369954bf92SDavid Howells 		break;
5379954bf92SDavid Howells 	case Opt_resvport:
538e38bb238SScott Mayhew 		if (result.negated)
539e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NORESVPORT;
540e38bb238SScott Mayhew 		else
5415eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NORESVPORT;
5429954bf92SDavid Howells 		break;
5439954bf92SDavid Howells 	case Opt_fscache:
5445eb005caSDavid Howells 		kfree(ctx->fscache_uniq);
545e38bb238SScott Mayhew 		ctx->fscache_uniq = param->string;
546e38bb238SScott Mayhew 		param->string = NULL;
547e38bb238SScott Mayhew 		if (result.negated)
5485eb005caSDavid Howells 			ctx->options &= ~NFS_OPTION_FSCACHE;
549e38bb238SScott Mayhew 		else
550e38bb238SScott Mayhew 			ctx->options |= NFS_OPTION_FSCACHE;
5519954bf92SDavid Howells 		break;
5529954bf92SDavid Howells 	case Opt_migration:
553e38bb238SScott Mayhew 		if (result.negated)
5545eb005caSDavid Howells 			ctx->options &= ~NFS_OPTION_MIGRATION;
555e38bb238SScott Mayhew 		else
556e38bb238SScott Mayhew 			ctx->options |= NFS_OPTION_MIGRATION;
5579954bf92SDavid Howells 		break;
5589954bf92SDavid Howells 
5599954bf92SDavid Howells 		/*
5609954bf92SDavid Howells 		 * options that take numeric values
5619954bf92SDavid Howells 		 */
5629954bf92SDavid Howells 	case Opt_port:
563e38bb238SScott Mayhew 		if (result.uint_32 > USHRT_MAX)
564e38bb238SScott Mayhew 			goto out_of_bounds;
565e38bb238SScott Mayhew 		ctx->nfs_server.port = result.uint_32;
5669954bf92SDavid Howells 		break;
5679954bf92SDavid Howells 	case Opt_rsize:
568e38bb238SScott Mayhew 		ctx->rsize = result.uint_32;
5699954bf92SDavid Howells 		break;
5709954bf92SDavid Howells 	case Opt_wsize:
571e38bb238SScott Mayhew 		ctx->wsize = result.uint_32;
5729954bf92SDavid Howells 		break;
5739954bf92SDavid Howells 	case Opt_bsize:
574e38bb238SScott Mayhew 		ctx->bsize = result.uint_32;
5759954bf92SDavid Howells 		break;
5769954bf92SDavid Howells 	case Opt_timeo:
577e38bb238SScott Mayhew 		if (result.uint_32 < 1 || result.uint_32 > INT_MAX)
578e38bb238SScott Mayhew 			goto out_of_bounds;
579e38bb238SScott Mayhew 		ctx->timeo = result.uint_32;
5809954bf92SDavid Howells 		break;
5819954bf92SDavid Howells 	case Opt_retrans:
582e38bb238SScott Mayhew 		if (result.uint_32 > INT_MAX)
583e38bb238SScott Mayhew 			goto out_of_bounds;
584e38bb238SScott Mayhew 		ctx->retrans = result.uint_32;
5859954bf92SDavid Howells 		break;
5869954bf92SDavid Howells 	case Opt_acregmin:
587e38bb238SScott Mayhew 		ctx->acregmin = result.uint_32;
5889954bf92SDavid Howells 		break;
5899954bf92SDavid Howells 	case Opt_acregmax:
590e38bb238SScott Mayhew 		ctx->acregmax = result.uint_32;
5919954bf92SDavid Howells 		break;
5929954bf92SDavid Howells 	case Opt_acdirmin:
593e38bb238SScott Mayhew 		ctx->acdirmin = result.uint_32;
5949954bf92SDavid Howells 		break;
5959954bf92SDavid Howells 	case Opt_acdirmax:
596e38bb238SScott Mayhew 		ctx->acdirmax = result.uint_32;
5979954bf92SDavid Howells 		break;
5989954bf92SDavid Howells 	case Opt_actimeo:
599e38bb238SScott Mayhew 		ctx->acregmin = result.uint_32;
600e38bb238SScott Mayhew 		ctx->acregmax = result.uint_32;
601e38bb238SScott Mayhew 		ctx->acdirmin = result.uint_32;
602e38bb238SScott Mayhew 		ctx->acdirmax = result.uint_32;
6039954bf92SDavid Howells 		break;
6049954bf92SDavid Howells 	case Opt_namelen:
605e38bb238SScott Mayhew 		ctx->namlen = result.uint_32;
6069954bf92SDavid Howells 		break;
6079954bf92SDavid Howells 	case Opt_mountport:
608e38bb238SScott Mayhew 		if (result.uint_32 > USHRT_MAX)
609e38bb238SScott Mayhew 			goto out_of_bounds;
610e38bb238SScott Mayhew 		ctx->mount_server.port = result.uint_32;
6119954bf92SDavid Howells 		break;
6129954bf92SDavid Howells 	case Opt_mountvers:
613e38bb238SScott Mayhew 		if (result.uint_32 < NFS_MNT_VERSION ||
614e38bb238SScott Mayhew 		    result.uint_32 > NFS_MNT3_VERSION)
615e38bb238SScott Mayhew 			goto out_of_bounds;
616e38bb238SScott Mayhew 		ctx->mount_server.version = result.uint_32;
6179954bf92SDavid Howells 		break;
6189954bf92SDavid Howells 	case Opt_minorversion:
619e38bb238SScott Mayhew 		if (result.uint_32 > NFS4_MAX_MINOR_VERSION)
620e38bb238SScott Mayhew 			goto out_of_bounds;
621e38bb238SScott Mayhew 		ctx->minorversion = result.uint_32;
6229954bf92SDavid Howells 		break;
6239954bf92SDavid Howells 
6249954bf92SDavid Howells 		/*
6259954bf92SDavid Howells 		 * options that take text values
6269954bf92SDavid Howells 		 */
627e38bb238SScott Mayhew 	case Opt_v:
62862a55d08SScott Mayhew 		ret = nfs_parse_version_string(fc, param->key + 1);
629e38bb238SScott Mayhew 		if (ret < 0)
630e38bb238SScott Mayhew 			return ret;
631e38bb238SScott Mayhew 		break;
632e38bb238SScott Mayhew 	case Opt_vers:
63362a55d08SScott Mayhew 		ret = nfs_parse_version_string(fc, param->string);
634e558100fSDavid Howells 		if (ret < 0)
635e558100fSDavid Howells 			return ret;
6369954bf92SDavid Howells 		break;
6379954bf92SDavid Howells 	case Opt_sec:
63862a55d08SScott Mayhew 		ret = nfs_parse_security_flavors(fc, param);
639e558100fSDavid Howells 		if (ret < 0)
640e558100fSDavid Howells 			return ret;
6419954bf92SDavid Howells 		break;
6429954bf92SDavid Howells 
643e38bb238SScott Mayhew 	case Opt_proto:
644e38bb238SScott Mayhew 		protofamily = AF_INET;
645e38bb238SScott Mayhew 		switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
6469954bf92SDavid Howells 		case Opt_xprt_udp6:
647e38bb238SScott Mayhew 			protofamily = AF_INET6;
6489954bf92SDavid Howells 			/* fall through */
6499954bf92SDavid Howells 		case Opt_xprt_udp:
6505eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_TCP;
6515eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
6529954bf92SDavid Howells 			break;
6539954bf92SDavid Howells 		case Opt_xprt_tcp6:
654e38bb238SScott Mayhew 			protofamily = AF_INET6;
6559954bf92SDavid Howells 			/* fall through */
6569954bf92SDavid Howells 		case Opt_xprt_tcp:
6575eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_TCP;
6585eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
6599954bf92SDavid Howells 			break;
6609954bf92SDavid Howells 		case Opt_xprt_rdma6:
661e38bb238SScott Mayhew 			protofamily = AF_INET6;
6629954bf92SDavid Howells 			/* fall through */
6639954bf92SDavid Howells 		case Opt_xprt_rdma:
6649954bf92SDavid Howells 			/* vector side protocols to TCP */
6655eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_TCP;
6665eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
667e38bb238SScott Mayhew 			xprt_load_transport(param->string);
6689954bf92SDavid Howells 			break;
6699954bf92SDavid Howells 		default:
670ce8866f0SScott Mayhew 			return nfs_invalf(fc, "NFS: Unrecognized transport protocol");
6719954bf92SDavid Howells 		}
6729954bf92SDavid Howells 
673e38bb238SScott Mayhew 		ctx->protofamily = protofamily;
674e38bb238SScott Mayhew 		break;
675e38bb238SScott Mayhew 
676e38bb238SScott Mayhew 	case Opt_mountproto:
677e38bb238SScott Mayhew 		mountfamily = AF_INET;
678e38bb238SScott Mayhew 		switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
6799954bf92SDavid Howells 		case Opt_xprt_udp6:
680e38bb238SScott Mayhew 			mountfamily = AF_INET6;
6819954bf92SDavid Howells 			/* fall through */
6829954bf92SDavid Howells 		case Opt_xprt_udp:
6835eb005caSDavid Howells 			ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
6849954bf92SDavid Howells 			break;
6859954bf92SDavid Howells 		case Opt_xprt_tcp6:
686e38bb238SScott Mayhew 			mountfamily = AF_INET6;
6879954bf92SDavid Howells 			/* fall through */
6889954bf92SDavid Howells 		case Opt_xprt_tcp:
6895eb005caSDavid Howells 			ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
6909954bf92SDavid Howells 			break;
6919954bf92SDavid Howells 		case Opt_xprt_rdma: /* not used for side protocols */
6929954bf92SDavid Howells 		default:
693ce8866f0SScott Mayhew 			return nfs_invalf(fc, "NFS: Unrecognized transport protocol");
6949954bf92SDavid Howells 		}
695e38bb238SScott Mayhew 		ctx->mountfamily = mountfamily;
6969954bf92SDavid Howells 		break;
697e38bb238SScott Mayhew 
6989954bf92SDavid Howells 	case Opt_addr:
69962a55d08SScott Mayhew 		len = rpc_pton(fc->net_ns, param->string, param->size,
7005eb005caSDavid Howells 			       &ctx->nfs_server.address,
701e558100fSDavid Howells 			       sizeof(ctx->nfs_server._address));
702e38bb238SScott Mayhew 		if (len == 0)
7039954bf92SDavid Howells 			goto out_invalid_address;
704e38bb238SScott Mayhew 		ctx->nfs_server.addrlen = len;
7059954bf92SDavid Howells 		break;
7069954bf92SDavid Howells 	case Opt_clientaddr:
707e38bb238SScott Mayhew 		kfree(ctx->client_address);
708e38bb238SScott Mayhew 		ctx->client_address = param->string;
709e38bb238SScott Mayhew 		param->string = NULL;
7109954bf92SDavid Howells 		break;
7119954bf92SDavid Howells 	case Opt_mounthost:
712e38bb238SScott Mayhew 		kfree(ctx->mount_server.hostname);
713e38bb238SScott Mayhew 		ctx->mount_server.hostname = param->string;
714e38bb238SScott Mayhew 		param->string = NULL;
7159954bf92SDavid Howells 		break;
7169954bf92SDavid Howells 	case Opt_mountaddr:
71762a55d08SScott Mayhew 		len = rpc_pton(fc->net_ns, param->string, param->size,
7185eb005caSDavid Howells 			       &ctx->mount_server.address,
719e558100fSDavid Howells 			       sizeof(ctx->mount_server._address));
720e38bb238SScott Mayhew 		if (len == 0)
7219954bf92SDavid Howells 			goto out_invalid_address;
722e38bb238SScott Mayhew 		ctx->mount_server.addrlen = len;
7239954bf92SDavid Howells 		break;
7249954bf92SDavid Howells 	case Opt_nconnect:
725e38bb238SScott Mayhew 		if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_CONNECTIONS)
726e38bb238SScott Mayhew 			goto out_of_bounds;
727e38bb238SScott Mayhew 		ctx->nfs_server.nconnect = result.uint_32;
7289954bf92SDavid Howells 		break;
7299954bf92SDavid Howells 	case Opt_lookupcache:
730e38bb238SScott Mayhew 		switch (result.uint_32) {
7319954bf92SDavid Howells 		case Opt_lookupcache_all:
7325eb005caSDavid Howells 			ctx->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
7339954bf92SDavid Howells 			break;
7349954bf92SDavid Howells 		case Opt_lookupcache_positive:
7355eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
7365eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
7379954bf92SDavid Howells 			break;
7389954bf92SDavid Howells 		case Opt_lookupcache_none:
7395eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
7409954bf92SDavid Howells 			break;
7419954bf92SDavid Howells 		default:
742e38bb238SScott Mayhew 			goto out_invalid_value;
7439954bf92SDavid Howells 		}
7449954bf92SDavid Howells 		break;
7459954bf92SDavid Howells 	case Opt_local_lock:
746e38bb238SScott Mayhew 		switch (result.uint_32) {
7479954bf92SDavid Howells 		case Opt_local_lock_all:
7485eb005caSDavid Howells 			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK |
7499954bf92SDavid Howells 				       NFS_MOUNT_LOCAL_FCNTL);
7509954bf92SDavid Howells 			break;
7519954bf92SDavid Howells 		case Opt_local_lock_flock:
7525eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOCAL_FLOCK;
7539954bf92SDavid Howells 			break;
7549954bf92SDavid Howells 		case Opt_local_lock_posix:
7555eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOCAL_FCNTL;
7569954bf92SDavid Howells 			break;
7579954bf92SDavid Howells 		case Opt_local_lock_none:
7585eb005caSDavid Howells 			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
7599954bf92SDavid Howells 					NFS_MOUNT_LOCAL_FCNTL);
7609954bf92SDavid Howells 			break;
7619954bf92SDavid Howells 		default:
762e38bb238SScott Mayhew 			goto out_invalid_value;
7639954bf92SDavid Howells 		}
7649954bf92SDavid Howells 		break;
7659954bf92SDavid Howells 
7669954bf92SDavid Howells 		/*
7679954bf92SDavid Howells 		 * Special options
7689954bf92SDavid Howells 		 */
7699954bf92SDavid Howells 	case Opt_sloppy:
770e38bb238SScott Mayhew 		ctx->sloppy = true;
7719954bf92SDavid Howells 		dfprintk(MOUNT, "NFS:   relaxing parsing rules\n");
7729954bf92SDavid Howells 		break;
7739954bf92SDavid Howells 	}
7749954bf92SDavid Howells 
775f8ee01e3SDavid Howells 	return 0;
776f8ee01e3SDavid Howells 
777f8ee01e3SDavid Howells out_invalid_value:
778ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Bad mount option value specified");
779e38bb238SScott Mayhew out_invalid_address:
780ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Bad IP address specified");
781e38bb238SScott Mayhew out_of_bounds:
782*3a21409aSDavid Howells 	return nfs_invalf(fc, "NFS: Value for '%s' out of range", param->key);
783e38bb238SScott Mayhew }
784e38bb238SScott Mayhew 
7859954bf92SDavid Howells /*
78662a55d08SScott Mayhew  * Split fc->source into "hostname:export_path".
7879954bf92SDavid Howells  *
7889954bf92SDavid Howells  * The leftmost colon demarks the split between the server's hostname
7899954bf92SDavid Howells  * and the export path.  If the hostname starts with a left square
7909954bf92SDavid Howells  * bracket, then it may contain colons.
7919954bf92SDavid Howells  *
7929954bf92SDavid Howells  * Note: caller frees hostname and export path, even on error.
7939954bf92SDavid Howells  */
79462a55d08SScott Mayhew static int nfs_parse_source(struct fs_context *fc,
795e558100fSDavid Howells 			    size_t maxnamlen, size_t maxpathlen)
7969954bf92SDavid Howells {
79762a55d08SScott Mayhew 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
79862a55d08SScott Mayhew 	const char *dev_name = fc->source;
7999954bf92SDavid Howells 	size_t len;
80062a55d08SScott Mayhew 	const char *end;
8019954bf92SDavid Howells 
8029954bf92SDavid Howells 	if (unlikely(!dev_name || !*dev_name)) {
8039954bf92SDavid Howells 		dfprintk(MOUNT, "NFS: device name not specified\n");
8049954bf92SDavid Howells 		return -EINVAL;
8059954bf92SDavid Howells 	}
8069954bf92SDavid Howells 
8079954bf92SDavid Howells 	/* Is the host name protected with square brakcets? */
8089954bf92SDavid Howells 	if (*dev_name == '[') {
8099954bf92SDavid Howells 		end = strchr(++dev_name, ']');
8109954bf92SDavid Howells 		if (end == NULL || end[1] != ':')
8119954bf92SDavid Howells 			goto out_bad_devname;
8129954bf92SDavid Howells 
8139954bf92SDavid Howells 		len = end - dev_name;
8149954bf92SDavid Howells 		end++;
8159954bf92SDavid Howells 	} else {
81662a55d08SScott Mayhew 		const char *comma;
8179954bf92SDavid Howells 
8189954bf92SDavid Howells 		end = strchr(dev_name, ':');
8199954bf92SDavid Howells 		if (end == NULL)
8209954bf92SDavid Howells 			goto out_bad_devname;
8219954bf92SDavid Howells 		len = end - dev_name;
8229954bf92SDavid Howells 
8239954bf92SDavid Howells 		/* kill possible hostname list: not supported */
82462a55d08SScott Mayhew 		comma = memchr(dev_name, ',', len);
82562a55d08SScott Mayhew 		if (comma)
8269954bf92SDavid Howells 			len = comma - dev_name;
8279954bf92SDavid Howells 	}
8289954bf92SDavid Howells 
8299954bf92SDavid Howells 	if (len > maxnamlen)
8309954bf92SDavid Howells 		goto out_hostname;
8319954bf92SDavid Howells 
8329954bf92SDavid Howells 	/* N.B. caller will free nfs_server.hostname in all cases */
833e558100fSDavid Howells 	ctx->nfs_server.hostname = kmemdup_nul(dev_name, len, GFP_KERNEL);
834e558100fSDavid Howells 	if (!ctx->nfs_server.hostname)
8359954bf92SDavid Howells 		goto out_nomem;
8369954bf92SDavid Howells 	len = strlen(++end);
8379954bf92SDavid Howells 	if (len > maxpathlen)
8389954bf92SDavid Howells 		goto out_path;
839e558100fSDavid Howells 	ctx->nfs_server.export_path = kmemdup_nul(end, len, GFP_KERNEL);
840e558100fSDavid Howells 	if (!ctx->nfs_server.export_path)
8419954bf92SDavid Howells 		goto out_nomem;
8429954bf92SDavid Howells 
843e558100fSDavid Howells 	dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", ctx->nfs_server.export_path);
8449954bf92SDavid Howells 	return 0;
8459954bf92SDavid Howells 
8469954bf92SDavid Howells out_bad_devname:
847ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: device name not in host:path format");
8489954bf92SDavid Howells out_nomem:
849ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: not enough memory to parse device name");
8509954bf92SDavid Howells 	return -ENOMEM;
8519954bf92SDavid Howells out_hostname:
852ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: server hostname too long");
8539954bf92SDavid Howells 	return -ENAMETOOLONG;
8549954bf92SDavid Howells out_path:
855ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: export pathname too long");
8569954bf92SDavid Howells 	return -ENAMETOOLONG;
8579954bf92SDavid Howells }
8589954bf92SDavid Howells 
859f2aedb71SDavid Howells static inline bool is_remount_fc(struct fs_context *fc)
860f2aedb71SDavid Howells {
861f2aedb71SDavid Howells 	return fc->root != NULL;
862f2aedb71SDavid Howells }
863f2aedb71SDavid Howells 
8649954bf92SDavid Howells /*
865e558100fSDavid Howells  * Parse monolithic NFS2/NFS3 mount data
8669954bf92SDavid Howells  * - fills in the mount root filehandle
8679954bf92SDavid Howells  *
8689954bf92SDavid Howells  * For option strings, user space handles the following behaviors:
8699954bf92SDavid Howells  *
8709954bf92SDavid Howells  * + DNS: mapping server host name to IP address ("addr=" option)
8719954bf92SDavid Howells  *
8729954bf92SDavid Howells  * + failure mode: how to behave if a mount request can't be handled
8739954bf92SDavid Howells  *   immediately ("fg/bg" option)
8749954bf92SDavid Howells  *
8759954bf92SDavid Howells  * + retry: how often to retry a mount request ("retry=" option)
8769954bf92SDavid Howells  *
8779954bf92SDavid Howells  * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
8789954bf92SDavid Howells  *   mountproto=tcp after mountproto=udp, and so on
8799954bf92SDavid Howells  */
880f2aedb71SDavid Howells static int nfs23_parse_monolithic(struct fs_context *fc,
881f2aedb71SDavid Howells 				  struct nfs_mount_data *data)
8829954bf92SDavid Howells {
883f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
88462a55d08SScott Mayhew 	struct nfs_fh *mntfh = ctx->mntfh;
8855eb005caSDavid Howells 	struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address;
8869954bf92SDavid Howells 	int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
8879954bf92SDavid Howells 
8889954bf92SDavid Howells 	if (data == NULL)
8899954bf92SDavid Howells 		goto out_no_data;
8909954bf92SDavid Howells 
8915eb005caSDavid Howells 	ctx->version = NFS_DEFAULT_VERSION;
8929954bf92SDavid Howells 	switch (data->version) {
8939954bf92SDavid Howells 	case 1:
8949954bf92SDavid Howells 		data->namlen = 0; /* fall through */
8959954bf92SDavid Howells 	case 2:
8969954bf92SDavid Howells 		data->bsize = 0; /* fall through */
8979954bf92SDavid Howells 	case 3:
8989954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_VER3)
8999954bf92SDavid Howells 			goto out_no_v3;
9009954bf92SDavid Howells 		data->root.size = NFS2_FHSIZE;
9019954bf92SDavid Howells 		memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
9029954bf92SDavid Howells 		/* Turn off security negotiation */
9039954bf92SDavid Howells 		extra_flags |= NFS_MOUNT_SECFLAVOUR;
9049954bf92SDavid Howells 		/* fall through */
9059954bf92SDavid Howells 	case 4:
9069954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_SECFLAVOUR)
9079954bf92SDavid Howells 			goto out_no_sec;
9089954bf92SDavid Howells 		/* fall through */
9099954bf92SDavid Howells 	case 5:
9109954bf92SDavid Howells 		memset(data->context, 0, sizeof(data->context));
9119954bf92SDavid Howells 		/* fall through */
9129954bf92SDavid Howells 	case 6:
9139954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_VER3) {
9149954bf92SDavid Howells 			if (data->root.size > NFS3_FHSIZE || data->root.size == 0)
9159954bf92SDavid Howells 				goto out_invalid_fh;
9169954bf92SDavid Howells 			mntfh->size = data->root.size;
9175eb005caSDavid Howells 			ctx->version = 3;
9189954bf92SDavid Howells 		} else {
9199954bf92SDavid Howells 			mntfh->size = NFS2_FHSIZE;
9205eb005caSDavid Howells 			ctx->version = 2;
9219954bf92SDavid Howells 		}
9229954bf92SDavid Howells 
9239954bf92SDavid Howells 
9249954bf92SDavid Howells 		memcpy(mntfh->data, data->root.data, mntfh->size);
9259954bf92SDavid Howells 		if (mntfh->size < sizeof(mntfh->data))
9269954bf92SDavid Howells 			memset(mntfh->data + mntfh->size, 0,
9279954bf92SDavid Howells 			       sizeof(mntfh->data) - mntfh->size);
9289954bf92SDavid Howells 
9299954bf92SDavid Howells 		/*
9305eb005caSDavid Howells 		 * Translate to nfs_fs_context, which nfs_fill_super
9319954bf92SDavid Howells 		 * can deal with.
9329954bf92SDavid Howells 		 */
9335eb005caSDavid Howells 		ctx->flags	= data->flags & NFS_MOUNT_FLAGMASK;
9345eb005caSDavid Howells 		ctx->flags	|= extra_flags;
9355eb005caSDavid Howells 		ctx->rsize	= data->rsize;
9365eb005caSDavid Howells 		ctx->wsize	= data->wsize;
9375eb005caSDavid Howells 		ctx->timeo	= data->timeo;
9385eb005caSDavid Howells 		ctx->retrans	= data->retrans;
9395eb005caSDavid Howells 		ctx->acregmin	= data->acregmin;
9405eb005caSDavid Howells 		ctx->acregmax	= data->acregmax;
9415eb005caSDavid Howells 		ctx->acdirmin	= data->acdirmin;
9425eb005caSDavid Howells 		ctx->acdirmax	= data->acdirmax;
9435eb005caSDavid Howells 		ctx->need_mount	= false;
9449954bf92SDavid Howells 
9459954bf92SDavid Howells 		memcpy(sap, &data->addr, sizeof(data->addr));
9465eb005caSDavid Howells 		ctx->nfs_server.addrlen = sizeof(data->addr);
9475eb005caSDavid Howells 		ctx->nfs_server.port = ntohs(data->addr.sin_port);
9489954bf92SDavid Howells 		if (sap->sa_family != AF_INET ||
9499954bf92SDavid Howells 		    !nfs_verify_server_address(sap))
9509954bf92SDavid Howells 			goto out_no_address;
9519954bf92SDavid Howells 
9529954bf92SDavid Howells 		if (!(data->flags & NFS_MOUNT_TCP))
9535eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
9549954bf92SDavid Howells 		/* N.B. caller will free nfs_server.hostname in all cases */
9555eb005caSDavid Howells 		ctx->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
956f2aedb71SDavid Howells 		if (!ctx->nfs_server.hostname)
957f2aedb71SDavid Howells 			goto out_nomem;
958f2aedb71SDavid Howells 
9595eb005caSDavid Howells 		ctx->namlen		= data->namlen;
9605eb005caSDavid Howells 		ctx->bsize		= data->bsize;
9619954bf92SDavid Howells 
9629954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_SECFLAVOUR)
9635eb005caSDavid Howells 			ctx->selected_flavor = data->pseudoflavor;
9649954bf92SDavid Howells 		else
9655eb005caSDavid Howells 			ctx->selected_flavor = RPC_AUTH_UNIX;
9669954bf92SDavid Howells 
9679954bf92SDavid Howells 		if (!(data->flags & NFS_MOUNT_NONLM))
9685eb005caSDavid Howells 			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK|
9699954bf92SDavid Howells 					 NFS_MOUNT_LOCAL_FCNTL);
9709954bf92SDavid Howells 		else
9715eb005caSDavid Howells 			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK|
9729954bf92SDavid Howells 					NFS_MOUNT_LOCAL_FCNTL);
97362a55d08SScott Mayhew 
9749954bf92SDavid Howells 		/*
9759954bf92SDavid Howells 		 * The legacy version 6 binary mount data from userspace has a
9769954bf92SDavid Howells 		 * field used only to transport selinux information into the
9779954bf92SDavid Howells 		 * the kernel.  To continue to support that functionality we
9789954bf92SDavid Howells 		 * have a touch of selinux knowledge here in the NFS code. The
9799954bf92SDavid Howells 		 * userspace code converted context=blah to just blah so we are
9809954bf92SDavid Howells 		 * converting back to the full string selinux understands.
9819954bf92SDavid Howells 		 */
9829954bf92SDavid Howells 		if (data->context[0]){
9839954bf92SDavid Howells #ifdef CONFIG_SECURITY_SELINUX
984f2aedb71SDavid Howells 			int ret;
985f2aedb71SDavid Howells 
9869954bf92SDavid Howells 			data->context[NFS_MAX_CONTEXT_LEN] = '\0';
987f2aedb71SDavid Howells 			ret = vfs_parse_fs_string(fc, "context",
988f2aedb71SDavid Howells 						  data->context, strlen(data->context));
989f2aedb71SDavid Howells 			if (ret < 0)
990f2aedb71SDavid Howells 				return ret;
9919954bf92SDavid Howells #else
9929954bf92SDavid Howells 			return -EINVAL;
9939954bf92SDavid Howells #endif
9949954bf92SDavid Howells 		}
9959954bf92SDavid Howells 
9969954bf92SDavid Howells 		break;
9979954bf92SDavid Howells 	default:
998f2aedb71SDavid Howells 		goto generic;
9999954bf92SDavid Howells 	}
10009954bf92SDavid Howells 
1001f2aedb71SDavid Howells 	ctx->skip_reconfig_option_check = true;
10029954bf92SDavid Howells 	return 0;
10039954bf92SDavid Howells 
1004f2aedb71SDavid Howells generic:
1005f2aedb71SDavid Howells 	return generic_parse_monolithic(fc, data);
1006f2aedb71SDavid Howells 
10079954bf92SDavid Howells out_no_data:
1008f2aedb71SDavid Howells 	if (is_remount_fc(fc)) {
1009f2aedb71SDavid Howells 		ctx->skip_reconfig_option_check = true;
1010f2aedb71SDavid Howells 		return 0;
1011f2aedb71SDavid Howells 	}
1012ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: mount program didn't pass any mount data");
10139954bf92SDavid Howells 
10149954bf92SDavid Howells out_no_v3:
1015ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: nfs_mount_data version does not support v3");
10169954bf92SDavid Howells 
10179954bf92SDavid Howells out_no_sec:
1018ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: nfs_mount_data version supports only AUTH_SYS");
10199954bf92SDavid Howells 
10209954bf92SDavid Howells out_nomem:
1021ce8866f0SScott Mayhew 	dfprintk(MOUNT, "NFS: not enough memory to handle mount options");
10229954bf92SDavid Howells 	return -ENOMEM;
10239954bf92SDavid Howells 
10249954bf92SDavid Howells out_no_address:
1025ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: mount program didn't pass remote address");
10269954bf92SDavid Howells 
10279954bf92SDavid Howells out_invalid_fh:
1028ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: invalid root filehandle");
10299954bf92SDavid Howells }
10309954bf92SDavid Howells 
10319954bf92SDavid Howells #if IS_ENABLED(CONFIG_NFS_V4)
10329954bf92SDavid Howells /*
10339954bf92SDavid Howells  * Validate NFSv4 mount options
10349954bf92SDavid Howells  */
1035f2aedb71SDavid Howells static int nfs4_parse_monolithic(struct fs_context *fc,
1036f2aedb71SDavid Howells 				 struct nfs4_mount_data *data)
10379954bf92SDavid Howells {
1038f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
10395eb005caSDavid Howells 	struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address;
10409954bf92SDavid Howells 	char *c;
10419954bf92SDavid Howells 
10429954bf92SDavid Howells 	if (data == NULL)
10439954bf92SDavid Howells 		goto out_no_data;
10449954bf92SDavid Howells 
10455eb005caSDavid Howells 	ctx->version = 4;
10469954bf92SDavid Howells 
10479954bf92SDavid Howells 	switch (data->version) {
10489954bf92SDavid Howells 	case 1:
10495eb005caSDavid Howells 		if (data->host_addrlen > sizeof(ctx->nfs_server.address))
10509954bf92SDavid Howells 			goto out_no_address;
10519954bf92SDavid Howells 		if (data->host_addrlen == 0)
10529954bf92SDavid Howells 			goto out_no_address;
10535eb005caSDavid Howells 		ctx->nfs_server.addrlen = data->host_addrlen;
10549954bf92SDavid Howells 		if (copy_from_user(sap, data->host_addr, data->host_addrlen))
10559954bf92SDavid Howells 			return -EFAULT;
10569954bf92SDavid Howells 		if (!nfs_verify_server_address(sap))
10579954bf92SDavid Howells 			goto out_no_address;
10585eb005caSDavid Howells 		ctx->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
10599954bf92SDavid Howells 
10609954bf92SDavid Howells 		if (data->auth_flavourlen) {
10619954bf92SDavid Howells 			rpc_authflavor_t pseudoflavor;
10629954bf92SDavid Howells 			if (data->auth_flavourlen > 1)
10639954bf92SDavid Howells 				goto out_inval_auth;
10649954bf92SDavid Howells 			if (copy_from_user(&pseudoflavor,
10659954bf92SDavid Howells 					   data->auth_flavours,
10669954bf92SDavid Howells 					   sizeof(pseudoflavor)))
10679954bf92SDavid Howells 				return -EFAULT;
10685eb005caSDavid Howells 			ctx->selected_flavor = pseudoflavor;
10699954bf92SDavid Howells 		} else
10705eb005caSDavid Howells 			ctx->selected_flavor = RPC_AUTH_UNIX;
10719954bf92SDavid Howells 
10729954bf92SDavid Howells 		c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
10739954bf92SDavid Howells 		if (IS_ERR(c))
10749954bf92SDavid Howells 			return PTR_ERR(c);
10755eb005caSDavid Howells 		ctx->nfs_server.hostname = c;
10769954bf92SDavid Howells 
10779954bf92SDavid Howells 		c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
10789954bf92SDavid Howells 		if (IS_ERR(c))
10799954bf92SDavid Howells 			return PTR_ERR(c);
10805eb005caSDavid Howells 		ctx->nfs_server.export_path = c;
10819954bf92SDavid Howells 		dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c);
10829954bf92SDavid Howells 
10839954bf92SDavid Howells 		c = strndup_user(data->client_addr.data, 16);
10849954bf92SDavid Howells 		if (IS_ERR(c))
10859954bf92SDavid Howells 			return PTR_ERR(c);
10865eb005caSDavid Howells 		ctx->client_address = c;
10879954bf92SDavid Howells 
10889954bf92SDavid Howells 		/*
1089f2aedb71SDavid Howells 		 * Translate to nfs_fs_context, which nfs_fill_super
10909954bf92SDavid Howells 		 * can deal with.
10919954bf92SDavid Howells 		 */
10929954bf92SDavid Howells 
10935eb005caSDavid Howells 		ctx->flags	= data->flags & NFS4_MOUNT_FLAGMASK;
10945eb005caSDavid Howells 		ctx->rsize	= data->rsize;
10955eb005caSDavid Howells 		ctx->wsize	= data->wsize;
10965eb005caSDavid Howells 		ctx->timeo	= data->timeo;
10975eb005caSDavid Howells 		ctx->retrans	= data->retrans;
10985eb005caSDavid Howells 		ctx->acregmin	= data->acregmin;
10995eb005caSDavid Howells 		ctx->acregmax	= data->acregmax;
11005eb005caSDavid Howells 		ctx->acdirmin	= data->acdirmin;
11015eb005caSDavid Howells 		ctx->acdirmax	= data->acdirmax;
11025eb005caSDavid Howells 		ctx->nfs_server.protocol = data->proto;
11035eb005caSDavid Howells 		nfs_validate_transport_protocol(ctx);
11045eb005caSDavid Howells 		if (ctx->nfs_server.protocol == XPRT_TRANSPORT_UDP)
11059954bf92SDavid Howells 			goto out_invalid_transport_udp;
11069954bf92SDavid Howells 
11079954bf92SDavid Howells 		break;
11089954bf92SDavid Howells 	default:
1109f2aedb71SDavid Howells 		goto generic;
11109954bf92SDavid Howells 	}
11119954bf92SDavid Howells 
1112f2aedb71SDavid Howells 	ctx->skip_reconfig_option_check = true;
11139954bf92SDavid Howells 	return 0;
11149954bf92SDavid Howells 
1115f2aedb71SDavid Howells generic:
1116f2aedb71SDavid Howells 	return generic_parse_monolithic(fc, data);
1117f2aedb71SDavid Howells 
11189954bf92SDavid Howells out_no_data:
1119f2aedb71SDavid Howells 	if (is_remount_fc(fc)) {
1120f2aedb71SDavid Howells 		ctx->skip_reconfig_option_check = true;
1121f2aedb71SDavid Howells 		return 0;
1122f2aedb71SDavid Howells 	}
1123ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS4: mount program didn't pass any mount data");
11249954bf92SDavid Howells 
11259954bf92SDavid Howells out_inval_auth:
1126ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS4: Invalid number of RPC auth flavours %d",
11279954bf92SDavid Howells 		      data->auth_flavourlen);
11289954bf92SDavid Howells 
11299954bf92SDavid Howells out_no_address:
1130ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS4: mount program didn't pass remote address");
11319954bf92SDavid Howells 
11329954bf92SDavid Howells out_invalid_transport_udp:
1133ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFSv4: Unsupported transport protocol udp");
11349954bf92SDavid Howells }
11359954bf92SDavid Howells #endif
11369954bf92SDavid Howells 
1137f2aedb71SDavid Howells /*
1138f2aedb71SDavid Howells  * Parse a monolithic block of data from sys_mount().
1139f2aedb71SDavid Howells  */
1140f2aedb71SDavid Howells static int nfs_fs_context_parse_monolithic(struct fs_context *fc,
1141f2aedb71SDavid Howells 					   void *data)
11429954bf92SDavid Howells {
1143f2aedb71SDavid Howells 	if (fc->fs_type == &nfs_fs_type)
1144f2aedb71SDavid Howells 		return nfs23_parse_monolithic(fc, data);
1145f2aedb71SDavid Howells 
1146f2aedb71SDavid Howells #if IS_ENABLED(CONFIG_NFS_V4)
1147f2aedb71SDavid Howells 	if (fc->fs_type == &nfs4_fs_type)
1148f2aedb71SDavid Howells 		return nfs4_parse_monolithic(fc, data);
1149f2aedb71SDavid Howells #endif
1150f2aedb71SDavid Howells 
1151ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Unsupported monolithic data version");
1152f2aedb71SDavid Howells }
1153f2aedb71SDavid Howells 
1154f2aedb71SDavid Howells /*
1155f2aedb71SDavid Howells  * Validate the preparsed information in the config.
1156f2aedb71SDavid Howells  */
1157f2aedb71SDavid Howells static int nfs_fs_context_validate(struct fs_context *fc)
1158f2aedb71SDavid Howells {
1159f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1160f2aedb71SDavid Howells 	struct nfs_subversion *nfs_mod;
1161f2aedb71SDavid Howells 	struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address;
11629954bf92SDavid Howells 	int max_namelen = PAGE_SIZE;
11639954bf92SDavid Howells 	int max_pathlen = NFS_MAXPATHLEN;
1164f2aedb71SDavid Howells 	int port = 0;
1165f2aedb71SDavid Howells 	int ret;
11669954bf92SDavid Howells 
1167f2aedb71SDavid Howells 	if (!fc->source)
1168f2aedb71SDavid Howells 		goto out_no_device_name;
1169f2aedb71SDavid Howells 
1170f2aedb71SDavid Howells 	/* Check for sanity first. */
1171f2aedb71SDavid Howells 	if (ctx->minorversion && ctx->version != 4)
1172f2aedb71SDavid Howells 		goto out_minorversion_mismatch;
1173f2aedb71SDavid Howells 
1174f2aedb71SDavid Howells 	if (ctx->options & NFS_OPTION_MIGRATION &&
1175f2aedb71SDavid Howells 	    (ctx->version != 4 || ctx->minorversion != 0))
1176f2aedb71SDavid Howells 		goto out_migration_misuse;
1177f2aedb71SDavid Howells 
1178f2aedb71SDavid Howells 	/* Verify that any proto=/mountproto= options match the address
1179f2aedb71SDavid Howells 	 * families in the addr=/mountaddr= options.
1180f2aedb71SDavid Howells 	 */
1181f2aedb71SDavid Howells 	if (ctx->protofamily != AF_UNSPEC &&
1182f2aedb71SDavid Howells 	    ctx->protofamily != ctx->nfs_server.address.sa_family)
1183f2aedb71SDavid Howells 		goto out_proto_mismatch;
1184f2aedb71SDavid Howells 
1185f2aedb71SDavid Howells 	if (ctx->mountfamily != AF_UNSPEC) {
1186f2aedb71SDavid Howells 		if (ctx->mount_server.addrlen) {
1187f2aedb71SDavid Howells 			if (ctx->mountfamily != ctx->mount_server.address.sa_family)
1188f2aedb71SDavid Howells 				goto out_mountproto_mismatch;
1189f2aedb71SDavid Howells 		} else {
1190f2aedb71SDavid Howells 			if (ctx->mountfamily != ctx->nfs_server.address.sa_family)
1191f2aedb71SDavid Howells 				goto out_mountproto_mismatch;
1192f2aedb71SDavid Howells 		}
1193f2aedb71SDavid Howells 	}
11949954bf92SDavid Howells 
11959954bf92SDavid Howells 	if (!nfs_verify_server_address(sap))
11969954bf92SDavid Howells 		goto out_no_address;
11979954bf92SDavid Howells 
11985eb005caSDavid Howells 	if (ctx->version == 4) {
119962a55d08SScott Mayhew 		if (IS_ENABLED(CONFIG_NFS_V4)) {
12005eb005caSDavid Howells 			if (ctx->nfs_server.protocol == XPRT_TRANSPORT_RDMA)
12019954bf92SDavid Howells 				port = NFS_RDMA_PORT;
12029954bf92SDavid Howells 			else
12039954bf92SDavid Howells 				port = NFS_PORT;
12049954bf92SDavid Howells 			max_namelen = NFS4_MAXNAMLEN;
12059954bf92SDavid Howells 			max_pathlen = NFS4_MAXPATHLEN;
12065eb005caSDavid Howells 			nfs_validate_transport_protocol(ctx);
12075eb005caSDavid Howells 			if (ctx->nfs_server.protocol == XPRT_TRANSPORT_UDP)
12089954bf92SDavid Howells 				goto out_invalid_transport_udp;
120962a55d08SScott Mayhew 			ctx->flags &= ~(NFS_MOUNT_NONLM | NFS_MOUNT_NOACL |
121062a55d08SScott Mayhew 					NFS_MOUNT_VER3 | NFS_MOUNT_LOCAL_FLOCK |
121162a55d08SScott Mayhew 					NFS_MOUNT_LOCAL_FCNTL);
121262a55d08SScott Mayhew 		} else {
12139954bf92SDavid Howells 			goto out_v4_not_compiled;
121462a55d08SScott Mayhew 		}
12159954bf92SDavid Howells 	} else {
12165eb005caSDavid Howells 		nfs_set_mount_transport_protocol(ctx);
1217b24ee6c6SOlga Kornievskaia #ifdef CONFIG_NFS_DISABLE_UDP_SUPPORT
1218b24ee6c6SOlga Kornievskaia 	       if (ctx->nfs_server.protocol == XPRT_TRANSPORT_UDP)
1219b24ee6c6SOlga Kornievskaia 		       goto out_invalid_transport_udp;
1220b24ee6c6SOlga Kornievskaia #endif
12215eb005caSDavid Howells 		if (ctx->nfs_server.protocol == XPRT_TRANSPORT_RDMA)
12229954bf92SDavid Howells 			port = NFS_RDMA_PORT;
12239954bf92SDavid Howells 	}
12249954bf92SDavid Howells 
12255eb005caSDavid Howells 	nfs_set_port(sap, &ctx->nfs_server.port, port);
12269954bf92SDavid Howells 
122762a55d08SScott Mayhew 	ret = nfs_parse_source(fc, max_namelen, max_pathlen);
1228f2aedb71SDavid Howells 	if (ret < 0)
1229f2aedb71SDavid Howells 		return ret;
12309954bf92SDavid Howells 
1231f2aedb71SDavid Howells 	/* Load the NFS protocol module if we haven't done so yet */
123262a55d08SScott Mayhew 	if (!ctx->nfs_mod) {
1233f2aedb71SDavid Howells 		nfs_mod = get_nfs_version(ctx->version);
1234f2aedb71SDavid Howells 		if (IS_ERR(nfs_mod)) {
1235f2aedb71SDavid Howells 			ret = PTR_ERR(nfs_mod);
1236f2aedb71SDavid Howells 			goto out_version_unavailable;
1237f2aedb71SDavid Howells 		}
123862a55d08SScott Mayhew 		ctx->nfs_mod = nfs_mod;
1239f2aedb71SDavid Howells 	}
1240f2aedb71SDavid Howells 	return 0;
1241f2aedb71SDavid Howells 
1242f2aedb71SDavid Howells out_no_device_name:
1243ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Device name not specified");
12449954bf92SDavid Howells out_v4_not_compiled:
1245ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: NFSv4 is not compiled into kernel");
12469954bf92SDavid Howells 	return -EPROTONOSUPPORT;
12479954bf92SDavid Howells out_invalid_transport_udp:
1248ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFSv4: Unsupported transport protocol udp");
12499954bf92SDavid Howells out_no_address:
1250ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: mount program didn't pass remote address");
1251f2aedb71SDavid Howells out_mountproto_mismatch:
1252ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Mount server address does not match mountproto= option");
1253f2aedb71SDavid Howells out_proto_mismatch:
1254ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Server address does not match proto= option");
1255f2aedb71SDavid Howells out_minorversion_mismatch:
1256ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Mount option vers=%u does not support minorversion=%u",
1257f2aedb71SDavid Howells 			  ctx->version, ctx->minorversion);
1258f2aedb71SDavid Howells out_migration_misuse:
1259ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: 'Migration' not supported for this NFS version");
1260f2aedb71SDavid Howells out_version_unavailable:
1261ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: Version unavailable");
1262f2aedb71SDavid Howells 	return ret;
12639954bf92SDavid Howells }
1264f2aedb71SDavid Howells 
1265f2aedb71SDavid Howells /*
1266f2aedb71SDavid Howells  * Create an NFS superblock by the appropriate method.
1267f2aedb71SDavid Howells  */
1268f2aedb71SDavid Howells static int nfs_get_tree(struct fs_context *fc)
1269f2aedb71SDavid Howells {
1270f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1271f2aedb71SDavid Howells 	int err = nfs_fs_context_validate(fc);
1272f2aedb71SDavid Howells 
1273f2aedb71SDavid Howells 	if (err)
1274f2aedb71SDavid Howells 		return err;
1275f2aedb71SDavid Howells 	if (!ctx->internal)
127662a55d08SScott Mayhew 		return ctx->nfs_mod->rpc_ops->try_get_tree(fc);
1277f2aedb71SDavid Howells 	else
1278f2aedb71SDavid Howells 		return nfs_get_tree_common(fc);
1279f2aedb71SDavid Howells }
1280f2aedb71SDavid Howells 
1281f2aedb71SDavid Howells /*
1282f2aedb71SDavid Howells  * Handle duplication of a configuration.  The caller copied *src into *sc, but
1283f2aedb71SDavid Howells  * it can't deal with resource pointers in the filesystem context, so we have
1284f2aedb71SDavid Howells  * to do that.  We need to clear pointers, copy data or get extra refs as
1285f2aedb71SDavid Howells  * appropriate.
1286f2aedb71SDavid Howells  */
1287f2aedb71SDavid Howells static int nfs_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
1288f2aedb71SDavid Howells {
1289f2aedb71SDavid Howells 	struct nfs_fs_context *src = nfs_fc2context(src_fc), *ctx;
1290f2aedb71SDavid Howells 
1291f2aedb71SDavid Howells 	ctx = kmemdup(src, sizeof(struct nfs_fs_context), GFP_KERNEL);
1292f2aedb71SDavid Howells 	if (!ctx)
1293f2aedb71SDavid Howells 		return -ENOMEM;
1294f2aedb71SDavid Howells 
129562a55d08SScott Mayhew 	ctx->mntfh = nfs_alloc_fhandle();
129662a55d08SScott Mayhew 	if (!ctx->mntfh) {
1297f2aedb71SDavid Howells 		kfree(ctx);
1298f2aedb71SDavid Howells 		return -ENOMEM;
1299f2aedb71SDavid Howells 	}
130062a55d08SScott Mayhew 	nfs_copy_fh(ctx->mntfh, src->mntfh);
1301f2aedb71SDavid Howells 
130262a55d08SScott Mayhew 	__module_get(ctx->nfs_mod->owner);
1303f2aedb71SDavid Howells 	ctx->client_address		= NULL;
1304f2aedb71SDavid Howells 	ctx->mount_server.hostname	= NULL;
1305f2aedb71SDavid Howells 	ctx->nfs_server.export_path	= NULL;
1306f2aedb71SDavid Howells 	ctx->nfs_server.hostname	= NULL;
1307f2aedb71SDavid Howells 	ctx->fscache_uniq		= NULL;
1308f2aedb71SDavid Howells 	ctx->clone_data.fattr		= NULL;
1309f2aedb71SDavid Howells 	fc->fs_private = ctx;
1310f2aedb71SDavid Howells 	return 0;
1311f2aedb71SDavid Howells }
1312f2aedb71SDavid Howells 
1313f2aedb71SDavid Howells static void nfs_fs_context_free(struct fs_context *fc)
1314f2aedb71SDavid Howells {
1315f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1316f2aedb71SDavid Howells 
1317f2aedb71SDavid Howells 	if (ctx) {
131862a55d08SScott Mayhew 		if (ctx->server)
131962a55d08SScott Mayhew 			nfs_free_server(ctx->server);
132062a55d08SScott Mayhew 		if (ctx->nfs_mod)
132162a55d08SScott Mayhew 			put_nfs_version(ctx->nfs_mod);
1322f2aedb71SDavid Howells 		kfree(ctx->client_address);
1323f2aedb71SDavid Howells 		kfree(ctx->mount_server.hostname);
1324f2aedb71SDavid Howells 		kfree(ctx->nfs_server.export_path);
1325f2aedb71SDavid Howells 		kfree(ctx->nfs_server.hostname);
1326f2aedb71SDavid Howells 		kfree(ctx->fscache_uniq);
132762a55d08SScott Mayhew 		nfs_free_fhandle(ctx->mntfh);
1328f2aedb71SDavid Howells 		nfs_free_fattr(ctx->clone_data.fattr);
1329f2aedb71SDavid Howells 		kfree(ctx);
1330f2aedb71SDavid Howells 	}
1331f2aedb71SDavid Howells }
1332f2aedb71SDavid Howells 
1333f2aedb71SDavid Howells static const struct fs_context_operations nfs_fs_context_ops = {
1334f2aedb71SDavid Howells 	.free			= nfs_fs_context_free,
1335f2aedb71SDavid Howells 	.dup			= nfs_fs_context_dup,
1336f2aedb71SDavid Howells 	.parse_param		= nfs_fs_context_parse_param,
1337f2aedb71SDavid Howells 	.parse_monolithic	= nfs_fs_context_parse_monolithic,
1338f2aedb71SDavid Howells 	.get_tree		= nfs_get_tree,
1339f2aedb71SDavid Howells 	.reconfigure		= nfs_reconfigure,
1340f2aedb71SDavid Howells };
1341f2aedb71SDavid Howells 
1342f2aedb71SDavid Howells /*
1343f2aedb71SDavid Howells  * Prepare superblock configuration.  We use the namespaces attached to the
1344f2aedb71SDavid Howells  * context.  This may be the current process's namespaces, or it may be a
1345f2aedb71SDavid Howells  * container's namespaces.
1346f2aedb71SDavid Howells  */
1347f2aedb71SDavid Howells static int nfs_init_fs_context(struct fs_context *fc)
1348f2aedb71SDavid Howells {
1349f2aedb71SDavid Howells 	struct nfs_fs_context *ctx;
1350f2aedb71SDavid Howells 
1351f2aedb71SDavid Howells 	ctx = kzalloc(sizeof(struct nfs_fs_context), GFP_KERNEL);
1352f2aedb71SDavid Howells 	if (unlikely(!ctx))
1353f2aedb71SDavid Howells 		return -ENOMEM;
1354f2aedb71SDavid Howells 
135562a55d08SScott Mayhew 	ctx->mntfh = nfs_alloc_fhandle();
135662a55d08SScott Mayhew 	if (unlikely(!ctx->mntfh)) {
1357f2aedb71SDavid Howells 		kfree(ctx);
1358f2aedb71SDavid Howells 		return -ENOMEM;
1359f2aedb71SDavid Howells 	}
1360f2aedb71SDavid Howells 
1361f2aedb71SDavid Howells 	ctx->protofamily	= AF_UNSPEC;
1362f2aedb71SDavid Howells 	ctx->mountfamily	= AF_UNSPEC;
1363f2aedb71SDavid Howells 	ctx->mount_server.port	= NFS_UNSPEC_PORT;
1364f2aedb71SDavid Howells 
1365f2aedb71SDavid Howells 	if (fc->root) {
1366f2aedb71SDavid Howells 		/* reconfigure, start with the current config */
1367f2aedb71SDavid Howells 		struct nfs_server *nfss = fc->root->d_sb->s_fs_info;
1368f2aedb71SDavid Howells 		struct net *net = nfss->nfs_client->cl_net;
1369f2aedb71SDavid Howells 
1370f2aedb71SDavid Howells 		ctx->flags		= nfss->flags;
1371f2aedb71SDavid Howells 		ctx->rsize		= nfss->rsize;
1372f2aedb71SDavid Howells 		ctx->wsize		= nfss->wsize;
1373f2aedb71SDavid Howells 		ctx->retrans		= nfss->client->cl_timeout->to_retries;
1374f2aedb71SDavid Howells 		ctx->selected_flavor	= nfss->client->cl_auth->au_flavor;
1375f2aedb71SDavid Howells 		ctx->acregmin		= nfss->acregmin / HZ;
1376f2aedb71SDavid Howells 		ctx->acregmax		= nfss->acregmax / HZ;
1377f2aedb71SDavid Howells 		ctx->acdirmin		= nfss->acdirmin / HZ;
1378f2aedb71SDavid Howells 		ctx->acdirmax		= nfss->acdirmax / HZ;
1379f2aedb71SDavid Howells 		ctx->timeo		= 10U * nfss->client->cl_timeout->to_initval / HZ;
1380f2aedb71SDavid Howells 		ctx->nfs_server.port	= nfss->port;
1381f2aedb71SDavid Howells 		ctx->nfs_server.addrlen	= nfss->nfs_client->cl_addrlen;
1382f2aedb71SDavid Howells 		ctx->version		= nfss->nfs_client->rpc_ops->version;
1383f2aedb71SDavid Howells 		ctx->minorversion	= nfss->nfs_client->cl_minorversion;
1384f2aedb71SDavid Howells 
1385f2aedb71SDavid Howells 		memcpy(&ctx->nfs_server.address, &nfss->nfs_client->cl_addr,
1386f2aedb71SDavid Howells 			ctx->nfs_server.addrlen);
1387f2aedb71SDavid Howells 
1388f2aedb71SDavid Howells 		if (fc->net_ns != net) {
1389f2aedb71SDavid Howells 			put_net(fc->net_ns);
1390f2aedb71SDavid Howells 			fc->net_ns = get_net(net);
1391f2aedb71SDavid Howells 		}
1392f2aedb71SDavid Howells 
139362a55d08SScott Mayhew 		ctx->nfs_mod = nfss->nfs_client->cl_nfs_mod;
139462a55d08SScott Mayhew 		__module_get(ctx->nfs_mod->owner);
1395f2aedb71SDavid Howells 	} else {
1396f2aedb71SDavid Howells 		/* defaults */
1397f2aedb71SDavid Howells 		ctx->timeo		= NFS_UNSPEC_TIMEO;
1398f2aedb71SDavid Howells 		ctx->retrans		= NFS_UNSPEC_RETRANS;
1399f2aedb71SDavid Howells 		ctx->acregmin		= NFS_DEF_ACREGMIN;
1400f2aedb71SDavid Howells 		ctx->acregmax		= NFS_DEF_ACREGMAX;
1401f2aedb71SDavid Howells 		ctx->acdirmin		= NFS_DEF_ACDIRMIN;
1402f2aedb71SDavid Howells 		ctx->acdirmax		= NFS_DEF_ACDIRMAX;
1403f2aedb71SDavid Howells 		ctx->nfs_server.port	= NFS_UNSPEC_PORT;
1404f2aedb71SDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
1405f2aedb71SDavid Howells 		ctx->selected_flavor	= RPC_AUTH_MAXFLAVOR;
1406f2aedb71SDavid Howells 		ctx->minorversion	= 0;
1407f2aedb71SDavid Howells 		ctx->need_mount		= true;
1408f2aedb71SDavid Howells 	}
1409f2aedb71SDavid Howells 	fc->fs_private = ctx;
1410f2aedb71SDavid Howells 	fc->ops = &nfs_fs_context_ops;
1411f2aedb71SDavid Howells 	return 0;
1412f2aedb71SDavid Howells }
1413f2aedb71SDavid Howells 
1414f2aedb71SDavid Howells struct file_system_type nfs_fs_type = {
1415f2aedb71SDavid Howells 	.owner			= THIS_MODULE,
1416f2aedb71SDavid Howells 	.name			= "nfs",
1417f2aedb71SDavid Howells 	.init_fs_context	= nfs_init_fs_context,
1418f2aedb71SDavid Howells 	.parameters		= &nfs_fs_parameters,
1419f2aedb71SDavid Howells 	.kill_sb		= nfs_kill_super,
1420f2aedb71SDavid Howells 	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
1421f2aedb71SDavid Howells };
1422f2aedb71SDavid Howells MODULE_ALIAS_FS("nfs");
1423f2aedb71SDavid Howells EXPORT_SYMBOL_GPL(nfs_fs_type);
1424f2aedb71SDavid Howells 
1425f2aedb71SDavid Howells #if IS_ENABLED(CONFIG_NFS_V4)
1426f2aedb71SDavid Howells struct file_system_type nfs4_fs_type = {
1427f2aedb71SDavid Howells 	.owner			= THIS_MODULE,
1428f2aedb71SDavid Howells 	.name			= "nfs4",
1429f2aedb71SDavid Howells 	.init_fs_context	= nfs_init_fs_context,
1430f2aedb71SDavid Howells 	.parameters		= &nfs_fs_parameters,
1431f2aedb71SDavid Howells 	.kill_sb		= nfs_kill_super,
1432f2aedb71SDavid Howells 	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
1433f2aedb71SDavid Howells };
1434f2aedb71SDavid Howells MODULE_ALIAS_FS("nfs4");
1435f2aedb71SDavid Howells MODULE_ALIAS("nfs4");
1436f2aedb71SDavid Howells EXPORT_SYMBOL_GPL(nfs4_fs_type);
1437f2aedb71SDavid Howells #endif /* CONFIG_NFS_V4 */
1438