xref: /openbmc/linux/fs/nfs/fs_context.c (revision b181f7029bd71238ac2754ce7052dffd69432085)
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 
13b6459415SJakub Kicinski #include <linux/compat.h>
149954bf92SDavid Howells #include <linux/module.h>
159954bf92SDavid Howells #include <linux/fs.h>
16e38bb238SScott Mayhew #include <linux/fs_context.h>
17e38bb238SScott Mayhew #include <linux/fs_parser.h>
189954bf92SDavid Howells #include <linux/nfs_fs.h>
199954bf92SDavid Howells #include <linux/nfs_mount.h>
209954bf92SDavid Howells #include <linux/nfs4_mount.h>
21c8407f2eSChuck Lever 
22c8407f2eSChuck Lever #include <net/handshake.h>
23c8407f2eSChuck Lever 
249954bf92SDavid Howells #include "nfs.h"
259954bf92SDavid Howells #include "internal.h"
269954bf92SDavid Howells 
2733ce83efSChuck Lever #include "nfstrace.h"
2833ce83efSChuck Lever 
299954bf92SDavid Howells #define NFSDBG_FACILITY		NFSDBG_MOUNT
309954bf92SDavid Howells 
319954bf92SDavid Howells #if IS_ENABLED(CONFIG_NFS_V3)
329954bf92SDavid Howells #define NFS_DEFAULT_VERSION 3
339954bf92SDavid Howells #else
349954bf92SDavid Howells #define NFS_DEFAULT_VERSION 2
359954bf92SDavid Howells #endif
369954bf92SDavid Howells 
379954bf92SDavid Howells #define NFS_MAX_CONNECTIONS 16
389954bf92SDavid Howells 
39e38bb238SScott Mayhew enum nfs_param {
40e38bb238SScott Mayhew 	Opt_ac,
41e38bb238SScott Mayhew 	Opt_acdirmax,
42e38bb238SScott Mayhew 	Opt_acdirmin,
43e38bb238SScott Mayhew 	Opt_acl,
44e38bb238SScott Mayhew 	Opt_acregmax,
45e38bb238SScott Mayhew 	Opt_acregmin,
469954bf92SDavid Howells 	Opt_actimeo,
47e38bb238SScott Mayhew 	Opt_addr,
48e38bb238SScott Mayhew 	Opt_bg,
49e38bb238SScott Mayhew 	Opt_bsize,
50e38bb238SScott Mayhew 	Opt_clientaddr,
51e38bb238SScott Mayhew 	Opt_cto,
52e38bb238SScott Mayhew 	Opt_fg,
53e38bb238SScott Mayhew 	Opt_fscache,
5448ce73b1SAl Viro 	Opt_fscache_flag,
55e38bb238SScott Mayhew 	Opt_hard,
56e38bb238SScott Mayhew 	Opt_intr,
579954bf92SDavid Howells 	Opt_local_lock,
58e38bb238SScott Mayhew 	Opt_lock,
59e38bb238SScott Mayhew 	Opt_lookupcache,
60e38bb238SScott Mayhew 	Opt_migration,
61e38bb238SScott Mayhew 	Opt_minorversion,
62e38bb238SScott Mayhew 	Opt_mountaddr,
63e38bb238SScott Mayhew 	Opt_mounthost,
64e38bb238SScott Mayhew 	Opt_mountport,
65e38bb238SScott Mayhew 	Opt_mountproto,
66e38bb238SScott Mayhew 	Opt_mountvers,
67e38bb238SScott Mayhew 	Opt_namelen,
68e38bb238SScott Mayhew 	Opt_nconnect,
697e134205SOlga Kornievskaia 	Opt_max_connect,
70e38bb238SScott Mayhew 	Opt_port,
71e38bb238SScott Mayhew 	Opt_posix,
72e38bb238SScott Mayhew 	Opt_proto,
73e38bb238SScott Mayhew 	Opt_rdirplus,
74e38bb238SScott Mayhew 	Opt_rdma,
75e38bb238SScott Mayhew 	Opt_resvport,
76e38bb238SScott Mayhew 	Opt_retrans,
77e38bb238SScott Mayhew 	Opt_retry,
78e38bb238SScott Mayhew 	Opt_rsize,
79e38bb238SScott Mayhew 	Opt_sec,
80e38bb238SScott Mayhew 	Opt_sharecache,
81e38bb238SScott Mayhew 	Opt_sloppy,
82e38bb238SScott Mayhew 	Opt_soft,
83e38bb238SScott Mayhew 	Opt_softerr,
84c74dfe97STrond Myklebust 	Opt_softreval,
85e38bb238SScott Mayhew 	Opt_source,
86e38bb238SScott Mayhew 	Opt_tcp,
87e38bb238SScott Mayhew 	Opt_timeo,
88a43bf604SOlga Kornievskaia 	Opt_trunkdiscovery,
89e38bb238SScott Mayhew 	Opt_udp,
90e38bb238SScott Mayhew 	Opt_v,
91e38bb238SScott Mayhew 	Opt_vers,
92e38bb238SScott Mayhew 	Opt_wsize,
93a0492339STrond Myklebust 	Opt_write,
94c8407f2eSChuck Lever 	Opt_xprtsec,
959954bf92SDavid Howells };
969954bf92SDavid Howells 
972710c957SAl Viro enum {
982710c957SAl Viro 	Opt_local_lock_all,
992710c957SAl Viro 	Opt_local_lock_flock,
1002710c957SAl Viro 	Opt_local_lock_none,
1012710c957SAl Viro 	Opt_local_lock_posix,
1022710c957SAl Viro };
1032710c957SAl Viro 
1045eede625SAl Viro static const struct constant_table nfs_param_enums_local_lock[] = {
1052710c957SAl Viro 	{ "all",		Opt_local_lock_all },
1062710c957SAl Viro 	{ "flock",	Opt_local_lock_flock },
107a2d24bcbSScott Mayhew 	{ "posix",	Opt_local_lock_posix },
1082710c957SAl Viro 	{ "none",		Opt_local_lock_none },
1092710c957SAl Viro 	{}
1102710c957SAl Viro };
1112710c957SAl Viro 
1122710c957SAl Viro enum {
1132710c957SAl Viro 	Opt_lookupcache_all,
1142710c957SAl Viro 	Opt_lookupcache_none,
1152710c957SAl Viro 	Opt_lookupcache_positive,
1162710c957SAl Viro };
1172710c957SAl Viro 
1185eede625SAl Viro static const struct constant_table nfs_param_enums_lookupcache[] = {
1192710c957SAl Viro 	{ "all",		Opt_lookupcache_all },
1202710c957SAl Viro 	{ "none",		Opt_lookupcache_none },
1212710c957SAl Viro 	{ "pos",		Opt_lookupcache_positive },
1222710c957SAl Viro 	{ "positive",		Opt_lookupcache_positive },
1232710c957SAl Viro 	{}
1242710c957SAl Viro };
1252710c957SAl Viro 
126a0492339STrond Myklebust enum {
127a0492339STrond Myklebust 	Opt_write_lazy,
128a0492339STrond Myklebust 	Opt_write_eager,
129a0492339STrond Myklebust 	Opt_write_wait,
130a0492339STrond Myklebust };
131a0492339STrond Myklebust 
132a0492339STrond Myklebust static const struct constant_table nfs_param_enums_write[] = {
133a0492339STrond Myklebust 	{ "lazy",		Opt_write_lazy },
134a0492339STrond Myklebust 	{ "eager",		Opt_write_eager },
135a0492339STrond Myklebust 	{ "wait",		Opt_write_wait },
136a0492339STrond Myklebust 	{}
137a0492339STrond Myklebust };
138a0492339STrond Myklebust 
139d7167b14SAl Viro static const struct fs_parameter_spec nfs_fs_parameters[] = {
140e38bb238SScott Mayhew 	fsparam_flag_no("ac",		Opt_ac),
141e38bb238SScott Mayhew 	fsparam_u32   ("acdirmax",	Opt_acdirmax),
142e38bb238SScott Mayhew 	fsparam_u32   ("acdirmin",	Opt_acdirmin),
143e38bb238SScott Mayhew 	fsparam_flag_no("acl",		Opt_acl),
144e38bb238SScott Mayhew 	fsparam_u32   ("acregmax",	Opt_acregmax),
145e38bb238SScott Mayhew 	fsparam_u32   ("acregmin",	Opt_acregmin),
146e38bb238SScott Mayhew 	fsparam_u32   ("actimeo",	Opt_actimeo),
147e38bb238SScott Mayhew 	fsparam_string("addr",		Opt_addr),
148e38bb238SScott Mayhew 	fsparam_flag  ("bg",		Opt_bg),
149e38bb238SScott Mayhew 	fsparam_u32   ("bsize",		Opt_bsize),
150e38bb238SScott Mayhew 	fsparam_string("clientaddr",	Opt_clientaddr),
151e38bb238SScott Mayhew 	fsparam_flag_no("cto",		Opt_cto),
152e38bb238SScott Mayhew 	fsparam_flag  ("fg",		Opt_fg),
15348ce73b1SAl Viro 	fsparam_flag_no("fsc",		Opt_fscache_flag),
15448ce73b1SAl Viro 	fsparam_string("fsc",		Opt_fscache),
155e38bb238SScott Mayhew 	fsparam_flag  ("hard",		Opt_hard),
156328de528SAl Viro 	__fsparam(NULL, "intr",		Opt_intr,
1572710c957SAl Viro 		  fs_param_neg_with_no|fs_param_deprecated, NULL),
1582710c957SAl Viro 	fsparam_enum  ("local_lock",	Opt_local_lock, nfs_param_enums_local_lock),
159e38bb238SScott Mayhew 	fsparam_flag_no("lock",		Opt_lock),
1602710c957SAl Viro 	fsparam_enum  ("lookupcache",	Opt_lookupcache, nfs_param_enums_lookupcache),
161e38bb238SScott Mayhew 	fsparam_flag_no("migration",	Opt_migration),
162e38bb238SScott Mayhew 	fsparam_u32   ("minorversion",	Opt_minorversion),
163e38bb238SScott Mayhew 	fsparam_string("mountaddr",	Opt_mountaddr),
164e38bb238SScott Mayhew 	fsparam_string("mounthost",	Opt_mounthost),
165e38bb238SScott Mayhew 	fsparam_u32   ("mountport",	Opt_mountport),
166e38bb238SScott Mayhew 	fsparam_string("mountproto",	Opt_mountproto),
167e38bb238SScott Mayhew 	fsparam_u32   ("mountvers",	Opt_mountvers),
168e38bb238SScott Mayhew 	fsparam_u32   ("namlen",	Opt_namelen),
169e38bb238SScott Mayhew 	fsparam_u32   ("nconnect",	Opt_nconnect),
1707e134205SOlga Kornievskaia 	fsparam_u32   ("max_connect",	Opt_max_connect),
171e38bb238SScott Mayhew 	fsparam_string("nfsvers",	Opt_vers),
172e38bb238SScott Mayhew 	fsparam_u32   ("port",		Opt_port),
173e38bb238SScott Mayhew 	fsparam_flag_no("posix",	Opt_posix),
174e38bb238SScott Mayhew 	fsparam_string("proto",		Opt_proto),
175e38bb238SScott Mayhew 	fsparam_flag_no("rdirplus",	Opt_rdirplus),
176e38bb238SScott Mayhew 	fsparam_flag  ("rdma",		Opt_rdma),
177e38bb238SScott Mayhew 	fsparam_flag_no("resvport",	Opt_resvport),
178e38bb238SScott Mayhew 	fsparam_u32   ("retrans",	Opt_retrans),
179e38bb238SScott Mayhew 	fsparam_string("retry",		Opt_retry),
180e38bb238SScott Mayhew 	fsparam_u32   ("rsize",		Opt_rsize),
181e38bb238SScott Mayhew 	fsparam_string("sec",		Opt_sec),
182e38bb238SScott Mayhew 	fsparam_flag_no("sharecache",	Opt_sharecache),
183e38bb238SScott Mayhew 	fsparam_flag  ("sloppy",	Opt_sloppy),
184e38bb238SScott Mayhew 	fsparam_flag  ("soft",		Opt_soft),
185e38bb238SScott Mayhew 	fsparam_flag  ("softerr",	Opt_softerr),
186c74dfe97STrond Myklebust 	fsparam_flag  ("softreval",	Opt_softreval),
187e38bb238SScott Mayhew 	fsparam_string("source",	Opt_source),
188e38bb238SScott Mayhew 	fsparam_flag  ("tcp",		Opt_tcp),
189e38bb238SScott Mayhew 	fsparam_u32   ("timeo",		Opt_timeo),
190a43bf604SOlga Kornievskaia 	fsparam_flag_no("trunkdiscovery", Opt_trunkdiscovery),
191e38bb238SScott Mayhew 	fsparam_flag  ("udp",		Opt_udp),
192e38bb238SScott Mayhew 	fsparam_flag  ("v2",		Opt_v),
193e38bb238SScott Mayhew 	fsparam_flag  ("v3",		Opt_v),
194e38bb238SScott Mayhew 	fsparam_flag  ("v4",		Opt_v),
195e38bb238SScott Mayhew 	fsparam_flag  ("v4.0",		Opt_v),
196e38bb238SScott Mayhew 	fsparam_flag  ("v4.1",		Opt_v),
197e38bb238SScott Mayhew 	fsparam_flag  ("v4.2",		Opt_v),
198e38bb238SScott Mayhew 	fsparam_string("vers",		Opt_vers),
199a0492339STrond Myklebust 	fsparam_enum  ("write",		Opt_write, nfs_param_enums_write),
200e38bb238SScott Mayhew 	fsparam_u32   ("wsize",		Opt_wsize),
201c8407f2eSChuck Lever 	fsparam_string("xprtsec",	Opt_xprtsec),
202e38bb238SScott Mayhew 	{}
2039954bf92SDavid Howells };
2049954bf92SDavid Howells 
205e38bb238SScott Mayhew enum {
206e38bb238SScott Mayhew 	Opt_vers_2,
207e38bb238SScott Mayhew 	Opt_vers_3,
208e38bb238SScott Mayhew 	Opt_vers_4,
209e38bb238SScott Mayhew 	Opt_vers_4_0,
210e38bb238SScott Mayhew 	Opt_vers_4_1,
211e38bb238SScott Mayhew 	Opt_vers_4_2,
212e38bb238SScott Mayhew };
213e38bb238SScott Mayhew 
214e38bb238SScott Mayhew static const struct constant_table nfs_vers_tokens[] = {
215e38bb238SScott Mayhew 	{ "2",		Opt_vers_2 },
216e38bb238SScott Mayhew 	{ "3",		Opt_vers_3 },
217e38bb238SScott Mayhew 	{ "4",		Opt_vers_4 },
218e38bb238SScott Mayhew 	{ "4.0",	Opt_vers_4_0 },
219e38bb238SScott Mayhew 	{ "4.1",	Opt_vers_4_1 },
220e38bb238SScott Mayhew 	{ "4.2",	Opt_vers_4_2 },
221529af905SScott Mayhew 	{}
222e38bb238SScott Mayhew };
223e38bb238SScott Mayhew 
224e38bb238SScott Mayhew enum {
225e38bb238SScott Mayhew 	Opt_xprt_rdma,
226e38bb238SScott Mayhew 	Opt_xprt_rdma6,
227e38bb238SScott Mayhew 	Opt_xprt_tcp,
228e38bb238SScott Mayhew 	Opt_xprt_tcp6,
229e38bb238SScott Mayhew 	Opt_xprt_udp,
230e38bb238SScott Mayhew 	Opt_xprt_udp6,
231e38bb238SScott Mayhew 	nr__Opt_xprt
232e38bb238SScott Mayhew };
233e38bb238SScott Mayhew 
234529af905SScott Mayhew static const struct constant_table nfs_xprt_protocol_tokens[] = {
235e38bb238SScott Mayhew 	{ "rdma",	Opt_xprt_rdma },
236e38bb238SScott Mayhew 	{ "rdma6",	Opt_xprt_rdma6 },
237e38bb238SScott Mayhew 	{ "tcp",	Opt_xprt_tcp },
238e38bb238SScott Mayhew 	{ "tcp6",	Opt_xprt_tcp6 },
239e38bb238SScott Mayhew 	{ "udp",	Opt_xprt_udp },
240e38bb238SScott Mayhew 	{ "udp6",	Opt_xprt_udp6 },
241529af905SScott Mayhew 	{}
242e38bb238SScott Mayhew };
243e38bb238SScott Mayhew 
244e38bb238SScott Mayhew enum {
245e38bb238SScott Mayhew 	Opt_sec_krb5,
246e38bb238SScott Mayhew 	Opt_sec_krb5i,
247e38bb238SScott Mayhew 	Opt_sec_krb5p,
248e38bb238SScott Mayhew 	Opt_sec_lkey,
249e38bb238SScott Mayhew 	Opt_sec_lkeyi,
250e38bb238SScott Mayhew 	Opt_sec_lkeyp,
251e38bb238SScott Mayhew 	Opt_sec_none,
252e38bb238SScott Mayhew 	Opt_sec_spkm,
253e38bb238SScott Mayhew 	Opt_sec_spkmi,
254e38bb238SScott Mayhew 	Opt_sec_spkmp,
255e38bb238SScott Mayhew 	Opt_sec_sys,
256e38bb238SScott Mayhew 	nr__Opt_sec
257e38bb238SScott Mayhew };
258e38bb238SScott Mayhew 
259e38bb238SScott Mayhew static const struct constant_table nfs_secflavor_tokens[] = {
260e38bb238SScott Mayhew 	{ "krb5",	Opt_sec_krb5 },
261e38bb238SScott Mayhew 	{ "krb5i",	Opt_sec_krb5i },
262e38bb238SScott Mayhew 	{ "krb5p",	Opt_sec_krb5p },
263e38bb238SScott Mayhew 	{ "lkey",	Opt_sec_lkey },
264e38bb238SScott Mayhew 	{ "lkeyi",	Opt_sec_lkeyi },
265e38bb238SScott Mayhew 	{ "lkeyp",	Opt_sec_lkeyp },
266e38bb238SScott Mayhew 	{ "none",	Opt_sec_none },
267e38bb238SScott Mayhew 	{ "null",	Opt_sec_none },
268e38bb238SScott Mayhew 	{ "spkm3",	Opt_sec_spkm },
269e38bb238SScott Mayhew 	{ "spkm3i",	Opt_sec_spkmi },
270e38bb238SScott Mayhew 	{ "spkm3p",	Opt_sec_spkmp },
271e38bb238SScott Mayhew 	{ "sys",	Opt_sec_sys },
272529af905SScott Mayhew 	{}
2739954bf92SDavid Howells };
2749954bf92SDavid Howells 
275c8407f2eSChuck Lever enum {
276c8407f2eSChuck Lever 	Opt_xprtsec_none,
277c8407f2eSChuck Lever 	Opt_xprtsec_tls,
278c8407f2eSChuck Lever 	Opt_xprtsec_mtls,
279c8407f2eSChuck Lever 	nr__Opt_xprtsec
280c8407f2eSChuck Lever };
281c8407f2eSChuck Lever 
282c8407f2eSChuck Lever static const struct constant_table nfs_xprtsec_policies[] = {
283c8407f2eSChuck Lever 	{ "none",	Opt_xprtsec_none },
284c8407f2eSChuck Lever 	{ "tls",	Opt_xprtsec_tls },
285c8407f2eSChuck Lever 	{ "mtls",	Opt_xprtsec_mtls },
286c8407f2eSChuck Lever 	{}
287c8407f2eSChuck Lever };
288c8407f2eSChuck Lever 
2899954bf92SDavid Howells /*
2909954bf92SDavid Howells  * Sanity-check a server address provided by the mount command.
2919954bf92SDavid Howells  *
2929954bf92SDavid Howells  * Address family must be initialized, and address must not be
2939954bf92SDavid Howells  * the ANY address for that family.
2949954bf92SDavid Howells  */
nfs_verify_server_address(struct sockaddr_storage * addr)295cf0d7e7fSKees Cook static int nfs_verify_server_address(struct sockaddr_storage *addr)
2969954bf92SDavid Howells {
297cf0d7e7fSKees Cook 	switch (addr->ss_family) {
2989954bf92SDavid Howells 	case AF_INET: {
2999954bf92SDavid Howells 		struct sockaddr_in *sa = (struct sockaddr_in *)addr;
3009954bf92SDavid Howells 		return sa->sin_addr.s_addr != htonl(INADDR_ANY);
3019954bf92SDavid Howells 	}
3029954bf92SDavid Howells 	case AF_INET6: {
3039954bf92SDavid Howells 		struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
3049954bf92SDavid Howells 		return !ipv6_addr_any(sa);
3059954bf92SDavid Howells 	}
3069954bf92SDavid Howells 	}
3079954bf92SDavid Howells 
3089954bf92SDavid Howells 	return 0;
3099954bf92SDavid Howells }
3109954bf92SDavid Howells 
31190ff57bfSTrond Myklebust #ifdef CONFIG_NFS_DISABLE_UDP_SUPPORT
nfs_server_transport_udp_invalid(const struct nfs_fs_context * ctx)31290ff57bfSTrond Myklebust static bool nfs_server_transport_udp_invalid(const struct nfs_fs_context *ctx)
31390ff57bfSTrond Myklebust {
31490ff57bfSTrond Myklebust 	return true;
31590ff57bfSTrond Myklebust }
31690ff57bfSTrond Myklebust #else
nfs_server_transport_udp_invalid(const struct nfs_fs_context * ctx)31790ff57bfSTrond Myklebust static bool nfs_server_transport_udp_invalid(const struct nfs_fs_context *ctx)
31890ff57bfSTrond Myklebust {
31990ff57bfSTrond Myklebust 	if (ctx->version == 4)
32090ff57bfSTrond Myklebust 		return true;
32190ff57bfSTrond Myklebust 	return false;
32290ff57bfSTrond Myklebust }
32390ff57bfSTrond Myklebust #endif
32490ff57bfSTrond Myklebust 
3259954bf92SDavid Howells /*
3269954bf92SDavid Howells  * Sanity check the NFS transport protocol.
3279954bf92SDavid Howells  */
nfs_validate_transport_protocol(struct fs_context * fc,struct nfs_fs_context * ctx)32890ff57bfSTrond Myklebust static int nfs_validate_transport_protocol(struct fs_context *fc,
32990ff57bfSTrond Myklebust 					   struct nfs_fs_context *ctx)
3309954bf92SDavid Howells {
3315eb005caSDavid Howells 	switch (ctx->nfs_server.protocol) {
3329954bf92SDavid Howells 	case XPRT_TRANSPORT_UDP:
33390ff57bfSTrond Myklebust 		if (nfs_server_transport_udp_invalid(ctx))
33490ff57bfSTrond Myklebust 			goto out_invalid_transport_udp;
33590ff57bfSTrond Myklebust 		break;
3369954bf92SDavid Howells 	case XPRT_TRANSPORT_TCP:
3379954bf92SDavid Howells 	case XPRT_TRANSPORT_RDMA:
3389954bf92SDavid Howells 		break;
3399954bf92SDavid Howells 	default:
3405eb005caSDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
3419954bf92SDavid Howells 	}
342c8407f2eSChuck Lever 
343c8407f2eSChuck Lever 	if (ctx->xprtsec.policy != RPC_XPRTSEC_NONE)
344c8407f2eSChuck Lever 		switch (ctx->nfs_server.protocol) {
345c8407f2eSChuck Lever 		case XPRT_TRANSPORT_TCP:
346c8407f2eSChuck Lever 			ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP_TLS;
347c8407f2eSChuck Lever 			break;
348c8407f2eSChuck Lever 		default:
349c8407f2eSChuck Lever 			goto out_invalid_xprtsec_policy;
350c8407f2eSChuck Lever 	}
351c8407f2eSChuck Lever 
35290ff57bfSTrond Myklebust 	return 0;
35390ff57bfSTrond Myklebust out_invalid_transport_udp:
35490ff57bfSTrond Myklebust 	return nfs_invalf(fc, "NFS: Unsupported transport protocol udp");
355c8407f2eSChuck Lever out_invalid_xprtsec_policy:
356c8407f2eSChuck Lever 	return nfs_invalf(fc, "NFS: Transport does not support xprtsec");
3579954bf92SDavid Howells }
3589954bf92SDavid Howells 
3599954bf92SDavid Howells /*
3609954bf92SDavid Howells  * For text based NFSv2/v3 mounts, the mount protocol transport default
3619954bf92SDavid Howells  * settings should depend upon the specified NFS transport.
3629954bf92SDavid Howells  */
nfs_set_mount_transport_protocol(struct nfs_fs_context * ctx)3635eb005caSDavid Howells static void nfs_set_mount_transport_protocol(struct nfs_fs_context *ctx)
3649954bf92SDavid Howells {
3655eb005caSDavid Howells 	if (ctx->mount_server.protocol == XPRT_TRANSPORT_UDP ||
3665eb005caSDavid Howells 	    ctx->mount_server.protocol == XPRT_TRANSPORT_TCP)
3679954bf92SDavid Howells 			return;
3685eb005caSDavid Howells 	switch (ctx->nfs_server.protocol) {
3699954bf92SDavid Howells 	case XPRT_TRANSPORT_UDP:
3705eb005caSDavid Howells 		ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
3719954bf92SDavid Howells 		break;
3729954bf92SDavid Howells 	case XPRT_TRANSPORT_TCP:
3739954bf92SDavid Howells 	case XPRT_TRANSPORT_RDMA:
3745eb005caSDavid Howells 		ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
3759954bf92SDavid Howells 	}
3769954bf92SDavid Howells }
3779954bf92SDavid Howells 
3789954bf92SDavid Howells /*
3799954bf92SDavid Howells  * Add 'flavor' to 'auth_info' if not already present.
3809954bf92SDavid Howells  * Returns true if 'flavor' ends up in the list, false otherwise
3819954bf92SDavid Howells  */
nfs_auth_info_add(struct fs_context * fc,struct nfs_auth_info * auth_info,rpc_authflavor_t flavor)38262a55d08SScott Mayhew static int nfs_auth_info_add(struct fs_context *fc,
383e558100fSDavid Howells 			     struct nfs_auth_info *auth_info,
3849954bf92SDavid Howells 			     rpc_authflavor_t flavor)
3859954bf92SDavid Howells {
3869954bf92SDavid Howells 	unsigned int i;
3879954bf92SDavid Howells 	unsigned int max_flavor_len = ARRAY_SIZE(auth_info->flavors);
3889954bf92SDavid Howells 
3899954bf92SDavid Howells 	/* make sure this flavor isn't already in the list */
3909954bf92SDavid Howells 	for (i = 0; i < auth_info->flavor_len; i++) {
3919954bf92SDavid Howells 		if (flavor == auth_info->flavors[i])
392e558100fSDavid Howells 			return 0;
3939954bf92SDavid Howells 	}
3949954bf92SDavid Howells 
395ce8866f0SScott Mayhew 	if (auth_info->flavor_len + 1 >= max_flavor_len)
396ce8866f0SScott Mayhew 		return nfs_invalf(fc, "NFS: too many sec= flavors");
3979954bf92SDavid Howells 
3989954bf92SDavid Howells 	auth_info->flavors[auth_info->flavor_len++] = flavor;
399e558100fSDavid Howells 	return 0;
4009954bf92SDavid Howells }
4019954bf92SDavid Howells 
4029954bf92SDavid Howells /*
4039954bf92SDavid Howells  * Parse the value of the 'sec=' option.
4049954bf92SDavid Howells  */
nfs_parse_security_flavors(struct fs_context * fc,struct fs_parameter * param)40562a55d08SScott Mayhew static int nfs_parse_security_flavors(struct fs_context *fc,
406e38bb238SScott Mayhew 				      struct fs_parameter *param)
4079954bf92SDavid Howells {
40862a55d08SScott Mayhew 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
4099954bf92SDavid Howells 	rpc_authflavor_t pseudoflavor;
410e38bb238SScott Mayhew 	char *string = param->string, *p;
411e558100fSDavid Howells 	int ret;
4129954bf92SDavid Howells 
41333ce83efSChuck Lever 	trace_nfs_mount_assign(param->key, string);
4149954bf92SDavid Howells 
415e38bb238SScott Mayhew 	while ((p = strsep(&string, ":")) != NULL) {
416e38bb238SScott Mayhew 		if (!*p)
417e38bb238SScott Mayhew 			continue;
418e38bb238SScott Mayhew 		switch (lookup_constant(nfs_secflavor_tokens, p, -1)) {
4199954bf92SDavid Howells 		case Opt_sec_none:
4209954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_NULL;
4219954bf92SDavid Howells 			break;
4229954bf92SDavid Howells 		case Opt_sec_sys:
4239954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_UNIX;
4249954bf92SDavid Howells 			break;
4259954bf92SDavid Howells 		case Opt_sec_krb5:
4269954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_KRB5;
4279954bf92SDavid Howells 			break;
4289954bf92SDavid Howells 		case Opt_sec_krb5i:
4299954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_KRB5I;
4309954bf92SDavid Howells 			break;
4319954bf92SDavid Howells 		case Opt_sec_krb5p:
4329954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_KRB5P;
4339954bf92SDavid Howells 			break;
4349954bf92SDavid Howells 		case Opt_sec_lkey:
4359954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_LKEY;
4369954bf92SDavid Howells 			break;
4379954bf92SDavid Howells 		case Opt_sec_lkeyi:
4389954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_LKEYI;
4399954bf92SDavid Howells 			break;
4409954bf92SDavid Howells 		case Opt_sec_lkeyp:
4419954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_LKEYP;
4429954bf92SDavid Howells 			break;
4439954bf92SDavid Howells 		case Opt_sec_spkm:
4449954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_SPKM;
4459954bf92SDavid Howells 			break;
4469954bf92SDavid Howells 		case Opt_sec_spkmi:
4479954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_SPKMI;
4489954bf92SDavid Howells 			break;
4499954bf92SDavid Howells 		case Opt_sec_spkmp:
4509954bf92SDavid Howells 			pseudoflavor = RPC_AUTH_GSS_SPKMP;
4519954bf92SDavid Howells 			break;
4529954bf92SDavid Howells 		default:
453ce8866f0SScott Mayhew 			return nfs_invalf(fc, "NFS: sec=%s option not recognized", p);
454e558100fSDavid Howells 		}
455e558100fSDavid Howells 
45662a55d08SScott Mayhew 		ret = nfs_auth_info_add(fc, &ctx->auth_info, pseudoflavor);
457e558100fSDavid Howells 		if (ret < 0)
458e558100fSDavid Howells 			return ret;
459e558100fSDavid Howells 	}
460e558100fSDavid Howells 
4619954bf92SDavid Howells 	return 0;
4629954bf92SDavid Howells }
4639954bf92SDavid Howells 
nfs_parse_xprtsec_policy(struct fs_context * fc,struct fs_parameter * param)464c8407f2eSChuck Lever static int nfs_parse_xprtsec_policy(struct fs_context *fc,
465c8407f2eSChuck Lever 				    struct fs_parameter *param)
466c8407f2eSChuck Lever {
467c8407f2eSChuck Lever 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
468c8407f2eSChuck Lever 
469c8407f2eSChuck Lever 	trace_nfs_mount_assign(param->key, param->string);
470c8407f2eSChuck Lever 
471c8407f2eSChuck Lever 	switch (lookup_constant(nfs_xprtsec_policies, param->string, -1)) {
472c8407f2eSChuck Lever 	case Opt_xprtsec_none:
473c8407f2eSChuck Lever 		ctx->xprtsec.policy = RPC_XPRTSEC_NONE;
474c8407f2eSChuck Lever 		break;
475c8407f2eSChuck Lever 	case Opt_xprtsec_tls:
476c8407f2eSChuck Lever 		ctx->xprtsec.policy = RPC_XPRTSEC_TLS_ANON;
477c8407f2eSChuck Lever 		break;
478c8407f2eSChuck Lever 	case Opt_xprtsec_mtls:
479c8407f2eSChuck Lever 		ctx->xprtsec.policy = RPC_XPRTSEC_TLS_X509;
480c8407f2eSChuck Lever 		break;
481c8407f2eSChuck Lever 	default:
482c8407f2eSChuck Lever 		return nfs_invalf(fc, "NFS: Unrecognized transport security policy");
483c8407f2eSChuck Lever 	}
484c8407f2eSChuck Lever 	return 0;
485c8407f2eSChuck Lever }
486c8407f2eSChuck Lever 
nfs_parse_version_string(struct fs_context * fc,const char * string)48762a55d08SScott Mayhew static int nfs_parse_version_string(struct fs_context *fc,
488e38bb238SScott Mayhew 				    const char *string)
4899954bf92SDavid Howells {
49062a55d08SScott Mayhew 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
49162a55d08SScott Mayhew 
4925eb005caSDavid Howells 	ctx->flags &= ~NFS_MOUNT_VER3;
493e38bb238SScott Mayhew 	switch (lookup_constant(nfs_vers_tokens, string, -1)) {
4949954bf92SDavid Howells 	case Opt_vers_2:
4955eb005caSDavid Howells 		ctx->version = 2;
4969954bf92SDavid Howells 		break;
4979954bf92SDavid Howells 	case Opt_vers_3:
4985eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_VER3;
4995eb005caSDavid Howells 		ctx->version = 3;
5009954bf92SDavid Howells 		break;
5019954bf92SDavid Howells 	case Opt_vers_4:
5029954bf92SDavid Howells 		/* Backward compatibility option. In future,
5039954bf92SDavid Howells 		 * the mount program should always supply
5049954bf92SDavid Howells 		 * a NFSv4 minor version number.
5059954bf92SDavid Howells 		 */
5065eb005caSDavid Howells 		ctx->version = 4;
5079954bf92SDavid Howells 		break;
5089954bf92SDavid Howells 	case Opt_vers_4_0:
5095eb005caSDavid Howells 		ctx->version = 4;
5105eb005caSDavid Howells 		ctx->minorversion = 0;
5119954bf92SDavid Howells 		break;
5129954bf92SDavid Howells 	case Opt_vers_4_1:
5135eb005caSDavid Howells 		ctx->version = 4;
5145eb005caSDavid Howells 		ctx->minorversion = 1;
5159954bf92SDavid Howells 		break;
5169954bf92SDavid Howells 	case Opt_vers_4_2:
5175eb005caSDavid Howells 		ctx->version = 4;
5185eb005caSDavid Howells 		ctx->minorversion = 2;
5199954bf92SDavid Howells 		break;
5209954bf92SDavid Howells 	default:
521ce8866f0SScott Mayhew 		return nfs_invalf(fc, "NFS: Unsupported NFS version");
5229954bf92SDavid Howells 	}
523e558100fSDavid Howells 	return 0;
5249954bf92SDavid Howells }
5259954bf92SDavid Howells 
5269954bf92SDavid Howells /*
527e38bb238SScott Mayhew  * Parse a single mount parameter.
5289954bf92SDavid Howells  */
nfs_fs_context_parse_param(struct fs_context * fc,struct fs_parameter * param)529f2aedb71SDavid Howells static int nfs_fs_context_parse_param(struct fs_context *fc,
530e38bb238SScott Mayhew 				      struct fs_parameter *param)
5319954bf92SDavid Howells {
532e38bb238SScott Mayhew 	struct fs_parse_result result;
533f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
534e38bb238SScott Mayhew 	unsigned short protofamily, mountfamily;
535e38bb238SScott Mayhew 	unsigned int len;
536e38bb238SScott Mayhew 	int ret, opt;
5379954bf92SDavid Howells 
53833ce83efSChuck Lever 	trace_nfs_mount_option(param);
5399954bf92SDavid Howells 
540d7167b14SAl Viro 	opt = fs_parse(fc, nfs_fs_parameters, param, &result);
541e38bb238SScott Mayhew 	if (opt < 0)
5428b4e87a1SIan Kent 		return (opt == -ENOPARAM && ctx->sloppy) ? 1 : opt;
543e38bb238SScott Mayhew 
544ec1ade6aSOlga Kornievskaia 	if (fc->security)
545ec1ade6aSOlga Kornievskaia 		ctx->has_sec_mnt_opts = 1;
546ec1ade6aSOlga Kornievskaia 
547e38bb238SScott Mayhew 	switch (opt) {
548f2aedb71SDavid Howells 	case Opt_source:
549ce8866f0SScott Mayhew 		if (fc->source)
550ce8866f0SScott Mayhew 			return nfs_invalf(fc, "NFS: Multiple sources not supported");
551f2aedb71SDavid Howells 		fc->source = param->string;
552f2aedb71SDavid Howells 		param->string = NULL;
553f2aedb71SDavid Howells 		break;
554f2aedb71SDavid Howells 
5559954bf92SDavid Howells 		/*
5569954bf92SDavid Howells 		 * boolean options:  foo/nofoo
5579954bf92SDavid Howells 		 */
5589954bf92SDavid Howells 	case Opt_soft:
5595eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_SOFT;
5605eb005caSDavid Howells 		ctx->flags &= ~NFS_MOUNT_SOFTERR;
5619954bf92SDavid Howells 		break;
5629954bf92SDavid Howells 	case Opt_softerr:
563c74dfe97STrond Myklebust 		ctx->flags |= NFS_MOUNT_SOFTERR | NFS_MOUNT_SOFTREVAL;
5645eb005caSDavid Howells 		ctx->flags &= ~NFS_MOUNT_SOFT;
5659954bf92SDavid Howells 		break;
5669954bf92SDavid Howells 	case Opt_hard:
567c74dfe97STrond Myklebust 		ctx->flags &= ~(NFS_MOUNT_SOFT |
568c74dfe97STrond Myklebust 				NFS_MOUNT_SOFTERR |
569c74dfe97STrond Myklebust 				NFS_MOUNT_SOFTREVAL);
570c74dfe97STrond Myklebust 		break;
571c74dfe97STrond Myklebust 	case Opt_softreval:
572c74dfe97STrond Myklebust 		if (result.negated)
573c74dfe97STrond Myklebust 			ctx->flags &= ~NFS_MOUNT_SOFTREVAL;
574c74dfe97STrond Myklebust 		else
575085d16d5SDan Aloni 			ctx->flags |= NFS_MOUNT_SOFTREVAL;
5769954bf92SDavid Howells 		break;
5779954bf92SDavid Howells 	case Opt_posix:
578e38bb238SScott Mayhew 		if (result.negated)
579e38bb238SScott Mayhew 			ctx->flags &= ~NFS_MOUNT_POSIX;
580e38bb238SScott Mayhew 		else
5815eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_POSIX;
5829954bf92SDavid Howells 		break;
5839954bf92SDavid Howells 	case Opt_cto:
584e38bb238SScott Mayhew 		if (result.negated)
585e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NOCTO;
586e38bb238SScott Mayhew 		else
5875eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NOCTO;
5889954bf92SDavid Howells 		break;
589a43bf604SOlga Kornievskaia 	case Opt_trunkdiscovery:
590a43bf604SOlga Kornievskaia 		if (result.negated)
591a43bf604SOlga Kornievskaia 			ctx->flags &= ~NFS_MOUNT_TRUNK_DISCOVERY;
592a43bf604SOlga Kornievskaia 		else
593a43bf604SOlga Kornievskaia 			ctx->flags |= NFS_MOUNT_TRUNK_DISCOVERY;
594a43bf604SOlga Kornievskaia 		break;
5959954bf92SDavid Howells 	case Opt_ac:
596e38bb238SScott Mayhew 		if (result.negated)
597e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NOAC;
598e38bb238SScott Mayhew 		else
5995eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NOAC;
6009954bf92SDavid Howells 		break;
6019954bf92SDavid Howells 	case Opt_lock:
602e38bb238SScott Mayhew 		if (result.negated) {
6035eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_NONLM;
604e558100fSDavid Howells 			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
605e38bb238SScott Mayhew 		} else {
606e38bb238SScott Mayhew 			ctx->flags &= ~NFS_MOUNT_NONLM;
607e38bb238SScott Mayhew 			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
608e38bb238SScott Mayhew 		}
6099954bf92SDavid Howells 		break;
6109954bf92SDavid Howells 	case Opt_udp:
6115eb005caSDavid Howells 		ctx->flags &= ~NFS_MOUNT_TCP;
6125eb005caSDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
6139954bf92SDavid Howells 		break;
6149954bf92SDavid Howells 	case Opt_tcp:
6159954bf92SDavid Howells 	case Opt_rdma:
6165eb005caSDavid Howells 		ctx->flags |= NFS_MOUNT_TCP; /* for side protocols */
6171c3695d0STrond Myklebust 		ret = xprt_find_transport_ident(param->key);
6181c3695d0STrond Myklebust 		if (ret < 0)
6191c3695d0STrond Myklebust 			goto out_bad_transport;
6201c3695d0STrond Myklebust 		ctx->nfs_server.protocol = ret;
6219954bf92SDavid Howells 		break;
6229954bf92SDavid Howells 	case Opt_acl:
623e38bb238SScott Mayhew 		if (result.negated)
624e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NOACL;
625e38bb238SScott Mayhew 		else
6265eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NOACL;
6279954bf92SDavid Howells 		break;
6289954bf92SDavid Howells 	case Opt_rdirplus:
629e38bb238SScott Mayhew 		if (result.negated)
630e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NORDIRPLUS;
631e38bb238SScott Mayhew 		else
6325eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
6339954bf92SDavid Howells 		break;
6349954bf92SDavid Howells 	case Opt_sharecache:
635e38bb238SScott Mayhew 		if (result.negated)
636e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_UNSHARED;
637e38bb238SScott Mayhew 		else
6385eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_UNSHARED;
6399954bf92SDavid Howells 		break;
6409954bf92SDavid Howells 	case Opt_resvport:
641e38bb238SScott Mayhew 		if (result.negated)
642e38bb238SScott Mayhew 			ctx->flags |= NFS_MOUNT_NORESVPORT;
643e38bb238SScott Mayhew 		else
6445eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_NORESVPORT;
6459954bf92SDavid Howells 		break;
64648ce73b1SAl Viro 	case Opt_fscache_flag:
647e38bb238SScott Mayhew 		if (result.negated)
6485eb005caSDavid Howells 			ctx->options &= ~NFS_OPTION_FSCACHE;
649e38bb238SScott Mayhew 		else
650e38bb238SScott Mayhew 			ctx->options |= NFS_OPTION_FSCACHE;
65148ce73b1SAl Viro 		kfree(ctx->fscache_uniq);
65248ce73b1SAl Viro 		ctx->fscache_uniq = NULL;
65348ce73b1SAl Viro 		break;
65448ce73b1SAl Viro 	case Opt_fscache:
65548ce73b1SAl Viro 		ctx->options |= NFS_OPTION_FSCACHE;
65648ce73b1SAl Viro 		kfree(ctx->fscache_uniq);
65748ce73b1SAl Viro 		ctx->fscache_uniq = param->string;
65848ce73b1SAl Viro 		param->string = NULL;
6599954bf92SDavid Howells 		break;
6609954bf92SDavid Howells 	case Opt_migration:
661e38bb238SScott Mayhew 		if (result.negated)
6625eb005caSDavid Howells 			ctx->options &= ~NFS_OPTION_MIGRATION;
663e38bb238SScott Mayhew 		else
664e38bb238SScott Mayhew 			ctx->options |= NFS_OPTION_MIGRATION;
6659954bf92SDavid Howells 		break;
6669954bf92SDavid Howells 
6679954bf92SDavid Howells 		/*
6689954bf92SDavid Howells 		 * options that take numeric values
6699954bf92SDavid Howells 		 */
6709954bf92SDavid Howells 	case Opt_port:
671e38bb238SScott Mayhew 		if (result.uint_32 > USHRT_MAX)
672e38bb238SScott Mayhew 			goto out_of_bounds;
673e38bb238SScott Mayhew 		ctx->nfs_server.port = result.uint_32;
6749954bf92SDavid Howells 		break;
6759954bf92SDavid Howells 	case Opt_rsize:
676e38bb238SScott Mayhew 		ctx->rsize = result.uint_32;
6779954bf92SDavid Howells 		break;
6789954bf92SDavid Howells 	case Opt_wsize:
679e38bb238SScott Mayhew 		ctx->wsize = result.uint_32;
6809954bf92SDavid Howells 		break;
6819954bf92SDavid Howells 	case Opt_bsize:
682e38bb238SScott Mayhew 		ctx->bsize = result.uint_32;
6839954bf92SDavid Howells 		break;
6849954bf92SDavid Howells 	case Opt_timeo:
685e38bb238SScott Mayhew 		if (result.uint_32 < 1 || result.uint_32 > INT_MAX)
686e38bb238SScott Mayhew 			goto out_of_bounds;
687e38bb238SScott Mayhew 		ctx->timeo = result.uint_32;
6889954bf92SDavid Howells 		break;
6899954bf92SDavid Howells 	case Opt_retrans:
690e38bb238SScott Mayhew 		if (result.uint_32 > INT_MAX)
691e38bb238SScott Mayhew 			goto out_of_bounds;
692e38bb238SScott Mayhew 		ctx->retrans = result.uint_32;
6939954bf92SDavid Howells 		break;
6949954bf92SDavid Howells 	case Opt_acregmin:
695e38bb238SScott Mayhew 		ctx->acregmin = result.uint_32;
6969954bf92SDavid Howells 		break;
6979954bf92SDavid Howells 	case Opt_acregmax:
698e38bb238SScott Mayhew 		ctx->acregmax = result.uint_32;
6999954bf92SDavid Howells 		break;
7009954bf92SDavid Howells 	case Opt_acdirmin:
701e38bb238SScott Mayhew 		ctx->acdirmin = result.uint_32;
7029954bf92SDavid Howells 		break;
7039954bf92SDavid Howells 	case Opt_acdirmax:
704e38bb238SScott Mayhew 		ctx->acdirmax = result.uint_32;
7059954bf92SDavid Howells 		break;
7069954bf92SDavid Howells 	case Opt_actimeo:
707e38bb238SScott Mayhew 		ctx->acregmin = result.uint_32;
708e38bb238SScott Mayhew 		ctx->acregmax = result.uint_32;
709e38bb238SScott Mayhew 		ctx->acdirmin = result.uint_32;
710e38bb238SScott Mayhew 		ctx->acdirmax = result.uint_32;
7119954bf92SDavid Howells 		break;
7129954bf92SDavid Howells 	case Opt_namelen:
713e38bb238SScott Mayhew 		ctx->namlen = result.uint_32;
7149954bf92SDavid Howells 		break;
7159954bf92SDavid Howells 	case Opt_mountport:
716e38bb238SScott Mayhew 		if (result.uint_32 > USHRT_MAX)
717e38bb238SScott Mayhew 			goto out_of_bounds;
718e38bb238SScott Mayhew 		ctx->mount_server.port = result.uint_32;
7199954bf92SDavid Howells 		break;
7209954bf92SDavid Howells 	case Opt_mountvers:
721e38bb238SScott Mayhew 		if (result.uint_32 < NFS_MNT_VERSION ||
722e38bb238SScott Mayhew 		    result.uint_32 > NFS_MNT3_VERSION)
723e38bb238SScott Mayhew 			goto out_of_bounds;
724e38bb238SScott Mayhew 		ctx->mount_server.version = result.uint_32;
7259954bf92SDavid Howells 		break;
7269954bf92SDavid Howells 	case Opt_minorversion:
727e38bb238SScott Mayhew 		if (result.uint_32 > NFS4_MAX_MINOR_VERSION)
728e38bb238SScott Mayhew 			goto out_of_bounds;
729e38bb238SScott Mayhew 		ctx->minorversion = result.uint_32;
7309954bf92SDavid Howells 		break;
7319954bf92SDavid Howells 
7329954bf92SDavid Howells 		/*
7339954bf92SDavid Howells 		 * options that take text values
7349954bf92SDavid Howells 		 */
735e38bb238SScott Mayhew 	case Opt_v:
73662a55d08SScott Mayhew 		ret = nfs_parse_version_string(fc, param->key + 1);
737e38bb238SScott Mayhew 		if (ret < 0)
738e38bb238SScott Mayhew 			return ret;
739e38bb238SScott Mayhew 		break;
740e38bb238SScott Mayhew 	case Opt_vers:
7415559405dSHawkins Jiawei 		if (!param->string)
7425559405dSHawkins Jiawei 			goto out_invalid_value;
74333ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
74462a55d08SScott Mayhew 		ret = nfs_parse_version_string(fc, param->string);
745e558100fSDavid Howells 		if (ret < 0)
746e558100fSDavid Howells 			return ret;
7479954bf92SDavid Howells 		break;
7489954bf92SDavid Howells 	case Opt_sec:
74962a55d08SScott Mayhew 		ret = nfs_parse_security_flavors(fc, param);
750e558100fSDavid Howells 		if (ret < 0)
751e558100fSDavid Howells 			return ret;
7529954bf92SDavid Howells 		break;
753c8407f2eSChuck Lever 	case Opt_xprtsec:
754c8407f2eSChuck Lever 		ret = nfs_parse_xprtsec_policy(fc, param);
755c8407f2eSChuck Lever 		if (ret < 0)
756c8407f2eSChuck Lever 			return ret;
757c8407f2eSChuck Lever 		break;
7589954bf92SDavid Howells 
759e38bb238SScott Mayhew 	case Opt_proto:
7605559405dSHawkins Jiawei 		if (!param->string)
7615559405dSHawkins Jiawei 			goto out_invalid_value;
76233ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
763e38bb238SScott Mayhew 		protofamily = AF_INET;
764e38bb238SScott Mayhew 		switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
7659954bf92SDavid Howells 		case Opt_xprt_udp6:
766e38bb238SScott Mayhew 			protofamily = AF_INET6;
767df561f66SGustavo A. R. Silva 			fallthrough;
7689954bf92SDavid Howells 		case Opt_xprt_udp:
7695eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_TCP;
7705eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
7719954bf92SDavid Howells 			break;
7729954bf92SDavid Howells 		case Opt_xprt_tcp6:
773e38bb238SScott Mayhew 			protofamily = AF_INET6;
774df561f66SGustavo A. R. Silva 			fallthrough;
7759954bf92SDavid Howells 		case Opt_xprt_tcp:
7765eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_TCP;
7775eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
7789954bf92SDavid Howells 			break;
7799954bf92SDavid Howells 		case Opt_xprt_rdma6:
780e38bb238SScott Mayhew 			protofamily = AF_INET6;
781df561f66SGustavo A. R. Silva 			fallthrough;
7829954bf92SDavid Howells 		case Opt_xprt_rdma:
7839954bf92SDavid Howells 			/* vector side protocols to TCP */
7845eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_TCP;
7851c3695d0STrond Myklebust 			ret = xprt_find_transport_ident(param->string);
7861c3695d0STrond Myklebust 			if (ret < 0)
7871c3695d0STrond Myklebust 				goto out_bad_transport;
7881c3695d0STrond Myklebust 			ctx->nfs_server.protocol = ret;
7899954bf92SDavid Howells 			break;
7909954bf92SDavid Howells 		default:
7911c3695d0STrond Myklebust 			goto out_bad_transport;
7929954bf92SDavid Howells 		}
7939954bf92SDavid Howells 
794e38bb238SScott Mayhew 		ctx->protofamily = protofamily;
795e38bb238SScott Mayhew 		break;
796e38bb238SScott Mayhew 
797e38bb238SScott Mayhew 	case Opt_mountproto:
7985559405dSHawkins Jiawei 		if (!param->string)
7995559405dSHawkins Jiawei 			goto out_invalid_value;
80033ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
801e38bb238SScott Mayhew 		mountfamily = AF_INET;
802e38bb238SScott Mayhew 		switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
8039954bf92SDavid Howells 		case Opt_xprt_udp6:
804e38bb238SScott Mayhew 			mountfamily = AF_INET6;
805df561f66SGustavo A. R. Silva 			fallthrough;
8069954bf92SDavid Howells 		case Opt_xprt_udp:
8075eb005caSDavid Howells 			ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
8089954bf92SDavid Howells 			break;
8099954bf92SDavid Howells 		case Opt_xprt_tcp6:
810e38bb238SScott Mayhew 			mountfamily = AF_INET6;
811df561f66SGustavo A. R. Silva 			fallthrough;
8129954bf92SDavid Howells 		case Opt_xprt_tcp:
8135eb005caSDavid Howells 			ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
8149954bf92SDavid Howells 			break;
8159954bf92SDavid Howells 		case Opt_xprt_rdma: /* not used for side protocols */
8169954bf92SDavid Howells 		default:
8171c3695d0STrond Myklebust 			goto out_bad_transport;
8189954bf92SDavid Howells 		}
819e38bb238SScott Mayhew 		ctx->mountfamily = mountfamily;
8209954bf92SDavid Howells 		break;
821e38bb238SScott Mayhew 
8229954bf92SDavid Howells 	case Opt_addr:
82333ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
82462a55d08SScott Mayhew 		len = rpc_pton(fc->net_ns, param->string, param->size,
8255eb005caSDavid Howells 			       &ctx->nfs_server.address,
826e558100fSDavid Howells 			       sizeof(ctx->nfs_server._address));
827e38bb238SScott Mayhew 		if (len == 0)
8289954bf92SDavid Howells 			goto out_invalid_address;
829e38bb238SScott Mayhew 		ctx->nfs_server.addrlen = len;
8309954bf92SDavid Howells 		break;
8319954bf92SDavid Howells 	case Opt_clientaddr:
83233ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
833e38bb238SScott Mayhew 		kfree(ctx->client_address);
834e38bb238SScott Mayhew 		ctx->client_address = param->string;
835e38bb238SScott Mayhew 		param->string = NULL;
8369954bf92SDavid Howells 		break;
8379954bf92SDavid Howells 	case Opt_mounthost:
83833ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
839e38bb238SScott Mayhew 		kfree(ctx->mount_server.hostname);
840e38bb238SScott Mayhew 		ctx->mount_server.hostname = param->string;
841e38bb238SScott Mayhew 		param->string = NULL;
8429954bf92SDavid Howells 		break;
8439954bf92SDavid Howells 	case Opt_mountaddr:
84433ce83efSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
84562a55d08SScott Mayhew 		len = rpc_pton(fc->net_ns, param->string, param->size,
8465eb005caSDavid Howells 			       &ctx->mount_server.address,
847e558100fSDavid Howells 			       sizeof(ctx->mount_server._address));
848e38bb238SScott Mayhew 		if (len == 0)
8499954bf92SDavid Howells 			goto out_invalid_address;
850e38bb238SScott Mayhew 		ctx->mount_server.addrlen = len;
8519954bf92SDavid Howells 		break;
8529954bf92SDavid Howells 	case Opt_nconnect:
8539e8ab85aSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
854e38bb238SScott Mayhew 		if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_CONNECTIONS)
855e38bb238SScott Mayhew 			goto out_of_bounds;
856e38bb238SScott Mayhew 		ctx->nfs_server.nconnect = result.uint_32;
8579954bf92SDavid Howells 		break;
8587e134205SOlga Kornievskaia 	case Opt_max_connect:
8599e8ab85aSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
8607e134205SOlga Kornievskaia 		if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_TRANSPORTS)
8617e134205SOlga Kornievskaia 			goto out_of_bounds;
8627e134205SOlga Kornievskaia 		ctx->nfs_server.max_connect = result.uint_32;
8637e134205SOlga Kornievskaia 		break;
8649954bf92SDavid Howells 	case Opt_lookupcache:
8659e8ab85aSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
866e38bb238SScott Mayhew 		switch (result.uint_32) {
8679954bf92SDavid Howells 		case Opt_lookupcache_all:
8685eb005caSDavid Howells 			ctx->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
8699954bf92SDavid Howells 			break;
8709954bf92SDavid Howells 		case Opt_lookupcache_positive:
8715eb005caSDavid Howells 			ctx->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
8725eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
8739954bf92SDavid Howells 			break;
8749954bf92SDavid Howells 		case Opt_lookupcache_none:
8755eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
8769954bf92SDavid Howells 			break;
8779954bf92SDavid Howells 		default:
878e38bb238SScott Mayhew 			goto out_invalid_value;
8799954bf92SDavid Howells 		}
8809954bf92SDavid Howells 		break;
8819954bf92SDavid Howells 	case Opt_local_lock:
8829e8ab85aSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
883e38bb238SScott Mayhew 		switch (result.uint_32) {
8849954bf92SDavid Howells 		case Opt_local_lock_all:
8855eb005caSDavid Howells 			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK |
8869954bf92SDavid Howells 				       NFS_MOUNT_LOCAL_FCNTL);
8879954bf92SDavid Howells 			break;
8889954bf92SDavid Howells 		case Opt_local_lock_flock:
8895eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOCAL_FLOCK;
8909954bf92SDavid Howells 			break;
8919954bf92SDavid Howells 		case Opt_local_lock_posix:
8925eb005caSDavid Howells 			ctx->flags |= NFS_MOUNT_LOCAL_FCNTL;
8939954bf92SDavid Howells 			break;
8949954bf92SDavid Howells 		case Opt_local_lock_none:
8955eb005caSDavid Howells 			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
8969954bf92SDavid Howells 					NFS_MOUNT_LOCAL_FCNTL);
8979954bf92SDavid Howells 			break;
8989954bf92SDavid Howells 		default:
899e38bb238SScott Mayhew 			goto out_invalid_value;
9009954bf92SDavid Howells 		}
9019954bf92SDavid Howells 		break;
902a0492339STrond Myklebust 	case Opt_write:
9039e8ab85aSChuck Lever 		trace_nfs_mount_assign(param->key, param->string);
904a0492339STrond Myklebust 		switch (result.uint_32) {
905a0492339STrond Myklebust 		case Opt_write_lazy:
906a0492339STrond Myklebust 			ctx->flags &=
907a0492339STrond Myklebust 				~(NFS_MOUNT_WRITE_EAGER | NFS_MOUNT_WRITE_WAIT);
908a0492339STrond Myklebust 			break;
909a0492339STrond Myklebust 		case Opt_write_eager:
910a0492339STrond Myklebust 			ctx->flags |= NFS_MOUNT_WRITE_EAGER;
911a0492339STrond Myklebust 			ctx->flags &= ~NFS_MOUNT_WRITE_WAIT;
912a0492339STrond Myklebust 			break;
913a0492339STrond Myklebust 		case Opt_write_wait:
914a0492339STrond Myklebust 			ctx->flags |=
915a0492339STrond Myklebust 				NFS_MOUNT_WRITE_EAGER | NFS_MOUNT_WRITE_WAIT;
916a0492339STrond Myklebust 			break;
917a0492339STrond Myklebust 		default:
918a0492339STrond Myklebust 			goto out_invalid_value;
919a0492339STrond Myklebust 		}
920a0492339STrond Myklebust 		break;
9219954bf92SDavid Howells 
9229954bf92SDavid Howells 		/*
9239954bf92SDavid Howells 		 * Special options
9249954bf92SDavid Howells 		 */
9259954bf92SDavid Howells 	case Opt_sloppy:
926e38bb238SScott Mayhew 		ctx->sloppy = true;
9279954bf92SDavid Howells 		break;
9289954bf92SDavid Howells 	}
9299954bf92SDavid Howells 
930f8ee01e3SDavid Howells 	return 0;
931f8ee01e3SDavid Howells 
932f8ee01e3SDavid Howells out_invalid_value:
933ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Bad mount option value specified");
934e38bb238SScott Mayhew out_invalid_address:
935ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Bad IP address specified");
936e38bb238SScott Mayhew out_of_bounds:
9373a21409aSDavid Howells 	return nfs_invalf(fc, "NFS: Value for '%s' out of range", param->key);
9381c3695d0STrond Myklebust out_bad_transport:
9391c3695d0STrond Myklebust 	return nfs_invalf(fc, "NFS: Unrecognized transport protocol");
940e38bb238SScott Mayhew }
941e38bb238SScott Mayhew 
9429954bf92SDavid Howells /*
94362a55d08SScott Mayhew  * Split fc->source into "hostname:export_path".
9449954bf92SDavid Howells  *
9459954bf92SDavid Howells  * The leftmost colon demarks the split between the server's hostname
9469954bf92SDavid Howells  * and the export path.  If the hostname starts with a left square
9479954bf92SDavid Howells  * bracket, then it may contain colons.
9489954bf92SDavid Howells  *
9499954bf92SDavid Howells  * Note: caller frees hostname and export path, even on error.
9509954bf92SDavid Howells  */
nfs_parse_source(struct fs_context * fc,size_t maxnamlen,size_t maxpathlen)95162a55d08SScott Mayhew static int nfs_parse_source(struct fs_context *fc,
952e558100fSDavid Howells 			    size_t maxnamlen, size_t maxpathlen)
9539954bf92SDavid Howells {
95462a55d08SScott Mayhew 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
95562a55d08SScott Mayhew 	const char *dev_name = fc->source;
9569954bf92SDavid Howells 	size_t len;
95762a55d08SScott Mayhew 	const char *end;
9589954bf92SDavid Howells 
95933ce83efSChuck Lever 	if (unlikely(!dev_name || !*dev_name))
9609954bf92SDavid Howells 		return -EINVAL;
9619954bf92SDavid Howells 
9629954bf92SDavid Howells 	/* Is the host name protected with square brakcets? */
9639954bf92SDavid Howells 	if (*dev_name == '[') {
9649954bf92SDavid Howells 		end = strchr(++dev_name, ']');
9659954bf92SDavid Howells 		if (end == NULL || end[1] != ':')
9669954bf92SDavid Howells 			goto out_bad_devname;
9679954bf92SDavid Howells 
9689954bf92SDavid Howells 		len = end - dev_name;
9699954bf92SDavid Howells 		end++;
9709954bf92SDavid Howells 	} else {
97162a55d08SScott Mayhew 		const char *comma;
9729954bf92SDavid Howells 
9739954bf92SDavid Howells 		end = strchr(dev_name, ':');
9749954bf92SDavid Howells 		if (end == NULL)
9759954bf92SDavid Howells 			goto out_bad_devname;
9769954bf92SDavid Howells 		len = end - dev_name;
9779954bf92SDavid Howells 
9789954bf92SDavid Howells 		/* kill possible hostname list: not supported */
97962a55d08SScott Mayhew 		comma = memchr(dev_name, ',', len);
98062a55d08SScott Mayhew 		if (comma)
9819954bf92SDavid Howells 			len = comma - dev_name;
9829954bf92SDavid Howells 	}
9839954bf92SDavid Howells 
9849954bf92SDavid Howells 	if (len > maxnamlen)
9859954bf92SDavid Howells 		goto out_hostname;
9869954bf92SDavid Howells 
98775a9b917SScott Mayhew 	kfree(ctx->nfs_server.hostname);
98875a9b917SScott Mayhew 
9899954bf92SDavid Howells 	/* N.B. caller will free nfs_server.hostname in all cases */
990e558100fSDavid Howells 	ctx->nfs_server.hostname = kmemdup_nul(dev_name, len, GFP_KERNEL);
991e558100fSDavid Howells 	if (!ctx->nfs_server.hostname)
9929954bf92SDavid Howells 		goto out_nomem;
9939954bf92SDavid Howells 	len = strlen(++end);
9949954bf92SDavid Howells 	if (len > maxpathlen)
9959954bf92SDavid Howells 		goto out_path;
996e558100fSDavid Howells 	ctx->nfs_server.export_path = kmemdup_nul(end, len, GFP_KERNEL);
997e558100fSDavid Howells 	if (!ctx->nfs_server.export_path)
9989954bf92SDavid Howells 		goto out_nomem;
9999954bf92SDavid Howells 
100033ce83efSChuck Lever 	trace_nfs_mount_path(ctx->nfs_server.export_path);
10019954bf92SDavid Howells 	return 0;
10029954bf92SDavid Howells 
10039954bf92SDavid Howells out_bad_devname:
1004ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: device name not in host:path format");
10059954bf92SDavid Howells out_nomem:
1006ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: not enough memory to parse device name");
10079954bf92SDavid Howells 	return -ENOMEM;
10089954bf92SDavid Howells out_hostname:
1009ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: server hostname too long");
10109954bf92SDavid Howells 	return -ENAMETOOLONG;
10119954bf92SDavid Howells out_path:
1012ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: export pathname too long");
10139954bf92SDavid Howells 	return -ENAMETOOLONG;
10149954bf92SDavid Howells }
10159954bf92SDavid Howells 
is_remount_fc(struct fs_context * fc)1016f2aedb71SDavid Howells static inline bool is_remount_fc(struct fs_context *fc)
1017f2aedb71SDavid Howells {
1018f2aedb71SDavid Howells 	return fc->root != NULL;
1019f2aedb71SDavid Howells }
1020f2aedb71SDavid Howells 
10219954bf92SDavid Howells /*
1022e558100fSDavid Howells  * Parse monolithic NFS2/NFS3 mount data
10239954bf92SDavid Howells  * - fills in the mount root filehandle
10249954bf92SDavid Howells  *
10259954bf92SDavid Howells  * For option strings, user space handles the following behaviors:
10269954bf92SDavid Howells  *
10279954bf92SDavid Howells  * + DNS: mapping server host name to IP address ("addr=" option)
10289954bf92SDavid Howells  *
10299954bf92SDavid Howells  * + failure mode: how to behave if a mount request can't be handled
10309954bf92SDavid Howells  *   immediately ("fg/bg" option)
10319954bf92SDavid Howells  *
10329954bf92SDavid Howells  * + retry: how often to retry a mount request ("retry=" option)
10339954bf92SDavid Howells  *
10349954bf92SDavid Howells  * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
10359954bf92SDavid Howells  *   mountproto=tcp after mountproto=udp, and so on
10369954bf92SDavid Howells  */
nfs23_parse_monolithic(struct fs_context * fc,struct nfs_mount_data * data)1037f2aedb71SDavid Howells static int nfs23_parse_monolithic(struct fs_context *fc,
1038f2aedb71SDavid Howells 				  struct nfs_mount_data *data)
10399954bf92SDavid Howells {
1040f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
104162a55d08SScott Mayhew 	struct nfs_fh *mntfh = ctx->mntfh;
1042cf0d7e7fSKees Cook 	struct sockaddr_storage *sap = &ctx->nfs_server._address;
10439954bf92SDavid Howells 	int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
104490ff57bfSTrond Myklebust 	int ret;
10459954bf92SDavid Howells 
10469954bf92SDavid Howells 	if (data == NULL)
10479954bf92SDavid Howells 		goto out_no_data;
10489954bf92SDavid Howells 
10495eb005caSDavid Howells 	ctx->version = NFS_DEFAULT_VERSION;
10509954bf92SDavid Howells 	switch (data->version) {
10519954bf92SDavid Howells 	case 1:
1052df561f66SGustavo A. R. Silva 		data->namlen = 0;
1053df561f66SGustavo A. R. Silva 		fallthrough;
10549954bf92SDavid Howells 	case 2:
1055df561f66SGustavo A. R. Silva 		data->bsize = 0;
1056df561f66SGustavo A. R. Silva 		fallthrough;
10579954bf92SDavid Howells 	case 3:
10589954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_VER3)
10599954bf92SDavid Howells 			goto out_no_v3;
10609954bf92SDavid Howells 		data->root.size = NFS2_FHSIZE;
10619954bf92SDavid Howells 		memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
10629954bf92SDavid Howells 		/* Turn off security negotiation */
10639954bf92SDavid Howells 		extra_flags |= NFS_MOUNT_SECFLAVOUR;
1064df561f66SGustavo A. R. Silva 		fallthrough;
10659954bf92SDavid Howells 	case 4:
10669954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_SECFLAVOUR)
10679954bf92SDavid Howells 			goto out_no_sec;
1068df561f66SGustavo A. R. Silva 		fallthrough;
10699954bf92SDavid Howells 	case 5:
10709954bf92SDavid Howells 		memset(data->context, 0, sizeof(data->context));
1071df561f66SGustavo A. R. Silva 		fallthrough;
10729954bf92SDavid Howells 	case 6:
10739954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_VER3) {
10749954bf92SDavid Howells 			if (data->root.size > NFS3_FHSIZE || data->root.size == 0)
10759954bf92SDavid Howells 				goto out_invalid_fh;
10769954bf92SDavid Howells 			mntfh->size = data->root.size;
10775eb005caSDavid Howells 			ctx->version = 3;
10789954bf92SDavid Howells 		} else {
10799954bf92SDavid Howells 			mntfh->size = NFS2_FHSIZE;
10805eb005caSDavid Howells 			ctx->version = 2;
10819954bf92SDavid Howells 		}
10829954bf92SDavid Howells 
10839954bf92SDavid Howells 
10849954bf92SDavid Howells 		memcpy(mntfh->data, data->root.data, mntfh->size);
10859954bf92SDavid Howells 		if (mntfh->size < sizeof(mntfh->data))
10869954bf92SDavid Howells 			memset(mntfh->data + mntfh->size, 0,
10879954bf92SDavid Howells 			       sizeof(mntfh->data) - mntfh->size);
10889954bf92SDavid Howells 
10899954bf92SDavid Howells 		/*
1090c09f11efSRandy Dunlap 		 * for proto == XPRT_TRANSPORT_UDP, which is what uses
1091c09f11efSRandy Dunlap 		 * to_exponential, implying shift: limit the shift value
1092c09f11efSRandy Dunlap 		 * to BITS_PER_LONG (majortimeo is unsigned long)
1093c09f11efSRandy Dunlap 		 */
1094c09f11efSRandy Dunlap 		if (!(data->flags & NFS_MOUNT_TCP)) /* this will be UDP */
1095c09f11efSRandy Dunlap 			if (data->retrans >= 64) /* shift value is too large */
1096c09f11efSRandy Dunlap 				goto out_invalid_data;
1097c09f11efSRandy Dunlap 
1098c09f11efSRandy Dunlap 		/*
10995eb005caSDavid Howells 		 * Translate to nfs_fs_context, which nfs_fill_super
11009954bf92SDavid Howells 		 * can deal with.
11019954bf92SDavid Howells 		 */
11025eb005caSDavid Howells 		ctx->flags	= data->flags & NFS_MOUNT_FLAGMASK;
11035eb005caSDavid Howells 		ctx->flags	|= extra_flags;
11045eb005caSDavid Howells 		ctx->rsize	= data->rsize;
11055eb005caSDavid Howells 		ctx->wsize	= data->wsize;
11065eb005caSDavid Howells 		ctx->timeo	= data->timeo;
11075eb005caSDavid Howells 		ctx->retrans	= data->retrans;
11085eb005caSDavid Howells 		ctx->acregmin	= data->acregmin;
11095eb005caSDavid Howells 		ctx->acregmax	= data->acregmax;
11105eb005caSDavid Howells 		ctx->acdirmin	= data->acdirmin;
11115eb005caSDavid Howells 		ctx->acdirmax	= data->acdirmax;
11125eb005caSDavid Howells 		ctx->need_mount	= false;
11139954bf92SDavid Howells 
1114*511811a7SMartin Kaiser 		if (!is_remount_fc(fc)) {
11159954bf92SDavid Howells 			memcpy(sap, &data->addr, sizeof(data->addr));
11165eb005caSDavid Howells 			ctx->nfs_server.addrlen = sizeof(data->addr);
11175eb005caSDavid Howells 			ctx->nfs_server.port = ntohs(data->addr.sin_port);
1118*511811a7SMartin Kaiser 		}
1119*511811a7SMartin Kaiser 
1120cf0d7e7fSKees Cook 		if (sap->ss_family != AF_INET ||
11219954bf92SDavid Howells 		    !nfs_verify_server_address(sap))
11229954bf92SDavid Howells 			goto out_no_address;
11239954bf92SDavid Howells 
11249954bf92SDavid Howells 		if (!(data->flags & NFS_MOUNT_TCP))
11255eb005caSDavid Howells 			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
11269954bf92SDavid Howells 		/* N.B. caller will free nfs_server.hostname in all cases */
11275eb005caSDavid Howells 		ctx->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
1128f2aedb71SDavid Howells 		if (!ctx->nfs_server.hostname)
1129f2aedb71SDavid Howells 			goto out_nomem;
1130f2aedb71SDavid Howells 
11315eb005caSDavid Howells 		ctx->namlen		= data->namlen;
11325eb005caSDavid Howells 		ctx->bsize		= data->bsize;
11339954bf92SDavid Howells 
11349954bf92SDavid Howells 		if (data->flags & NFS_MOUNT_SECFLAVOUR)
11355eb005caSDavid Howells 			ctx->selected_flavor = data->pseudoflavor;
11369954bf92SDavid Howells 		else
11375eb005caSDavid Howells 			ctx->selected_flavor = RPC_AUTH_UNIX;
11389954bf92SDavid Howells 
11399954bf92SDavid Howells 		if (!(data->flags & NFS_MOUNT_NONLM))
11405eb005caSDavid Howells 			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK|
11419954bf92SDavid Howells 					 NFS_MOUNT_LOCAL_FCNTL);
11429954bf92SDavid Howells 		else
11435eb005caSDavid Howells 			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK|
11449954bf92SDavid Howells 					NFS_MOUNT_LOCAL_FCNTL);
114562a55d08SScott Mayhew 
11469954bf92SDavid Howells 		/*
11479954bf92SDavid Howells 		 * The legacy version 6 binary mount data from userspace has a
11489954bf92SDavid Howells 		 * field used only to transport selinux information into the
1149a5032910SRandy Dunlap 		 * kernel.  To continue to support that functionality we
11509954bf92SDavid Howells 		 * have a touch of selinux knowledge here in the NFS code. The
11519954bf92SDavid Howells 		 * userspace code converted context=blah to just blah so we are
11529954bf92SDavid Howells 		 * converting back to the full string selinux understands.
11539954bf92SDavid Howells 		 */
11549954bf92SDavid Howells 		if (data->context[0]){
11559954bf92SDavid Howells #ifdef CONFIG_SECURITY_SELINUX
1156f2aedb71SDavid Howells 			int ret;
1157f2aedb71SDavid Howells 
11589954bf92SDavid Howells 			data->context[NFS_MAX_CONTEXT_LEN] = '\0';
1159f2aedb71SDavid Howells 			ret = vfs_parse_fs_string(fc, "context",
1160f2aedb71SDavid Howells 						  data->context, strlen(data->context));
1161f2aedb71SDavid Howells 			if (ret < 0)
1162f2aedb71SDavid Howells 				return ret;
11639954bf92SDavid Howells #else
11649954bf92SDavid Howells 			return -EINVAL;
11659954bf92SDavid Howells #endif
11669954bf92SDavid Howells 		}
11679954bf92SDavid Howells 
11689954bf92SDavid Howells 		break;
11699954bf92SDavid Howells 	default:
1170f2aedb71SDavid Howells 		goto generic;
11719954bf92SDavid Howells 	}
11729954bf92SDavid Howells 
117390ff57bfSTrond Myklebust 	ret = nfs_validate_transport_protocol(fc, ctx);
117490ff57bfSTrond Myklebust 	if (ret)
117590ff57bfSTrond Myklebust 		return ret;
117690ff57bfSTrond Myklebust 
1177f2aedb71SDavid Howells 	ctx->skip_reconfig_option_check = true;
11789954bf92SDavid Howells 	return 0;
11799954bf92SDavid Howells 
1180f2aedb71SDavid Howells generic:
1181f2aedb71SDavid Howells 	return generic_parse_monolithic(fc, data);
1182f2aedb71SDavid Howells 
11839954bf92SDavid Howells out_no_data:
1184f2aedb71SDavid Howells 	if (is_remount_fc(fc)) {
1185f2aedb71SDavid Howells 		ctx->skip_reconfig_option_check = true;
1186f2aedb71SDavid Howells 		return 0;
1187f2aedb71SDavid Howells 	}
1188ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: mount program didn't pass any mount data");
11899954bf92SDavid Howells 
11909954bf92SDavid Howells out_no_v3:
1191ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: nfs_mount_data version does not support v3");
11929954bf92SDavid Howells 
11939954bf92SDavid Howells out_no_sec:
1194ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: nfs_mount_data version supports only AUTH_SYS");
11959954bf92SDavid Howells 
11969954bf92SDavid Howells out_nomem:
11979954bf92SDavid Howells 	return -ENOMEM;
11989954bf92SDavid Howells 
11999954bf92SDavid Howells out_no_address:
1200ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: mount program didn't pass remote address");
12019954bf92SDavid Howells 
12029954bf92SDavid Howells out_invalid_fh:
1203ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: invalid root filehandle");
1204c09f11efSRandy Dunlap 
1205c09f11efSRandy Dunlap out_invalid_data:
1206c09f11efSRandy Dunlap 	return nfs_invalf(fc, "NFS: invalid binary mount data");
12079954bf92SDavid Howells }
12089954bf92SDavid Howells 
12099954bf92SDavid Howells #if IS_ENABLED(CONFIG_NFS_V4)
121067e306c6SChristoph Hellwig struct compat_nfs_string {
121167e306c6SChristoph Hellwig 	compat_uint_t len;
121267e306c6SChristoph Hellwig 	compat_uptr_t data;
121367e306c6SChristoph Hellwig };
121467e306c6SChristoph Hellwig 
compat_nfs_string(struct nfs_string * dst,struct compat_nfs_string * src)121567e306c6SChristoph Hellwig static inline void compat_nfs_string(struct nfs_string *dst,
121667e306c6SChristoph Hellwig 				     struct compat_nfs_string *src)
121767e306c6SChristoph Hellwig {
121867e306c6SChristoph Hellwig 	dst->data = compat_ptr(src->data);
121967e306c6SChristoph Hellwig 	dst->len = src->len;
122067e306c6SChristoph Hellwig }
122167e306c6SChristoph Hellwig 
122267e306c6SChristoph Hellwig struct compat_nfs4_mount_data_v1 {
122367e306c6SChristoph Hellwig 	compat_int_t version;
122467e306c6SChristoph Hellwig 	compat_int_t flags;
122567e306c6SChristoph Hellwig 	compat_int_t rsize;
122667e306c6SChristoph Hellwig 	compat_int_t wsize;
122767e306c6SChristoph Hellwig 	compat_int_t timeo;
122867e306c6SChristoph Hellwig 	compat_int_t retrans;
122967e306c6SChristoph Hellwig 	compat_int_t acregmin;
123067e306c6SChristoph Hellwig 	compat_int_t acregmax;
123167e306c6SChristoph Hellwig 	compat_int_t acdirmin;
123267e306c6SChristoph Hellwig 	compat_int_t acdirmax;
123367e306c6SChristoph Hellwig 	struct compat_nfs_string client_addr;
123467e306c6SChristoph Hellwig 	struct compat_nfs_string mnt_path;
123567e306c6SChristoph Hellwig 	struct compat_nfs_string hostname;
123667e306c6SChristoph Hellwig 	compat_uint_t host_addrlen;
123767e306c6SChristoph Hellwig 	compat_uptr_t host_addr;
123867e306c6SChristoph Hellwig 	compat_int_t proto;
123967e306c6SChristoph Hellwig 	compat_int_t auth_flavourlen;
124067e306c6SChristoph Hellwig 	compat_uptr_t auth_flavours;
124167e306c6SChristoph Hellwig };
124267e306c6SChristoph Hellwig 
nfs4_compat_mount_data_conv(struct nfs4_mount_data * data)124367e306c6SChristoph Hellwig static void nfs4_compat_mount_data_conv(struct nfs4_mount_data *data)
124467e306c6SChristoph Hellwig {
124567e306c6SChristoph Hellwig 	struct compat_nfs4_mount_data_v1 *compat =
124667e306c6SChristoph Hellwig 			(struct compat_nfs4_mount_data_v1 *)data;
124767e306c6SChristoph Hellwig 
124867e306c6SChristoph Hellwig 	/* copy the fields backwards */
124967e306c6SChristoph Hellwig 	data->auth_flavours = compat_ptr(compat->auth_flavours);
125067e306c6SChristoph Hellwig 	data->auth_flavourlen = compat->auth_flavourlen;
125167e306c6SChristoph Hellwig 	data->proto = compat->proto;
125267e306c6SChristoph Hellwig 	data->host_addr = compat_ptr(compat->host_addr);
125367e306c6SChristoph Hellwig 	data->host_addrlen = compat->host_addrlen;
125467e306c6SChristoph Hellwig 	compat_nfs_string(&data->hostname, &compat->hostname);
125567e306c6SChristoph Hellwig 	compat_nfs_string(&data->mnt_path, &compat->mnt_path);
125667e306c6SChristoph Hellwig 	compat_nfs_string(&data->client_addr, &compat->client_addr);
125767e306c6SChristoph Hellwig 	data->acdirmax = compat->acdirmax;
125867e306c6SChristoph Hellwig 	data->acdirmin = compat->acdirmin;
125967e306c6SChristoph Hellwig 	data->acregmax = compat->acregmax;
126067e306c6SChristoph Hellwig 	data->acregmin = compat->acregmin;
126167e306c6SChristoph Hellwig 	data->retrans = compat->retrans;
126267e306c6SChristoph Hellwig 	data->timeo = compat->timeo;
126367e306c6SChristoph Hellwig 	data->wsize = compat->wsize;
126467e306c6SChristoph Hellwig 	data->rsize = compat->rsize;
126567e306c6SChristoph Hellwig 	data->flags = compat->flags;
126667e306c6SChristoph Hellwig 	data->version = compat->version;
126767e306c6SChristoph Hellwig }
126867e306c6SChristoph Hellwig 
12699954bf92SDavid Howells /*
12709954bf92SDavid Howells  * Validate NFSv4 mount options
12719954bf92SDavid Howells  */
nfs4_parse_monolithic(struct fs_context * fc,struct nfs4_mount_data * data)1272f2aedb71SDavid Howells static int nfs4_parse_monolithic(struct fs_context *fc,
1273f2aedb71SDavid Howells 				 struct nfs4_mount_data *data)
12749954bf92SDavid Howells {
1275f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1276cf0d7e7fSKees Cook 	struct sockaddr_storage *sap = &ctx->nfs_server._address;
127790ff57bfSTrond Myklebust 	int ret;
12789954bf92SDavid Howells 	char *c;
12799954bf92SDavid Howells 
1280a1c7dc5dSChristoph Hellwig 	if (!data) {
1281a1c7dc5dSChristoph Hellwig 		if (is_remount_fc(fc))
1282a1c7dc5dSChristoph Hellwig 			goto done;
1283a1c7dc5dSChristoph Hellwig 		return nfs_invalf(fc,
1284a1c7dc5dSChristoph Hellwig 			"NFS4: mount program didn't pass any mount data");
1285a1c7dc5dSChristoph Hellwig 	}
12869954bf92SDavid Howells 
12875eb005caSDavid Howells 	ctx->version = 4;
12889954bf92SDavid Howells 
1289a1c7dc5dSChristoph Hellwig 	if (data->version != 1)
1290a1c7dc5dSChristoph Hellwig 		return generic_parse_monolithic(fc, data);
1291a1c7dc5dSChristoph Hellwig 
129267e306c6SChristoph Hellwig 	if (in_compat_syscall())
129367e306c6SChristoph Hellwig 		nfs4_compat_mount_data_conv(data);
129467e306c6SChristoph Hellwig 
12955eb005caSDavid Howells 	if (data->host_addrlen > sizeof(ctx->nfs_server.address))
12969954bf92SDavid Howells 		goto out_no_address;
12979954bf92SDavid Howells 	if (data->host_addrlen == 0)
12989954bf92SDavid Howells 		goto out_no_address;
12995eb005caSDavid Howells 	ctx->nfs_server.addrlen = data->host_addrlen;
13009954bf92SDavid Howells 	if (copy_from_user(sap, data->host_addr, data->host_addrlen))
13019954bf92SDavid Howells 		return -EFAULT;
13029954bf92SDavid Howells 	if (!nfs_verify_server_address(sap))
13039954bf92SDavid Howells 		goto out_no_address;
13045eb005caSDavid Howells 	ctx->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
13059954bf92SDavid Howells 
13069954bf92SDavid Howells 	if (data->auth_flavourlen) {
13079954bf92SDavid Howells 		rpc_authflavor_t pseudoflavor;
1308a1c7dc5dSChristoph Hellwig 
13099954bf92SDavid Howells 		if (data->auth_flavourlen > 1)
13109954bf92SDavid Howells 			goto out_inval_auth;
1311a1c7dc5dSChristoph Hellwig 		if (copy_from_user(&pseudoflavor, data->auth_flavours,
13129954bf92SDavid Howells 				   sizeof(pseudoflavor)))
13139954bf92SDavid Howells 			return -EFAULT;
13145eb005caSDavid Howells 		ctx->selected_flavor = pseudoflavor;
1315a1c7dc5dSChristoph Hellwig 	} else {
13165eb005caSDavid Howells 		ctx->selected_flavor = RPC_AUTH_UNIX;
1317a1c7dc5dSChristoph Hellwig 	}
13189954bf92SDavid Howells 
13199954bf92SDavid Howells 	c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
13209954bf92SDavid Howells 	if (IS_ERR(c))
13219954bf92SDavid Howells 		return PTR_ERR(c);
13225eb005caSDavid Howells 	ctx->nfs_server.hostname = c;
13239954bf92SDavid Howells 
13249954bf92SDavid Howells 	c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
13259954bf92SDavid Howells 	if (IS_ERR(c))
13269954bf92SDavid Howells 		return PTR_ERR(c);
13275eb005caSDavid Howells 	ctx->nfs_server.export_path = c;
132833ce83efSChuck Lever 	trace_nfs_mount_path(c);
13299954bf92SDavid Howells 
13309954bf92SDavid Howells 	c = strndup_user(data->client_addr.data, 16);
13319954bf92SDavid Howells 	if (IS_ERR(c))
13329954bf92SDavid Howells 		return PTR_ERR(c);
13335eb005caSDavid Howells 	ctx->client_address = c;
13349954bf92SDavid Howells 
13359954bf92SDavid Howells 	/*
1336f2aedb71SDavid Howells 	 * Translate to nfs_fs_context, which nfs_fill_super
13379954bf92SDavid Howells 	 * can deal with.
13389954bf92SDavid Howells 	 */
13399954bf92SDavid Howells 
13405eb005caSDavid Howells 	ctx->flags	= data->flags & NFS4_MOUNT_FLAGMASK;
13415eb005caSDavid Howells 	ctx->rsize	= data->rsize;
13425eb005caSDavid Howells 	ctx->wsize	= data->wsize;
13435eb005caSDavid Howells 	ctx->timeo	= data->timeo;
13445eb005caSDavid Howells 	ctx->retrans	= data->retrans;
13455eb005caSDavid Howells 	ctx->acregmin	= data->acregmin;
13465eb005caSDavid Howells 	ctx->acregmax	= data->acregmax;
13475eb005caSDavid Howells 	ctx->acdirmin	= data->acdirmin;
13485eb005caSDavid Howells 	ctx->acdirmax	= data->acdirmax;
13495eb005caSDavid Howells 	ctx->nfs_server.protocol = data->proto;
135090ff57bfSTrond Myklebust 	ret = nfs_validate_transport_protocol(fc, ctx);
135190ff57bfSTrond Myklebust 	if (ret)
135290ff57bfSTrond Myklebust 		return ret;
1353a1c7dc5dSChristoph Hellwig done:
1354f2aedb71SDavid Howells 	ctx->skip_reconfig_option_check = true;
13559954bf92SDavid Howells 	return 0;
13569954bf92SDavid Howells 
13579954bf92SDavid Howells out_inval_auth:
1358ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS4: Invalid number of RPC auth flavours %d",
13599954bf92SDavid Howells 		      data->auth_flavourlen);
13609954bf92SDavid Howells 
13619954bf92SDavid Howells out_no_address:
1362ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS4: mount program didn't pass remote address");
13639954bf92SDavid Howells }
13649954bf92SDavid Howells #endif
13659954bf92SDavid Howells 
1366f2aedb71SDavid Howells /*
1367f2aedb71SDavid Howells  * Parse a monolithic block of data from sys_mount().
1368f2aedb71SDavid Howells  */
nfs_fs_context_parse_monolithic(struct fs_context * fc,void * data)1369f2aedb71SDavid Howells static int nfs_fs_context_parse_monolithic(struct fs_context *fc,
1370f2aedb71SDavid Howells 					   void *data)
13719954bf92SDavid Howells {
1372f2aedb71SDavid Howells 	if (fc->fs_type == &nfs_fs_type)
1373f2aedb71SDavid Howells 		return nfs23_parse_monolithic(fc, data);
1374f2aedb71SDavid Howells 
1375f2aedb71SDavid Howells #if IS_ENABLED(CONFIG_NFS_V4)
1376f2aedb71SDavid Howells 	if (fc->fs_type == &nfs4_fs_type)
1377f2aedb71SDavid Howells 		return nfs4_parse_monolithic(fc, data);
1378f2aedb71SDavid Howells #endif
1379f2aedb71SDavid Howells 
1380ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Unsupported monolithic data version");
1381f2aedb71SDavid Howells }
1382f2aedb71SDavid Howells 
1383f2aedb71SDavid Howells /*
1384f2aedb71SDavid Howells  * Validate the preparsed information in the config.
1385f2aedb71SDavid Howells  */
nfs_fs_context_validate(struct fs_context * fc)1386f2aedb71SDavid Howells static int nfs_fs_context_validate(struct fs_context *fc)
1387f2aedb71SDavid Howells {
1388f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1389f2aedb71SDavid Howells 	struct nfs_subversion *nfs_mod;
1390cf0d7e7fSKees Cook 	struct sockaddr_storage *sap = &ctx->nfs_server._address;
13919954bf92SDavid Howells 	int max_namelen = PAGE_SIZE;
13929954bf92SDavid Howells 	int max_pathlen = NFS_MAXPATHLEN;
1393f2aedb71SDavid Howells 	int port = 0;
1394f2aedb71SDavid Howells 	int ret;
13959954bf92SDavid Howells 
1396f2aedb71SDavid Howells 	if (!fc->source)
1397f2aedb71SDavid Howells 		goto out_no_device_name;
1398f2aedb71SDavid Howells 
1399f2aedb71SDavid Howells 	/* Check for sanity first. */
1400f2aedb71SDavid Howells 	if (ctx->minorversion && ctx->version != 4)
1401f2aedb71SDavid Howells 		goto out_minorversion_mismatch;
1402f2aedb71SDavid Howells 
1403f2aedb71SDavid Howells 	if (ctx->options & NFS_OPTION_MIGRATION &&
1404f2aedb71SDavid Howells 	    (ctx->version != 4 || ctx->minorversion != 0))
1405f2aedb71SDavid Howells 		goto out_migration_misuse;
1406f2aedb71SDavid Howells 
1407f2aedb71SDavid Howells 	/* Verify that any proto=/mountproto= options match the address
1408f2aedb71SDavid Howells 	 * families in the addr=/mountaddr= options.
1409f2aedb71SDavid Howells 	 */
1410f2aedb71SDavid Howells 	if (ctx->protofamily != AF_UNSPEC &&
1411f2aedb71SDavid Howells 	    ctx->protofamily != ctx->nfs_server.address.sa_family)
1412f2aedb71SDavid Howells 		goto out_proto_mismatch;
1413f2aedb71SDavid Howells 
1414f2aedb71SDavid Howells 	if (ctx->mountfamily != AF_UNSPEC) {
1415f2aedb71SDavid Howells 		if (ctx->mount_server.addrlen) {
1416f2aedb71SDavid Howells 			if (ctx->mountfamily != ctx->mount_server.address.sa_family)
1417f2aedb71SDavid Howells 				goto out_mountproto_mismatch;
1418f2aedb71SDavid Howells 		} else {
1419f2aedb71SDavid Howells 			if (ctx->mountfamily != ctx->nfs_server.address.sa_family)
1420f2aedb71SDavid Howells 				goto out_mountproto_mismatch;
1421f2aedb71SDavid Howells 		}
1422f2aedb71SDavid Howells 	}
14239954bf92SDavid Howells 
14249954bf92SDavid Howells 	if (!nfs_verify_server_address(sap))
14259954bf92SDavid Howells 		goto out_no_address;
14269954bf92SDavid Howells 
142790ff57bfSTrond Myklebust 	ret = nfs_validate_transport_protocol(fc, ctx);
142890ff57bfSTrond Myklebust 	if (ret)
142990ff57bfSTrond Myklebust 		return ret;
143090ff57bfSTrond Myklebust 
14315eb005caSDavid Howells 	if (ctx->version == 4) {
143262a55d08SScott Mayhew 		if (IS_ENABLED(CONFIG_NFS_V4)) {
14335eb005caSDavid Howells 			if (ctx->nfs_server.protocol == XPRT_TRANSPORT_RDMA)
14349954bf92SDavid Howells 				port = NFS_RDMA_PORT;
14359954bf92SDavid Howells 			else
14369954bf92SDavid Howells 				port = NFS_PORT;
14379954bf92SDavid Howells 			max_namelen = NFS4_MAXNAMLEN;
14389954bf92SDavid Howells 			max_pathlen = NFS4_MAXPATHLEN;
143962a55d08SScott Mayhew 			ctx->flags &= ~(NFS_MOUNT_NONLM | NFS_MOUNT_NOACL |
144062a55d08SScott Mayhew 					NFS_MOUNT_VER3 | NFS_MOUNT_LOCAL_FLOCK |
144162a55d08SScott Mayhew 					NFS_MOUNT_LOCAL_FCNTL);
144262a55d08SScott Mayhew 		} else {
14439954bf92SDavid Howells 			goto out_v4_not_compiled;
144462a55d08SScott Mayhew 		}
14459954bf92SDavid Howells 	} else {
14465eb005caSDavid Howells 		nfs_set_mount_transport_protocol(ctx);
14475eb005caSDavid Howells 		if (ctx->nfs_server.protocol == XPRT_TRANSPORT_RDMA)
14489954bf92SDavid Howells 			port = NFS_RDMA_PORT;
14499954bf92SDavid Howells 	}
14509954bf92SDavid Howells 
14515eb005caSDavid Howells 	nfs_set_port(sap, &ctx->nfs_server.port, port);
14529954bf92SDavid Howells 
145362a55d08SScott Mayhew 	ret = nfs_parse_source(fc, max_namelen, max_pathlen);
1454f2aedb71SDavid Howells 	if (ret < 0)
1455f2aedb71SDavid Howells 		return ret;
14569954bf92SDavid Howells 
1457f2aedb71SDavid Howells 	/* Load the NFS protocol module if we haven't done so yet */
145862a55d08SScott Mayhew 	if (!ctx->nfs_mod) {
1459f2aedb71SDavid Howells 		nfs_mod = get_nfs_version(ctx->version);
1460f2aedb71SDavid Howells 		if (IS_ERR(nfs_mod)) {
1461f2aedb71SDavid Howells 			ret = PTR_ERR(nfs_mod);
1462f2aedb71SDavid Howells 			goto out_version_unavailable;
1463f2aedb71SDavid Howells 		}
146462a55d08SScott Mayhew 		ctx->nfs_mod = nfs_mod;
1465f2aedb71SDavid Howells 	}
14661cef2184SScott Mayhew 
14671cef2184SScott Mayhew 	/* Ensure the filesystem context has the correct fs_type */
14681cef2184SScott Mayhew 	if (fc->fs_type != ctx->nfs_mod->nfs_fs) {
14691cef2184SScott Mayhew 		module_put(fc->fs_type->owner);
14701cef2184SScott Mayhew 		__module_get(ctx->nfs_mod->nfs_fs->owner);
14711cef2184SScott Mayhew 		fc->fs_type = ctx->nfs_mod->nfs_fs;
14721cef2184SScott Mayhew 	}
1473f2aedb71SDavid Howells 	return 0;
1474f2aedb71SDavid Howells 
1475f2aedb71SDavid Howells out_no_device_name:
1476ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Device name not specified");
14779954bf92SDavid Howells out_v4_not_compiled:
1478ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: NFSv4 is not compiled into kernel");
14799954bf92SDavid Howells 	return -EPROTONOSUPPORT;
14809954bf92SDavid Howells out_no_address:
1481ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: mount program didn't pass remote address");
1482f2aedb71SDavid Howells out_mountproto_mismatch:
1483ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Mount server address does not match mountproto= option");
1484f2aedb71SDavid Howells out_proto_mismatch:
1485ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Server address does not match proto= option");
1486f2aedb71SDavid Howells out_minorversion_mismatch:
1487ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: Mount option vers=%u does not support minorversion=%u",
1488f2aedb71SDavid Howells 			  ctx->version, ctx->minorversion);
1489f2aedb71SDavid Howells out_migration_misuse:
1490ce8866f0SScott Mayhew 	return nfs_invalf(fc, "NFS: 'Migration' not supported for this NFS version");
1491f2aedb71SDavid Howells out_version_unavailable:
1492ce8866f0SScott Mayhew 	nfs_errorf(fc, "NFS: Version unavailable");
1493f2aedb71SDavid Howells 	return ret;
14949954bf92SDavid Howells }
1495f2aedb71SDavid Howells 
1496f2aedb71SDavid Howells /*
1497f2aedb71SDavid Howells  * Create an NFS superblock by the appropriate method.
1498f2aedb71SDavid Howells  */
nfs_get_tree(struct fs_context * fc)1499f2aedb71SDavid Howells static int nfs_get_tree(struct fs_context *fc)
1500f2aedb71SDavid Howells {
1501f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1502f2aedb71SDavid Howells 	int err = nfs_fs_context_validate(fc);
1503f2aedb71SDavid Howells 
1504f2aedb71SDavid Howells 	if (err)
1505f2aedb71SDavid Howells 		return err;
1506f2aedb71SDavid Howells 	if (!ctx->internal)
150762a55d08SScott Mayhew 		return ctx->nfs_mod->rpc_ops->try_get_tree(fc);
1508f2aedb71SDavid Howells 	else
1509f2aedb71SDavid Howells 		return nfs_get_tree_common(fc);
1510f2aedb71SDavid Howells }
1511f2aedb71SDavid Howells 
1512f2aedb71SDavid Howells /*
1513f2aedb71SDavid Howells  * Handle duplication of a configuration.  The caller copied *src into *sc, but
1514f2aedb71SDavid Howells  * it can't deal with resource pointers in the filesystem context, so we have
1515f2aedb71SDavid Howells  * to do that.  We need to clear pointers, copy data or get extra refs as
1516f2aedb71SDavid Howells  * appropriate.
1517f2aedb71SDavid Howells  */
nfs_fs_context_dup(struct fs_context * fc,struct fs_context * src_fc)1518f2aedb71SDavid Howells static int nfs_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
1519f2aedb71SDavid Howells {
1520f2aedb71SDavid Howells 	struct nfs_fs_context *src = nfs_fc2context(src_fc), *ctx;
1521f2aedb71SDavid Howells 
1522f2aedb71SDavid Howells 	ctx = kmemdup(src, sizeof(struct nfs_fs_context), GFP_KERNEL);
1523f2aedb71SDavid Howells 	if (!ctx)
1524f2aedb71SDavid Howells 		return -ENOMEM;
1525f2aedb71SDavid Howells 
152662a55d08SScott Mayhew 	ctx->mntfh = nfs_alloc_fhandle();
152762a55d08SScott Mayhew 	if (!ctx->mntfh) {
1528f2aedb71SDavid Howells 		kfree(ctx);
1529f2aedb71SDavid Howells 		return -ENOMEM;
1530f2aedb71SDavid Howells 	}
153162a55d08SScott Mayhew 	nfs_copy_fh(ctx->mntfh, src->mntfh);
1532f2aedb71SDavid Howells 
153362a55d08SScott Mayhew 	__module_get(ctx->nfs_mod->owner);
1534f2aedb71SDavid Howells 	ctx->client_address		= NULL;
1535f2aedb71SDavid Howells 	ctx->mount_server.hostname	= NULL;
1536f2aedb71SDavid Howells 	ctx->nfs_server.export_path	= NULL;
1537f2aedb71SDavid Howells 	ctx->nfs_server.hostname	= NULL;
1538f2aedb71SDavid Howells 	ctx->fscache_uniq		= NULL;
1539f2aedb71SDavid Howells 	ctx->clone_data.fattr		= NULL;
1540f2aedb71SDavid Howells 	fc->fs_private = ctx;
1541f2aedb71SDavid Howells 	return 0;
1542f2aedb71SDavid Howells }
1543f2aedb71SDavid Howells 
nfs_fs_context_free(struct fs_context * fc)1544f2aedb71SDavid Howells static void nfs_fs_context_free(struct fs_context *fc)
1545f2aedb71SDavid Howells {
1546f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
1547f2aedb71SDavid Howells 
1548f2aedb71SDavid Howells 	if (ctx) {
154962a55d08SScott Mayhew 		if (ctx->server)
155062a55d08SScott Mayhew 			nfs_free_server(ctx->server);
155162a55d08SScott Mayhew 		if (ctx->nfs_mod)
155262a55d08SScott Mayhew 			put_nfs_version(ctx->nfs_mod);
1553f2aedb71SDavid Howells 		kfree(ctx->client_address);
1554f2aedb71SDavid Howells 		kfree(ctx->mount_server.hostname);
1555f2aedb71SDavid Howells 		kfree(ctx->nfs_server.export_path);
1556f2aedb71SDavid Howells 		kfree(ctx->nfs_server.hostname);
1557f2aedb71SDavid Howells 		kfree(ctx->fscache_uniq);
155862a55d08SScott Mayhew 		nfs_free_fhandle(ctx->mntfh);
1559f2aedb71SDavid Howells 		nfs_free_fattr(ctx->clone_data.fattr);
1560f2aedb71SDavid Howells 		kfree(ctx);
1561f2aedb71SDavid Howells 	}
1562f2aedb71SDavid Howells }
1563f2aedb71SDavid Howells 
1564f2aedb71SDavid Howells static const struct fs_context_operations nfs_fs_context_ops = {
1565f2aedb71SDavid Howells 	.free			= nfs_fs_context_free,
1566f2aedb71SDavid Howells 	.dup			= nfs_fs_context_dup,
1567f2aedb71SDavid Howells 	.parse_param		= nfs_fs_context_parse_param,
1568f2aedb71SDavid Howells 	.parse_monolithic	= nfs_fs_context_parse_monolithic,
1569f2aedb71SDavid Howells 	.get_tree		= nfs_get_tree,
1570f2aedb71SDavid Howells 	.reconfigure		= nfs_reconfigure,
1571f2aedb71SDavid Howells };
1572f2aedb71SDavid Howells 
1573f2aedb71SDavid Howells /*
1574f2aedb71SDavid Howells  * Prepare superblock configuration.  We use the namespaces attached to the
1575f2aedb71SDavid Howells  * context.  This may be the current process's namespaces, or it may be a
1576f2aedb71SDavid Howells  * container's namespaces.
1577f2aedb71SDavid Howells  */
nfs_init_fs_context(struct fs_context * fc)1578f2aedb71SDavid Howells static int nfs_init_fs_context(struct fs_context *fc)
1579f2aedb71SDavid Howells {
1580f2aedb71SDavid Howells 	struct nfs_fs_context *ctx;
1581f2aedb71SDavid Howells 
1582f2aedb71SDavid Howells 	ctx = kzalloc(sizeof(struct nfs_fs_context), GFP_KERNEL);
1583f2aedb71SDavid Howells 	if (unlikely(!ctx))
1584f2aedb71SDavid Howells 		return -ENOMEM;
1585f2aedb71SDavid Howells 
158662a55d08SScott Mayhew 	ctx->mntfh = nfs_alloc_fhandle();
158762a55d08SScott Mayhew 	if (unlikely(!ctx->mntfh)) {
1588f2aedb71SDavid Howells 		kfree(ctx);
1589f2aedb71SDavid Howells 		return -ENOMEM;
1590f2aedb71SDavid Howells 	}
1591f2aedb71SDavid Howells 
1592f2aedb71SDavid Howells 	ctx->protofamily	= AF_UNSPEC;
1593f2aedb71SDavid Howells 	ctx->mountfamily	= AF_UNSPEC;
1594f2aedb71SDavid Howells 	ctx->mount_server.port	= NFS_UNSPEC_PORT;
1595f2aedb71SDavid Howells 
1596f2aedb71SDavid Howells 	if (fc->root) {
1597f2aedb71SDavid Howells 		/* reconfigure, start with the current config */
1598f2aedb71SDavid Howells 		struct nfs_server *nfss = fc->root->d_sb->s_fs_info;
1599f2aedb71SDavid Howells 		struct net *net = nfss->nfs_client->cl_net;
1600f2aedb71SDavid Howells 
1601f2aedb71SDavid Howells 		ctx->flags		= nfss->flags;
1602f2aedb71SDavid Howells 		ctx->rsize		= nfss->rsize;
1603f2aedb71SDavid Howells 		ctx->wsize		= nfss->wsize;
1604f2aedb71SDavid Howells 		ctx->retrans		= nfss->client->cl_timeout->to_retries;
1605f2aedb71SDavid Howells 		ctx->selected_flavor	= nfss->client->cl_auth->au_flavor;
1606f2aedb71SDavid Howells 		ctx->acregmin		= nfss->acregmin / HZ;
1607f2aedb71SDavid Howells 		ctx->acregmax		= nfss->acregmax / HZ;
1608f2aedb71SDavid Howells 		ctx->acdirmin		= nfss->acdirmin / HZ;
1609f2aedb71SDavid Howells 		ctx->acdirmax		= nfss->acdirmax / HZ;
1610f2aedb71SDavid Howells 		ctx->timeo		= 10U * nfss->client->cl_timeout->to_initval / HZ;
1611f2aedb71SDavid Howells 		ctx->nfs_server.port	= nfss->port;
1612f2aedb71SDavid Howells 		ctx->nfs_server.addrlen	= nfss->nfs_client->cl_addrlen;
1613f2aedb71SDavid Howells 		ctx->version		= nfss->nfs_client->rpc_ops->version;
1614f2aedb71SDavid Howells 		ctx->minorversion	= nfss->nfs_client->cl_minorversion;
1615f2aedb71SDavid Howells 
1616cf0d7e7fSKees Cook 		memcpy(&ctx->nfs_server._address, &nfss->nfs_client->cl_addr,
1617f2aedb71SDavid Howells 			ctx->nfs_server.addrlen);
1618f2aedb71SDavid Howells 
1619f2aedb71SDavid Howells 		if (fc->net_ns != net) {
1620f2aedb71SDavid Howells 			put_net(fc->net_ns);
1621f2aedb71SDavid Howells 			fc->net_ns = get_net(net);
1622f2aedb71SDavid Howells 		}
1623f2aedb71SDavid Howells 
162462a55d08SScott Mayhew 		ctx->nfs_mod = nfss->nfs_client->cl_nfs_mod;
162562a55d08SScott Mayhew 		__module_get(ctx->nfs_mod->owner);
1626f2aedb71SDavid Howells 	} else {
1627f2aedb71SDavid Howells 		/* defaults */
1628f2aedb71SDavid Howells 		ctx->timeo		= NFS_UNSPEC_TIMEO;
1629f2aedb71SDavid Howells 		ctx->retrans		= NFS_UNSPEC_RETRANS;
1630f2aedb71SDavid Howells 		ctx->acregmin		= NFS_DEF_ACREGMIN;
1631f2aedb71SDavid Howells 		ctx->acregmax		= NFS_DEF_ACREGMAX;
1632f2aedb71SDavid Howells 		ctx->acdirmin		= NFS_DEF_ACDIRMIN;
1633f2aedb71SDavid Howells 		ctx->acdirmax		= NFS_DEF_ACDIRMAX;
1634f2aedb71SDavid Howells 		ctx->nfs_server.port	= NFS_UNSPEC_PORT;
1635f2aedb71SDavid Howells 		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
1636f2aedb71SDavid Howells 		ctx->selected_flavor	= RPC_AUTH_MAXFLAVOR;
1637f2aedb71SDavid Howells 		ctx->minorversion	= 0;
1638f2aedb71SDavid Howells 		ctx->need_mount		= true;
1639c8407f2eSChuck Lever 		ctx->xprtsec.policy	= RPC_XPRTSEC_NONE;
1640c8407f2eSChuck Lever 		ctx->xprtsec.cert_serial	= TLS_NO_CERT;
1641c8407f2eSChuck Lever 		ctx->xprtsec.privkey_serial	= TLS_NO_PRIVKEY;
16426c17260cSTrond Myklebust 
16436c17260cSTrond Myklebust 		fc->s_iflags		|= SB_I_STABLE_WRITES;
1644f2aedb71SDavid Howells 	}
1645f2aedb71SDavid Howells 	fc->fs_private = ctx;
1646f2aedb71SDavid Howells 	fc->ops = &nfs_fs_context_ops;
1647f2aedb71SDavid Howells 	return 0;
1648f2aedb71SDavid Howells }
1649f2aedb71SDavid Howells 
1650f2aedb71SDavid Howells struct file_system_type nfs_fs_type = {
1651f2aedb71SDavid Howells 	.owner			= THIS_MODULE,
1652f2aedb71SDavid Howells 	.name			= "nfs",
1653f2aedb71SDavid Howells 	.init_fs_context	= nfs_init_fs_context,
1654d7167b14SAl Viro 	.parameters		= nfs_fs_parameters,
1655f2aedb71SDavid Howells 	.kill_sb		= nfs_kill_super,
1656f2aedb71SDavid Howells 	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
1657f2aedb71SDavid Howells };
1658f2aedb71SDavid Howells MODULE_ALIAS_FS("nfs");
1659f2aedb71SDavid Howells EXPORT_SYMBOL_GPL(nfs_fs_type);
1660f2aedb71SDavid Howells 
1661f2aedb71SDavid Howells #if IS_ENABLED(CONFIG_NFS_V4)
1662f2aedb71SDavid Howells struct file_system_type nfs4_fs_type = {
1663f2aedb71SDavid Howells 	.owner			= THIS_MODULE,
1664f2aedb71SDavid Howells 	.name			= "nfs4",
1665f2aedb71SDavid Howells 	.init_fs_context	= nfs_init_fs_context,
1666d7167b14SAl Viro 	.parameters		= nfs_fs_parameters,
1667f2aedb71SDavid Howells 	.kill_sb		= nfs_kill_super,
1668f2aedb71SDavid Howells 	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
1669f2aedb71SDavid Howells };
1670f2aedb71SDavid Howells MODULE_ALIAS_FS("nfs4");
1671f2aedb71SDavid Howells MODULE_ALIAS("nfs4");
1672f2aedb71SDavid Howells EXPORT_SYMBOL_GPL(nfs4_fs_type);
1673f2aedb71SDavid Howells #endif /* CONFIG_NFS_V4 */
1674