162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/nfs/fs_context.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1992 Rick Sladkey
662306a36Sopenharmony_ci * Conversion to new mount api Copyright (C) David Howells
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * NFS mount handling.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Split from fs/nfs/super.c by David Howells <dhowells@redhat.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/compat.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/fs.h>
1662306a36Sopenharmony_ci#include <linux/fs_context.h>
1762306a36Sopenharmony_ci#include <linux/fs_parser.h>
1862306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1962306a36Sopenharmony_ci#include <linux/nfs_mount.h>
2062306a36Sopenharmony_ci#include <linux/nfs4_mount.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <net/handshake.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "nfs.h"
2562306a36Sopenharmony_ci#include "internal.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "nfstrace.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_MOUNT
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V3)
3262306a36Sopenharmony_ci#define NFS_DEFAULT_VERSION 3
3362306a36Sopenharmony_ci#else
3462306a36Sopenharmony_ci#define NFS_DEFAULT_VERSION 2
3562306a36Sopenharmony_ci#endif
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define NFS_MAX_CONNECTIONS 16
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cienum nfs_param {
4062306a36Sopenharmony_ci	Opt_ac,
4162306a36Sopenharmony_ci	Opt_acdirmax,
4262306a36Sopenharmony_ci	Opt_acdirmin,
4362306a36Sopenharmony_ci	Opt_acl,
4462306a36Sopenharmony_ci	Opt_acregmax,
4562306a36Sopenharmony_ci	Opt_acregmin,
4662306a36Sopenharmony_ci	Opt_actimeo,
4762306a36Sopenharmony_ci	Opt_addr,
4862306a36Sopenharmony_ci	Opt_bg,
4962306a36Sopenharmony_ci	Opt_bsize,
5062306a36Sopenharmony_ci	Opt_clientaddr,
5162306a36Sopenharmony_ci	Opt_cto,
5262306a36Sopenharmony_ci	Opt_fg,
5362306a36Sopenharmony_ci	Opt_fscache,
5462306a36Sopenharmony_ci	Opt_fscache_flag,
5562306a36Sopenharmony_ci	Opt_hard,
5662306a36Sopenharmony_ci	Opt_intr,
5762306a36Sopenharmony_ci	Opt_local_lock,
5862306a36Sopenharmony_ci	Opt_lock,
5962306a36Sopenharmony_ci	Opt_lookupcache,
6062306a36Sopenharmony_ci	Opt_migration,
6162306a36Sopenharmony_ci	Opt_minorversion,
6262306a36Sopenharmony_ci	Opt_mountaddr,
6362306a36Sopenharmony_ci	Opt_mounthost,
6462306a36Sopenharmony_ci	Opt_mountport,
6562306a36Sopenharmony_ci	Opt_mountproto,
6662306a36Sopenharmony_ci	Opt_mountvers,
6762306a36Sopenharmony_ci	Opt_namelen,
6862306a36Sopenharmony_ci	Opt_nconnect,
6962306a36Sopenharmony_ci	Opt_max_connect,
7062306a36Sopenharmony_ci	Opt_port,
7162306a36Sopenharmony_ci	Opt_posix,
7262306a36Sopenharmony_ci	Opt_proto,
7362306a36Sopenharmony_ci	Opt_rdirplus,
7462306a36Sopenharmony_ci	Opt_rdma,
7562306a36Sopenharmony_ci	Opt_resvport,
7662306a36Sopenharmony_ci	Opt_retrans,
7762306a36Sopenharmony_ci	Opt_retry,
7862306a36Sopenharmony_ci	Opt_rsize,
7962306a36Sopenharmony_ci	Opt_sec,
8062306a36Sopenharmony_ci	Opt_sharecache,
8162306a36Sopenharmony_ci	Opt_sloppy,
8262306a36Sopenharmony_ci	Opt_soft,
8362306a36Sopenharmony_ci	Opt_softerr,
8462306a36Sopenharmony_ci	Opt_softreval,
8562306a36Sopenharmony_ci	Opt_source,
8662306a36Sopenharmony_ci	Opt_tcp,
8762306a36Sopenharmony_ci	Opt_timeo,
8862306a36Sopenharmony_ci	Opt_trunkdiscovery,
8962306a36Sopenharmony_ci	Opt_udp,
9062306a36Sopenharmony_ci	Opt_v,
9162306a36Sopenharmony_ci	Opt_vers,
9262306a36Sopenharmony_ci	Opt_wsize,
9362306a36Sopenharmony_ci	Opt_write,
9462306a36Sopenharmony_ci	Opt_xprtsec,
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cienum {
9862306a36Sopenharmony_ci	Opt_local_lock_all,
9962306a36Sopenharmony_ci	Opt_local_lock_flock,
10062306a36Sopenharmony_ci	Opt_local_lock_none,
10162306a36Sopenharmony_ci	Opt_local_lock_posix,
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const struct constant_table nfs_param_enums_local_lock[] = {
10562306a36Sopenharmony_ci	{ "all",		Opt_local_lock_all },
10662306a36Sopenharmony_ci	{ "flock",	Opt_local_lock_flock },
10762306a36Sopenharmony_ci	{ "posix",	Opt_local_lock_posix },
10862306a36Sopenharmony_ci	{ "none",		Opt_local_lock_none },
10962306a36Sopenharmony_ci	{}
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cienum {
11362306a36Sopenharmony_ci	Opt_lookupcache_all,
11462306a36Sopenharmony_ci	Opt_lookupcache_none,
11562306a36Sopenharmony_ci	Opt_lookupcache_positive,
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic const struct constant_table nfs_param_enums_lookupcache[] = {
11962306a36Sopenharmony_ci	{ "all",		Opt_lookupcache_all },
12062306a36Sopenharmony_ci	{ "none",		Opt_lookupcache_none },
12162306a36Sopenharmony_ci	{ "pos",		Opt_lookupcache_positive },
12262306a36Sopenharmony_ci	{ "positive",		Opt_lookupcache_positive },
12362306a36Sopenharmony_ci	{}
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cienum {
12762306a36Sopenharmony_ci	Opt_write_lazy,
12862306a36Sopenharmony_ci	Opt_write_eager,
12962306a36Sopenharmony_ci	Opt_write_wait,
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic const struct constant_table nfs_param_enums_write[] = {
13362306a36Sopenharmony_ci	{ "lazy",		Opt_write_lazy },
13462306a36Sopenharmony_ci	{ "eager",		Opt_write_eager },
13562306a36Sopenharmony_ci	{ "wait",		Opt_write_wait },
13662306a36Sopenharmony_ci	{}
13762306a36Sopenharmony_ci};
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic const struct fs_parameter_spec nfs_fs_parameters[] = {
14062306a36Sopenharmony_ci	fsparam_flag_no("ac",		Opt_ac),
14162306a36Sopenharmony_ci	fsparam_u32   ("acdirmax",	Opt_acdirmax),
14262306a36Sopenharmony_ci	fsparam_u32   ("acdirmin",	Opt_acdirmin),
14362306a36Sopenharmony_ci	fsparam_flag_no("acl",		Opt_acl),
14462306a36Sopenharmony_ci	fsparam_u32   ("acregmax",	Opt_acregmax),
14562306a36Sopenharmony_ci	fsparam_u32   ("acregmin",	Opt_acregmin),
14662306a36Sopenharmony_ci	fsparam_u32   ("actimeo",	Opt_actimeo),
14762306a36Sopenharmony_ci	fsparam_string("addr",		Opt_addr),
14862306a36Sopenharmony_ci	fsparam_flag  ("bg",		Opt_bg),
14962306a36Sopenharmony_ci	fsparam_u32   ("bsize",		Opt_bsize),
15062306a36Sopenharmony_ci	fsparam_string("clientaddr",	Opt_clientaddr),
15162306a36Sopenharmony_ci	fsparam_flag_no("cto",		Opt_cto),
15262306a36Sopenharmony_ci	fsparam_flag  ("fg",		Opt_fg),
15362306a36Sopenharmony_ci	fsparam_flag_no("fsc",		Opt_fscache_flag),
15462306a36Sopenharmony_ci	fsparam_string("fsc",		Opt_fscache),
15562306a36Sopenharmony_ci	fsparam_flag  ("hard",		Opt_hard),
15662306a36Sopenharmony_ci	__fsparam(NULL, "intr",		Opt_intr,
15762306a36Sopenharmony_ci		  fs_param_neg_with_no|fs_param_deprecated, NULL),
15862306a36Sopenharmony_ci	fsparam_enum  ("local_lock",	Opt_local_lock, nfs_param_enums_local_lock),
15962306a36Sopenharmony_ci	fsparam_flag_no("lock",		Opt_lock),
16062306a36Sopenharmony_ci	fsparam_enum  ("lookupcache",	Opt_lookupcache, nfs_param_enums_lookupcache),
16162306a36Sopenharmony_ci	fsparam_flag_no("migration",	Opt_migration),
16262306a36Sopenharmony_ci	fsparam_u32   ("minorversion",	Opt_minorversion),
16362306a36Sopenharmony_ci	fsparam_string("mountaddr",	Opt_mountaddr),
16462306a36Sopenharmony_ci	fsparam_string("mounthost",	Opt_mounthost),
16562306a36Sopenharmony_ci	fsparam_u32   ("mountport",	Opt_mountport),
16662306a36Sopenharmony_ci	fsparam_string("mountproto",	Opt_mountproto),
16762306a36Sopenharmony_ci	fsparam_u32   ("mountvers",	Opt_mountvers),
16862306a36Sopenharmony_ci	fsparam_u32   ("namlen",	Opt_namelen),
16962306a36Sopenharmony_ci	fsparam_u32   ("nconnect",	Opt_nconnect),
17062306a36Sopenharmony_ci	fsparam_u32   ("max_connect",	Opt_max_connect),
17162306a36Sopenharmony_ci	fsparam_string("nfsvers",	Opt_vers),
17262306a36Sopenharmony_ci	fsparam_u32   ("port",		Opt_port),
17362306a36Sopenharmony_ci	fsparam_flag_no("posix",	Opt_posix),
17462306a36Sopenharmony_ci	fsparam_string("proto",		Opt_proto),
17562306a36Sopenharmony_ci	fsparam_flag_no("rdirplus",	Opt_rdirplus),
17662306a36Sopenharmony_ci	fsparam_flag  ("rdma",		Opt_rdma),
17762306a36Sopenharmony_ci	fsparam_flag_no("resvport",	Opt_resvport),
17862306a36Sopenharmony_ci	fsparam_u32   ("retrans",	Opt_retrans),
17962306a36Sopenharmony_ci	fsparam_string("retry",		Opt_retry),
18062306a36Sopenharmony_ci	fsparam_u32   ("rsize",		Opt_rsize),
18162306a36Sopenharmony_ci	fsparam_string("sec",		Opt_sec),
18262306a36Sopenharmony_ci	fsparam_flag_no("sharecache",	Opt_sharecache),
18362306a36Sopenharmony_ci	fsparam_flag  ("sloppy",	Opt_sloppy),
18462306a36Sopenharmony_ci	fsparam_flag  ("soft",		Opt_soft),
18562306a36Sopenharmony_ci	fsparam_flag  ("softerr",	Opt_softerr),
18662306a36Sopenharmony_ci	fsparam_flag  ("softreval",	Opt_softreval),
18762306a36Sopenharmony_ci	fsparam_string("source",	Opt_source),
18862306a36Sopenharmony_ci	fsparam_flag  ("tcp",		Opt_tcp),
18962306a36Sopenharmony_ci	fsparam_u32   ("timeo",		Opt_timeo),
19062306a36Sopenharmony_ci	fsparam_flag_no("trunkdiscovery", Opt_trunkdiscovery),
19162306a36Sopenharmony_ci	fsparam_flag  ("udp",		Opt_udp),
19262306a36Sopenharmony_ci	fsparam_flag  ("v2",		Opt_v),
19362306a36Sopenharmony_ci	fsparam_flag  ("v3",		Opt_v),
19462306a36Sopenharmony_ci	fsparam_flag  ("v4",		Opt_v),
19562306a36Sopenharmony_ci	fsparam_flag  ("v4.0",		Opt_v),
19662306a36Sopenharmony_ci	fsparam_flag  ("v4.1",		Opt_v),
19762306a36Sopenharmony_ci	fsparam_flag  ("v4.2",		Opt_v),
19862306a36Sopenharmony_ci	fsparam_string("vers",		Opt_vers),
19962306a36Sopenharmony_ci	fsparam_enum  ("write",		Opt_write, nfs_param_enums_write),
20062306a36Sopenharmony_ci	fsparam_u32   ("wsize",		Opt_wsize),
20162306a36Sopenharmony_ci	fsparam_string("xprtsec",	Opt_xprtsec),
20262306a36Sopenharmony_ci	{}
20362306a36Sopenharmony_ci};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cienum {
20662306a36Sopenharmony_ci	Opt_vers_2,
20762306a36Sopenharmony_ci	Opt_vers_3,
20862306a36Sopenharmony_ci	Opt_vers_4,
20962306a36Sopenharmony_ci	Opt_vers_4_0,
21062306a36Sopenharmony_ci	Opt_vers_4_1,
21162306a36Sopenharmony_ci	Opt_vers_4_2,
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic const struct constant_table nfs_vers_tokens[] = {
21562306a36Sopenharmony_ci	{ "2",		Opt_vers_2 },
21662306a36Sopenharmony_ci	{ "3",		Opt_vers_3 },
21762306a36Sopenharmony_ci	{ "4",		Opt_vers_4 },
21862306a36Sopenharmony_ci	{ "4.0",	Opt_vers_4_0 },
21962306a36Sopenharmony_ci	{ "4.1",	Opt_vers_4_1 },
22062306a36Sopenharmony_ci	{ "4.2",	Opt_vers_4_2 },
22162306a36Sopenharmony_ci	{}
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cienum {
22562306a36Sopenharmony_ci	Opt_xprt_rdma,
22662306a36Sopenharmony_ci	Opt_xprt_rdma6,
22762306a36Sopenharmony_ci	Opt_xprt_tcp,
22862306a36Sopenharmony_ci	Opt_xprt_tcp6,
22962306a36Sopenharmony_ci	Opt_xprt_udp,
23062306a36Sopenharmony_ci	Opt_xprt_udp6,
23162306a36Sopenharmony_ci	nr__Opt_xprt
23262306a36Sopenharmony_ci};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic const struct constant_table nfs_xprt_protocol_tokens[] = {
23562306a36Sopenharmony_ci	{ "rdma",	Opt_xprt_rdma },
23662306a36Sopenharmony_ci	{ "rdma6",	Opt_xprt_rdma6 },
23762306a36Sopenharmony_ci	{ "tcp",	Opt_xprt_tcp },
23862306a36Sopenharmony_ci	{ "tcp6",	Opt_xprt_tcp6 },
23962306a36Sopenharmony_ci	{ "udp",	Opt_xprt_udp },
24062306a36Sopenharmony_ci	{ "udp6",	Opt_xprt_udp6 },
24162306a36Sopenharmony_ci	{}
24262306a36Sopenharmony_ci};
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cienum {
24562306a36Sopenharmony_ci	Opt_sec_krb5,
24662306a36Sopenharmony_ci	Opt_sec_krb5i,
24762306a36Sopenharmony_ci	Opt_sec_krb5p,
24862306a36Sopenharmony_ci	Opt_sec_lkey,
24962306a36Sopenharmony_ci	Opt_sec_lkeyi,
25062306a36Sopenharmony_ci	Opt_sec_lkeyp,
25162306a36Sopenharmony_ci	Opt_sec_none,
25262306a36Sopenharmony_ci	Opt_sec_spkm,
25362306a36Sopenharmony_ci	Opt_sec_spkmi,
25462306a36Sopenharmony_ci	Opt_sec_spkmp,
25562306a36Sopenharmony_ci	Opt_sec_sys,
25662306a36Sopenharmony_ci	nr__Opt_sec
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic const struct constant_table nfs_secflavor_tokens[] = {
26062306a36Sopenharmony_ci	{ "krb5",	Opt_sec_krb5 },
26162306a36Sopenharmony_ci	{ "krb5i",	Opt_sec_krb5i },
26262306a36Sopenharmony_ci	{ "krb5p",	Opt_sec_krb5p },
26362306a36Sopenharmony_ci	{ "lkey",	Opt_sec_lkey },
26462306a36Sopenharmony_ci	{ "lkeyi",	Opt_sec_lkeyi },
26562306a36Sopenharmony_ci	{ "lkeyp",	Opt_sec_lkeyp },
26662306a36Sopenharmony_ci	{ "none",	Opt_sec_none },
26762306a36Sopenharmony_ci	{ "null",	Opt_sec_none },
26862306a36Sopenharmony_ci	{ "spkm3",	Opt_sec_spkm },
26962306a36Sopenharmony_ci	{ "spkm3i",	Opt_sec_spkmi },
27062306a36Sopenharmony_ci	{ "spkm3p",	Opt_sec_spkmp },
27162306a36Sopenharmony_ci	{ "sys",	Opt_sec_sys },
27262306a36Sopenharmony_ci	{}
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cienum {
27662306a36Sopenharmony_ci	Opt_xprtsec_none,
27762306a36Sopenharmony_ci	Opt_xprtsec_tls,
27862306a36Sopenharmony_ci	Opt_xprtsec_mtls,
27962306a36Sopenharmony_ci	nr__Opt_xprtsec
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const struct constant_table nfs_xprtsec_policies[] = {
28362306a36Sopenharmony_ci	{ "none",	Opt_xprtsec_none },
28462306a36Sopenharmony_ci	{ "tls",	Opt_xprtsec_tls },
28562306a36Sopenharmony_ci	{ "mtls",	Opt_xprtsec_mtls },
28662306a36Sopenharmony_ci	{}
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/*
29062306a36Sopenharmony_ci * Sanity-check a server address provided by the mount command.
29162306a36Sopenharmony_ci *
29262306a36Sopenharmony_ci * Address family must be initialized, and address must not be
29362306a36Sopenharmony_ci * the ANY address for that family.
29462306a36Sopenharmony_ci */
29562306a36Sopenharmony_cistatic int nfs_verify_server_address(struct sockaddr_storage *addr)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	switch (addr->ss_family) {
29862306a36Sopenharmony_ci	case AF_INET: {
29962306a36Sopenharmony_ci		struct sockaddr_in *sa = (struct sockaddr_in *)addr;
30062306a36Sopenharmony_ci		return sa->sin_addr.s_addr != htonl(INADDR_ANY);
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci	case AF_INET6: {
30362306a36Sopenharmony_ci		struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
30462306a36Sopenharmony_ci		return !ipv6_addr_any(sa);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return 0;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci#ifdef CONFIG_NFS_DISABLE_UDP_SUPPORT
31262306a36Sopenharmony_cistatic bool nfs_server_transport_udp_invalid(const struct nfs_fs_context *ctx)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	return true;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci#else
31762306a36Sopenharmony_cistatic bool nfs_server_transport_udp_invalid(const struct nfs_fs_context *ctx)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	if (ctx->version == 4)
32062306a36Sopenharmony_ci		return true;
32162306a36Sopenharmony_ci	return false;
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ci#endif
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * Sanity check the NFS transport protocol.
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_cistatic int nfs_validate_transport_protocol(struct fs_context *fc,
32962306a36Sopenharmony_ci					   struct nfs_fs_context *ctx)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	switch (ctx->nfs_server.protocol) {
33262306a36Sopenharmony_ci	case XPRT_TRANSPORT_UDP:
33362306a36Sopenharmony_ci		if (nfs_server_transport_udp_invalid(ctx))
33462306a36Sopenharmony_ci			goto out_invalid_transport_udp;
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP:
33762306a36Sopenharmony_ci	case XPRT_TRANSPORT_RDMA:
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	default:
34062306a36Sopenharmony_ci		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (ctx->xprtsec.policy != RPC_XPRTSEC_NONE)
34462306a36Sopenharmony_ci		switch (ctx->nfs_server.protocol) {
34562306a36Sopenharmony_ci		case XPRT_TRANSPORT_TCP:
34662306a36Sopenharmony_ci			ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP_TLS;
34762306a36Sopenharmony_ci			break;
34862306a36Sopenharmony_ci		default:
34962306a36Sopenharmony_ci			goto out_invalid_xprtsec_policy;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ciout_invalid_transport_udp:
35462306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Unsupported transport protocol udp");
35562306a36Sopenharmony_ciout_invalid_xprtsec_policy:
35662306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Transport does not support xprtsec");
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/*
36062306a36Sopenharmony_ci * For text based NFSv2/v3 mounts, the mount protocol transport default
36162306a36Sopenharmony_ci * settings should depend upon the specified NFS transport.
36262306a36Sopenharmony_ci */
36362306a36Sopenharmony_cistatic void nfs_set_mount_transport_protocol(struct nfs_fs_context *ctx)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	if (ctx->mount_server.protocol == XPRT_TRANSPORT_UDP ||
36662306a36Sopenharmony_ci	    ctx->mount_server.protocol == XPRT_TRANSPORT_TCP)
36762306a36Sopenharmony_ci			return;
36862306a36Sopenharmony_ci	switch (ctx->nfs_server.protocol) {
36962306a36Sopenharmony_ci	case XPRT_TRANSPORT_UDP:
37062306a36Sopenharmony_ci		ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
37162306a36Sopenharmony_ci		break;
37262306a36Sopenharmony_ci	case XPRT_TRANSPORT_TCP:
37362306a36Sopenharmony_ci	case XPRT_TRANSPORT_RDMA:
37462306a36Sopenharmony_ci		ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*
37962306a36Sopenharmony_ci * Add 'flavor' to 'auth_info' if not already present.
38062306a36Sopenharmony_ci * Returns true if 'flavor' ends up in the list, false otherwise
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_cistatic int nfs_auth_info_add(struct fs_context *fc,
38362306a36Sopenharmony_ci			     struct nfs_auth_info *auth_info,
38462306a36Sopenharmony_ci			     rpc_authflavor_t flavor)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	unsigned int i;
38762306a36Sopenharmony_ci	unsigned int max_flavor_len = ARRAY_SIZE(auth_info->flavors);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* make sure this flavor isn't already in the list */
39062306a36Sopenharmony_ci	for (i = 0; i < auth_info->flavor_len; i++) {
39162306a36Sopenharmony_ci		if (flavor == auth_info->flavors[i])
39262306a36Sopenharmony_ci			return 0;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (auth_info->flavor_len + 1 >= max_flavor_len)
39662306a36Sopenharmony_ci		return nfs_invalf(fc, "NFS: too many sec= flavors");
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	auth_info->flavors[auth_info->flavor_len++] = flavor;
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * Parse the value of the 'sec=' option.
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_cistatic int nfs_parse_security_flavors(struct fs_context *fc,
40662306a36Sopenharmony_ci				      struct fs_parameter *param)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
40962306a36Sopenharmony_ci	rpc_authflavor_t pseudoflavor;
41062306a36Sopenharmony_ci	char *string = param->string, *p;
41162306a36Sopenharmony_ci	int ret;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	trace_nfs_mount_assign(param->key, string);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	while ((p = strsep(&string, ":")) != NULL) {
41662306a36Sopenharmony_ci		if (!*p)
41762306a36Sopenharmony_ci			continue;
41862306a36Sopenharmony_ci		switch (lookup_constant(nfs_secflavor_tokens, p, -1)) {
41962306a36Sopenharmony_ci		case Opt_sec_none:
42062306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_NULL;
42162306a36Sopenharmony_ci			break;
42262306a36Sopenharmony_ci		case Opt_sec_sys:
42362306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_UNIX;
42462306a36Sopenharmony_ci			break;
42562306a36Sopenharmony_ci		case Opt_sec_krb5:
42662306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_KRB5;
42762306a36Sopenharmony_ci			break;
42862306a36Sopenharmony_ci		case Opt_sec_krb5i:
42962306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_KRB5I;
43062306a36Sopenharmony_ci			break;
43162306a36Sopenharmony_ci		case Opt_sec_krb5p:
43262306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_KRB5P;
43362306a36Sopenharmony_ci			break;
43462306a36Sopenharmony_ci		case Opt_sec_lkey:
43562306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_LKEY;
43662306a36Sopenharmony_ci			break;
43762306a36Sopenharmony_ci		case Opt_sec_lkeyi:
43862306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_LKEYI;
43962306a36Sopenharmony_ci			break;
44062306a36Sopenharmony_ci		case Opt_sec_lkeyp:
44162306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_LKEYP;
44262306a36Sopenharmony_ci			break;
44362306a36Sopenharmony_ci		case Opt_sec_spkm:
44462306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_SPKM;
44562306a36Sopenharmony_ci			break;
44662306a36Sopenharmony_ci		case Opt_sec_spkmi:
44762306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_SPKMI;
44862306a36Sopenharmony_ci			break;
44962306a36Sopenharmony_ci		case Opt_sec_spkmp:
45062306a36Sopenharmony_ci			pseudoflavor = RPC_AUTH_GSS_SPKMP;
45162306a36Sopenharmony_ci			break;
45262306a36Sopenharmony_ci		default:
45362306a36Sopenharmony_ci			return nfs_invalf(fc, "NFS: sec=%s option not recognized", p);
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		ret = nfs_auth_info_add(fc, &ctx->auth_info, pseudoflavor);
45762306a36Sopenharmony_ci		if (ret < 0)
45862306a36Sopenharmony_ci			return ret;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int nfs_parse_xprtsec_policy(struct fs_context *fc,
46562306a36Sopenharmony_ci				    struct fs_parameter *param)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	trace_nfs_mount_assign(param->key, param->string);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	switch (lookup_constant(nfs_xprtsec_policies, param->string, -1)) {
47262306a36Sopenharmony_ci	case Opt_xprtsec_none:
47362306a36Sopenharmony_ci		ctx->xprtsec.policy = RPC_XPRTSEC_NONE;
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	case Opt_xprtsec_tls:
47662306a36Sopenharmony_ci		ctx->xprtsec.policy = RPC_XPRTSEC_TLS_ANON;
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	case Opt_xprtsec_mtls:
47962306a36Sopenharmony_ci		ctx->xprtsec.policy = RPC_XPRTSEC_TLS_X509;
48062306a36Sopenharmony_ci		break;
48162306a36Sopenharmony_ci	default:
48262306a36Sopenharmony_ci		return nfs_invalf(fc, "NFS: Unrecognized transport security policy");
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic int nfs_parse_version_string(struct fs_context *fc,
48862306a36Sopenharmony_ci				    const char *string)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ctx->flags &= ~NFS_MOUNT_VER3;
49362306a36Sopenharmony_ci	switch (lookup_constant(nfs_vers_tokens, string, -1)) {
49462306a36Sopenharmony_ci	case Opt_vers_2:
49562306a36Sopenharmony_ci		ctx->version = 2;
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	case Opt_vers_3:
49862306a36Sopenharmony_ci		ctx->flags |= NFS_MOUNT_VER3;
49962306a36Sopenharmony_ci		ctx->version = 3;
50062306a36Sopenharmony_ci		break;
50162306a36Sopenharmony_ci	case Opt_vers_4:
50262306a36Sopenharmony_ci		/* Backward compatibility option. In future,
50362306a36Sopenharmony_ci		 * the mount program should always supply
50462306a36Sopenharmony_ci		 * a NFSv4 minor version number.
50562306a36Sopenharmony_ci		 */
50662306a36Sopenharmony_ci		ctx->version = 4;
50762306a36Sopenharmony_ci		break;
50862306a36Sopenharmony_ci	case Opt_vers_4_0:
50962306a36Sopenharmony_ci		ctx->version = 4;
51062306a36Sopenharmony_ci		ctx->minorversion = 0;
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	case Opt_vers_4_1:
51362306a36Sopenharmony_ci		ctx->version = 4;
51462306a36Sopenharmony_ci		ctx->minorversion = 1;
51562306a36Sopenharmony_ci		break;
51662306a36Sopenharmony_ci	case Opt_vers_4_2:
51762306a36Sopenharmony_ci		ctx->version = 4;
51862306a36Sopenharmony_ci		ctx->minorversion = 2;
51962306a36Sopenharmony_ci		break;
52062306a36Sopenharmony_ci	default:
52162306a36Sopenharmony_ci		return nfs_invalf(fc, "NFS: Unsupported NFS version");
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	return 0;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci/*
52762306a36Sopenharmony_ci * Parse a single mount parameter.
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_cistatic int nfs_fs_context_parse_param(struct fs_context *fc,
53062306a36Sopenharmony_ci				      struct fs_parameter *param)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct fs_parse_result result;
53362306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
53462306a36Sopenharmony_ci	unsigned short protofamily, mountfamily;
53562306a36Sopenharmony_ci	unsigned int len;
53662306a36Sopenharmony_ci	int ret, opt;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	trace_nfs_mount_option(param);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	opt = fs_parse(fc, nfs_fs_parameters, param, &result);
54162306a36Sopenharmony_ci	if (opt < 0)
54262306a36Sopenharmony_ci		return (opt == -ENOPARAM && ctx->sloppy) ? 1 : opt;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (fc->security)
54562306a36Sopenharmony_ci		ctx->has_sec_mnt_opts = 1;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	switch (opt) {
54862306a36Sopenharmony_ci	case Opt_source:
54962306a36Sopenharmony_ci		if (fc->source)
55062306a36Sopenharmony_ci			return nfs_invalf(fc, "NFS: Multiple sources not supported");
55162306a36Sopenharmony_ci		fc->source = param->string;
55262306a36Sopenharmony_ci		param->string = NULL;
55362306a36Sopenharmony_ci		break;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		/*
55662306a36Sopenharmony_ci		 * boolean options:  foo/nofoo
55762306a36Sopenharmony_ci		 */
55862306a36Sopenharmony_ci	case Opt_soft:
55962306a36Sopenharmony_ci		ctx->flags |= NFS_MOUNT_SOFT;
56062306a36Sopenharmony_ci		ctx->flags &= ~NFS_MOUNT_SOFTERR;
56162306a36Sopenharmony_ci		break;
56262306a36Sopenharmony_ci	case Opt_softerr:
56362306a36Sopenharmony_ci		ctx->flags |= NFS_MOUNT_SOFTERR | NFS_MOUNT_SOFTREVAL;
56462306a36Sopenharmony_ci		ctx->flags &= ~NFS_MOUNT_SOFT;
56562306a36Sopenharmony_ci		break;
56662306a36Sopenharmony_ci	case Opt_hard:
56762306a36Sopenharmony_ci		ctx->flags &= ~(NFS_MOUNT_SOFT |
56862306a36Sopenharmony_ci				NFS_MOUNT_SOFTERR |
56962306a36Sopenharmony_ci				NFS_MOUNT_SOFTREVAL);
57062306a36Sopenharmony_ci		break;
57162306a36Sopenharmony_ci	case Opt_softreval:
57262306a36Sopenharmony_ci		if (result.negated)
57362306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_SOFTREVAL;
57462306a36Sopenharmony_ci		else
57562306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_SOFTREVAL;
57662306a36Sopenharmony_ci		break;
57762306a36Sopenharmony_ci	case Opt_posix:
57862306a36Sopenharmony_ci		if (result.negated)
57962306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_POSIX;
58062306a36Sopenharmony_ci		else
58162306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_POSIX;
58262306a36Sopenharmony_ci		break;
58362306a36Sopenharmony_ci	case Opt_cto:
58462306a36Sopenharmony_ci		if (result.negated)
58562306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_NOCTO;
58662306a36Sopenharmony_ci		else
58762306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_NOCTO;
58862306a36Sopenharmony_ci		break;
58962306a36Sopenharmony_ci	case Opt_trunkdiscovery:
59062306a36Sopenharmony_ci		if (result.negated)
59162306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_TRUNK_DISCOVERY;
59262306a36Sopenharmony_ci		else
59362306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_TRUNK_DISCOVERY;
59462306a36Sopenharmony_ci		break;
59562306a36Sopenharmony_ci	case Opt_ac:
59662306a36Sopenharmony_ci		if (result.negated)
59762306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_NOAC;
59862306a36Sopenharmony_ci		else
59962306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_NOAC;
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci	case Opt_lock:
60262306a36Sopenharmony_ci		if (result.negated) {
60362306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_NONLM;
60462306a36Sopenharmony_ci			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
60562306a36Sopenharmony_ci		} else {
60662306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_NONLM;
60762306a36Sopenharmony_ci			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
60862306a36Sopenharmony_ci		}
60962306a36Sopenharmony_ci		break;
61062306a36Sopenharmony_ci	case Opt_udp:
61162306a36Sopenharmony_ci		ctx->flags &= ~NFS_MOUNT_TCP;
61262306a36Sopenharmony_ci		ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
61362306a36Sopenharmony_ci		break;
61462306a36Sopenharmony_ci	case Opt_tcp:
61562306a36Sopenharmony_ci	case Opt_rdma:
61662306a36Sopenharmony_ci		ctx->flags |= NFS_MOUNT_TCP; /* for side protocols */
61762306a36Sopenharmony_ci		ret = xprt_find_transport_ident(param->key);
61862306a36Sopenharmony_ci		if (ret < 0)
61962306a36Sopenharmony_ci			goto out_bad_transport;
62062306a36Sopenharmony_ci		ctx->nfs_server.protocol = ret;
62162306a36Sopenharmony_ci		break;
62262306a36Sopenharmony_ci	case Opt_acl:
62362306a36Sopenharmony_ci		if (result.negated)
62462306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_NOACL;
62562306a36Sopenharmony_ci		else
62662306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_NOACL;
62762306a36Sopenharmony_ci		break;
62862306a36Sopenharmony_ci	case Opt_rdirplus:
62962306a36Sopenharmony_ci		if (result.negated)
63062306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_NORDIRPLUS;
63162306a36Sopenharmony_ci		else
63262306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_NORDIRPLUS;
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	case Opt_sharecache:
63562306a36Sopenharmony_ci		if (result.negated)
63662306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_UNSHARED;
63762306a36Sopenharmony_ci		else
63862306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_UNSHARED;
63962306a36Sopenharmony_ci		break;
64062306a36Sopenharmony_ci	case Opt_resvport:
64162306a36Sopenharmony_ci		if (result.negated)
64262306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_NORESVPORT;
64362306a36Sopenharmony_ci		else
64462306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_NORESVPORT;
64562306a36Sopenharmony_ci		break;
64662306a36Sopenharmony_ci	case Opt_fscache_flag:
64762306a36Sopenharmony_ci		if (result.negated)
64862306a36Sopenharmony_ci			ctx->options &= ~NFS_OPTION_FSCACHE;
64962306a36Sopenharmony_ci		else
65062306a36Sopenharmony_ci			ctx->options |= NFS_OPTION_FSCACHE;
65162306a36Sopenharmony_ci		kfree(ctx->fscache_uniq);
65262306a36Sopenharmony_ci		ctx->fscache_uniq = NULL;
65362306a36Sopenharmony_ci		break;
65462306a36Sopenharmony_ci	case Opt_fscache:
65562306a36Sopenharmony_ci		ctx->options |= NFS_OPTION_FSCACHE;
65662306a36Sopenharmony_ci		kfree(ctx->fscache_uniq);
65762306a36Sopenharmony_ci		ctx->fscache_uniq = param->string;
65862306a36Sopenharmony_ci		param->string = NULL;
65962306a36Sopenharmony_ci		break;
66062306a36Sopenharmony_ci	case Opt_migration:
66162306a36Sopenharmony_ci		if (result.negated)
66262306a36Sopenharmony_ci			ctx->options &= ~NFS_OPTION_MIGRATION;
66362306a36Sopenharmony_ci		else
66462306a36Sopenharmony_ci			ctx->options |= NFS_OPTION_MIGRATION;
66562306a36Sopenharmony_ci		break;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		/*
66862306a36Sopenharmony_ci		 * options that take numeric values
66962306a36Sopenharmony_ci		 */
67062306a36Sopenharmony_ci	case Opt_port:
67162306a36Sopenharmony_ci		if (result.uint_32 > USHRT_MAX)
67262306a36Sopenharmony_ci			goto out_of_bounds;
67362306a36Sopenharmony_ci		ctx->nfs_server.port = result.uint_32;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	case Opt_rsize:
67662306a36Sopenharmony_ci		ctx->rsize = result.uint_32;
67762306a36Sopenharmony_ci		break;
67862306a36Sopenharmony_ci	case Opt_wsize:
67962306a36Sopenharmony_ci		ctx->wsize = result.uint_32;
68062306a36Sopenharmony_ci		break;
68162306a36Sopenharmony_ci	case Opt_bsize:
68262306a36Sopenharmony_ci		ctx->bsize = result.uint_32;
68362306a36Sopenharmony_ci		break;
68462306a36Sopenharmony_ci	case Opt_timeo:
68562306a36Sopenharmony_ci		if (result.uint_32 < 1 || result.uint_32 > INT_MAX)
68662306a36Sopenharmony_ci			goto out_of_bounds;
68762306a36Sopenharmony_ci		ctx->timeo = result.uint_32;
68862306a36Sopenharmony_ci		break;
68962306a36Sopenharmony_ci	case Opt_retrans:
69062306a36Sopenharmony_ci		if (result.uint_32 > INT_MAX)
69162306a36Sopenharmony_ci			goto out_of_bounds;
69262306a36Sopenharmony_ci		ctx->retrans = result.uint_32;
69362306a36Sopenharmony_ci		break;
69462306a36Sopenharmony_ci	case Opt_acregmin:
69562306a36Sopenharmony_ci		ctx->acregmin = result.uint_32;
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci	case Opt_acregmax:
69862306a36Sopenharmony_ci		ctx->acregmax = result.uint_32;
69962306a36Sopenharmony_ci		break;
70062306a36Sopenharmony_ci	case Opt_acdirmin:
70162306a36Sopenharmony_ci		ctx->acdirmin = result.uint_32;
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	case Opt_acdirmax:
70462306a36Sopenharmony_ci		ctx->acdirmax = result.uint_32;
70562306a36Sopenharmony_ci		break;
70662306a36Sopenharmony_ci	case Opt_actimeo:
70762306a36Sopenharmony_ci		ctx->acregmin = result.uint_32;
70862306a36Sopenharmony_ci		ctx->acregmax = result.uint_32;
70962306a36Sopenharmony_ci		ctx->acdirmin = result.uint_32;
71062306a36Sopenharmony_ci		ctx->acdirmax = result.uint_32;
71162306a36Sopenharmony_ci		break;
71262306a36Sopenharmony_ci	case Opt_namelen:
71362306a36Sopenharmony_ci		ctx->namlen = result.uint_32;
71462306a36Sopenharmony_ci		break;
71562306a36Sopenharmony_ci	case Opt_mountport:
71662306a36Sopenharmony_ci		if (result.uint_32 > USHRT_MAX)
71762306a36Sopenharmony_ci			goto out_of_bounds;
71862306a36Sopenharmony_ci		ctx->mount_server.port = result.uint_32;
71962306a36Sopenharmony_ci		break;
72062306a36Sopenharmony_ci	case Opt_mountvers:
72162306a36Sopenharmony_ci		if (result.uint_32 < NFS_MNT_VERSION ||
72262306a36Sopenharmony_ci		    result.uint_32 > NFS_MNT3_VERSION)
72362306a36Sopenharmony_ci			goto out_of_bounds;
72462306a36Sopenharmony_ci		ctx->mount_server.version = result.uint_32;
72562306a36Sopenharmony_ci		break;
72662306a36Sopenharmony_ci	case Opt_minorversion:
72762306a36Sopenharmony_ci		if (result.uint_32 > NFS4_MAX_MINOR_VERSION)
72862306a36Sopenharmony_ci			goto out_of_bounds;
72962306a36Sopenharmony_ci		ctx->minorversion = result.uint_32;
73062306a36Sopenharmony_ci		break;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		/*
73362306a36Sopenharmony_ci		 * options that take text values
73462306a36Sopenharmony_ci		 */
73562306a36Sopenharmony_ci	case Opt_v:
73662306a36Sopenharmony_ci		ret = nfs_parse_version_string(fc, param->key + 1);
73762306a36Sopenharmony_ci		if (ret < 0)
73862306a36Sopenharmony_ci			return ret;
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci	case Opt_vers:
74162306a36Sopenharmony_ci		if (!param->string)
74262306a36Sopenharmony_ci			goto out_invalid_value;
74362306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
74462306a36Sopenharmony_ci		ret = nfs_parse_version_string(fc, param->string);
74562306a36Sopenharmony_ci		if (ret < 0)
74662306a36Sopenharmony_ci			return ret;
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci	case Opt_sec:
74962306a36Sopenharmony_ci		ret = nfs_parse_security_flavors(fc, param);
75062306a36Sopenharmony_ci		if (ret < 0)
75162306a36Sopenharmony_ci			return ret;
75262306a36Sopenharmony_ci		break;
75362306a36Sopenharmony_ci	case Opt_xprtsec:
75462306a36Sopenharmony_ci		ret = nfs_parse_xprtsec_policy(fc, param);
75562306a36Sopenharmony_ci		if (ret < 0)
75662306a36Sopenharmony_ci			return ret;
75762306a36Sopenharmony_ci		break;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	case Opt_proto:
76062306a36Sopenharmony_ci		if (!param->string)
76162306a36Sopenharmony_ci			goto out_invalid_value;
76262306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
76362306a36Sopenharmony_ci		protofamily = AF_INET;
76462306a36Sopenharmony_ci		switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
76562306a36Sopenharmony_ci		case Opt_xprt_udp6:
76662306a36Sopenharmony_ci			protofamily = AF_INET6;
76762306a36Sopenharmony_ci			fallthrough;
76862306a36Sopenharmony_ci		case Opt_xprt_udp:
76962306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_TCP;
77062306a36Sopenharmony_ci			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
77162306a36Sopenharmony_ci			break;
77262306a36Sopenharmony_ci		case Opt_xprt_tcp6:
77362306a36Sopenharmony_ci			protofamily = AF_INET6;
77462306a36Sopenharmony_ci			fallthrough;
77562306a36Sopenharmony_ci		case Opt_xprt_tcp:
77662306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_TCP;
77762306a36Sopenharmony_ci			ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
77862306a36Sopenharmony_ci			break;
77962306a36Sopenharmony_ci		case Opt_xprt_rdma6:
78062306a36Sopenharmony_ci			protofamily = AF_INET6;
78162306a36Sopenharmony_ci			fallthrough;
78262306a36Sopenharmony_ci		case Opt_xprt_rdma:
78362306a36Sopenharmony_ci			/* vector side protocols to TCP */
78462306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_TCP;
78562306a36Sopenharmony_ci			ret = xprt_find_transport_ident(param->string);
78662306a36Sopenharmony_ci			if (ret < 0)
78762306a36Sopenharmony_ci				goto out_bad_transport;
78862306a36Sopenharmony_ci			ctx->nfs_server.protocol = ret;
78962306a36Sopenharmony_ci			break;
79062306a36Sopenharmony_ci		default:
79162306a36Sopenharmony_ci			goto out_bad_transport;
79262306a36Sopenharmony_ci		}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		ctx->protofamily = protofamily;
79562306a36Sopenharmony_ci		break;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	case Opt_mountproto:
79862306a36Sopenharmony_ci		if (!param->string)
79962306a36Sopenharmony_ci			goto out_invalid_value;
80062306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
80162306a36Sopenharmony_ci		mountfamily = AF_INET;
80262306a36Sopenharmony_ci		switch (lookup_constant(nfs_xprt_protocol_tokens, param->string, -1)) {
80362306a36Sopenharmony_ci		case Opt_xprt_udp6:
80462306a36Sopenharmony_ci			mountfamily = AF_INET6;
80562306a36Sopenharmony_ci			fallthrough;
80662306a36Sopenharmony_ci		case Opt_xprt_udp:
80762306a36Sopenharmony_ci			ctx->mount_server.protocol = XPRT_TRANSPORT_UDP;
80862306a36Sopenharmony_ci			break;
80962306a36Sopenharmony_ci		case Opt_xprt_tcp6:
81062306a36Sopenharmony_ci			mountfamily = AF_INET6;
81162306a36Sopenharmony_ci			fallthrough;
81262306a36Sopenharmony_ci		case Opt_xprt_tcp:
81362306a36Sopenharmony_ci			ctx->mount_server.protocol = XPRT_TRANSPORT_TCP;
81462306a36Sopenharmony_ci			break;
81562306a36Sopenharmony_ci		case Opt_xprt_rdma: /* not used for side protocols */
81662306a36Sopenharmony_ci		default:
81762306a36Sopenharmony_ci			goto out_bad_transport;
81862306a36Sopenharmony_ci		}
81962306a36Sopenharmony_ci		ctx->mountfamily = mountfamily;
82062306a36Sopenharmony_ci		break;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	case Opt_addr:
82362306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
82462306a36Sopenharmony_ci		len = rpc_pton(fc->net_ns, param->string, param->size,
82562306a36Sopenharmony_ci			       &ctx->nfs_server.address,
82662306a36Sopenharmony_ci			       sizeof(ctx->nfs_server._address));
82762306a36Sopenharmony_ci		if (len == 0)
82862306a36Sopenharmony_ci			goto out_invalid_address;
82962306a36Sopenharmony_ci		ctx->nfs_server.addrlen = len;
83062306a36Sopenharmony_ci		break;
83162306a36Sopenharmony_ci	case Opt_clientaddr:
83262306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
83362306a36Sopenharmony_ci		kfree(ctx->client_address);
83462306a36Sopenharmony_ci		ctx->client_address = param->string;
83562306a36Sopenharmony_ci		param->string = NULL;
83662306a36Sopenharmony_ci		break;
83762306a36Sopenharmony_ci	case Opt_mounthost:
83862306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
83962306a36Sopenharmony_ci		kfree(ctx->mount_server.hostname);
84062306a36Sopenharmony_ci		ctx->mount_server.hostname = param->string;
84162306a36Sopenharmony_ci		param->string = NULL;
84262306a36Sopenharmony_ci		break;
84362306a36Sopenharmony_ci	case Opt_mountaddr:
84462306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
84562306a36Sopenharmony_ci		len = rpc_pton(fc->net_ns, param->string, param->size,
84662306a36Sopenharmony_ci			       &ctx->mount_server.address,
84762306a36Sopenharmony_ci			       sizeof(ctx->mount_server._address));
84862306a36Sopenharmony_ci		if (len == 0)
84962306a36Sopenharmony_ci			goto out_invalid_address;
85062306a36Sopenharmony_ci		ctx->mount_server.addrlen = len;
85162306a36Sopenharmony_ci		break;
85262306a36Sopenharmony_ci	case Opt_nconnect:
85362306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
85462306a36Sopenharmony_ci		if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_CONNECTIONS)
85562306a36Sopenharmony_ci			goto out_of_bounds;
85662306a36Sopenharmony_ci		ctx->nfs_server.nconnect = result.uint_32;
85762306a36Sopenharmony_ci		break;
85862306a36Sopenharmony_ci	case Opt_max_connect:
85962306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
86062306a36Sopenharmony_ci		if (result.uint_32 < 1 || result.uint_32 > NFS_MAX_TRANSPORTS)
86162306a36Sopenharmony_ci			goto out_of_bounds;
86262306a36Sopenharmony_ci		ctx->nfs_server.max_connect = result.uint_32;
86362306a36Sopenharmony_ci		break;
86462306a36Sopenharmony_ci	case Opt_lookupcache:
86562306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
86662306a36Sopenharmony_ci		switch (result.uint_32) {
86762306a36Sopenharmony_ci		case Opt_lookupcache_all:
86862306a36Sopenharmony_ci			ctx->flags &= ~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
86962306a36Sopenharmony_ci			break;
87062306a36Sopenharmony_ci		case Opt_lookupcache_positive:
87162306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
87262306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
87362306a36Sopenharmony_ci			break;
87462306a36Sopenharmony_ci		case Opt_lookupcache_none:
87562306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
87662306a36Sopenharmony_ci			break;
87762306a36Sopenharmony_ci		default:
87862306a36Sopenharmony_ci			goto out_invalid_value;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci		break;
88162306a36Sopenharmony_ci	case Opt_local_lock:
88262306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
88362306a36Sopenharmony_ci		switch (result.uint_32) {
88462306a36Sopenharmony_ci		case Opt_local_lock_all:
88562306a36Sopenharmony_ci			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK |
88662306a36Sopenharmony_ci				       NFS_MOUNT_LOCAL_FCNTL);
88762306a36Sopenharmony_ci			break;
88862306a36Sopenharmony_ci		case Opt_local_lock_flock:
88962306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_LOCAL_FLOCK;
89062306a36Sopenharmony_ci			break;
89162306a36Sopenharmony_ci		case Opt_local_lock_posix:
89262306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_LOCAL_FCNTL;
89362306a36Sopenharmony_ci			break;
89462306a36Sopenharmony_ci		case Opt_local_lock_none:
89562306a36Sopenharmony_ci			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
89662306a36Sopenharmony_ci					NFS_MOUNT_LOCAL_FCNTL);
89762306a36Sopenharmony_ci			break;
89862306a36Sopenharmony_ci		default:
89962306a36Sopenharmony_ci			goto out_invalid_value;
90062306a36Sopenharmony_ci		}
90162306a36Sopenharmony_ci		break;
90262306a36Sopenharmony_ci	case Opt_write:
90362306a36Sopenharmony_ci		trace_nfs_mount_assign(param->key, param->string);
90462306a36Sopenharmony_ci		switch (result.uint_32) {
90562306a36Sopenharmony_ci		case Opt_write_lazy:
90662306a36Sopenharmony_ci			ctx->flags &=
90762306a36Sopenharmony_ci				~(NFS_MOUNT_WRITE_EAGER | NFS_MOUNT_WRITE_WAIT);
90862306a36Sopenharmony_ci			break;
90962306a36Sopenharmony_ci		case Opt_write_eager:
91062306a36Sopenharmony_ci			ctx->flags |= NFS_MOUNT_WRITE_EAGER;
91162306a36Sopenharmony_ci			ctx->flags &= ~NFS_MOUNT_WRITE_WAIT;
91262306a36Sopenharmony_ci			break;
91362306a36Sopenharmony_ci		case Opt_write_wait:
91462306a36Sopenharmony_ci			ctx->flags |=
91562306a36Sopenharmony_ci				NFS_MOUNT_WRITE_EAGER | NFS_MOUNT_WRITE_WAIT;
91662306a36Sopenharmony_ci			break;
91762306a36Sopenharmony_ci		default:
91862306a36Sopenharmony_ci			goto out_invalid_value;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci		break;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		/*
92362306a36Sopenharmony_ci		 * Special options
92462306a36Sopenharmony_ci		 */
92562306a36Sopenharmony_ci	case Opt_sloppy:
92662306a36Sopenharmony_ci		ctx->sloppy = true;
92762306a36Sopenharmony_ci		break;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	return 0;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ciout_invalid_value:
93362306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Bad mount option value specified");
93462306a36Sopenharmony_ciout_invalid_address:
93562306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Bad IP address specified");
93662306a36Sopenharmony_ciout_of_bounds:
93762306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Value for '%s' out of range", param->key);
93862306a36Sopenharmony_ciout_bad_transport:
93962306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Unrecognized transport protocol");
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci/*
94362306a36Sopenharmony_ci * Split fc->source into "hostname:export_path".
94462306a36Sopenharmony_ci *
94562306a36Sopenharmony_ci * The leftmost colon demarks the split between the server's hostname
94662306a36Sopenharmony_ci * and the export path.  If the hostname starts with a left square
94762306a36Sopenharmony_ci * bracket, then it may contain colons.
94862306a36Sopenharmony_ci *
94962306a36Sopenharmony_ci * Note: caller frees hostname and export path, even on error.
95062306a36Sopenharmony_ci */
95162306a36Sopenharmony_cistatic int nfs_parse_source(struct fs_context *fc,
95262306a36Sopenharmony_ci			    size_t maxnamlen, size_t maxpathlen)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
95562306a36Sopenharmony_ci	const char *dev_name = fc->source;
95662306a36Sopenharmony_ci	size_t len;
95762306a36Sopenharmony_ci	const char *end;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (unlikely(!dev_name || !*dev_name))
96062306a36Sopenharmony_ci		return -EINVAL;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* Is the host name protected with square brakcets? */
96362306a36Sopenharmony_ci	if (*dev_name == '[') {
96462306a36Sopenharmony_ci		end = strchr(++dev_name, ']');
96562306a36Sopenharmony_ci		if (end == NULL || end[1] != ':')
96662306a36Sopenharmony_ci			goto out_bad_devname;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		len = end - dev_name;
96962306a36Sopenharmony_ci		end++;
97062306a36Sopenharmony_ci	} else {
97162306a36Sopenharmony_ci		const char *comma;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci		end = strchr(dev_name, ':');
97462306a36Sopenharmony_ci		if (end == NULL)
97562306a36Sopenharmony_ci			goto out_bad_devname;
97662306a36Sopenharmony_ci		len = end - dev_name;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci		/* kill possible hostname list: not supported */
97962306a36Sopenharmony_ci		comma = memchr(dev_name, ',', len);
98062306a36Sopenharmony_ci		if (comma)
98162306a36Sopenharmony_ci			len = comma - dev_name;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (len > maxnamlen)
98562306a36Sopenharmony_ci		goto out_hostname;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	kfree(ctx->nfs_server.hostname);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* N.B. caller will free nfs_server.hostname in all cases */
99062306a36Sopenharmony_ci	ctx->nfs_server.hostname = kmemdup_nul(dev_name, len, GFP_KERNEL);
99162306a36Sopenharmony_ci	if (!ctx->nfs_server.hostname)
99262306a36Sopenharmony_ci		goto out_nomem;
99362306a36Sopenharmony_ci	len = strlen(++end);
99462306a36Sopenharmony_ci	if (len > maxpathlen)
99562306a36Sopenharmony_ci		goto out_path;
99662306a36Sopenharmony_ci	ctx->nfs_server.export_path = kmemdup_nul(end, len, GFP_KERNEL);
99762306a36Sopenharmony_ci	if (!ctx->nfs_server.export_path)
99862306a36Sopenharmony_ci		goto out_nomem;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	trace_nfs_mount_path(ctx->nfs_server.export_path);
100162306a36Sopenharmony_ci	return 0;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ciout_bad_devname:
100462306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: device name not in host:path format");
100562306a36Sopenharmony_ciout_nomem:
100662306a36Sopenharmony_ci	nfs_errorf(fc, "NFS: not enough memory to parse device name");
100762306a36Sopenharmony_ci	return -ENOMEM;
100862306a36Sopenharmony_ciout_hostname:
100962306a36Sopenharmony_ci	nfs_errorf(fc, "NFS: server hostname too long");
101062306a36Sopenharmony_ci	return -ENAMETOOLONG;
101162306a36Sopenharmony_ciout_path:
101262306a36Sopenharmony_ci	nfs_errorf(fc, "NFS: export pathname too long");
101362306a36Sopenharmony_ci	return -ENAMETOOLONG;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_cistatic inline bool is_remount_fc(struct fs_context *fc)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	return fc->root != NULL;
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci/*
102262306a36Sopenharmony_ci * Parse monolithic NFS2/NFS3 mount data
102362306a36Sopenharmony_ci * - fills in the mount root filehandle
102462306a36Sopenharmony_ci *
102562306a36Sopenharmony_ci * For option strings, user space handles the following behaviors:
102662306a36Sopenharmony_ci *
102762306a36Sopenharmony_ci * + DNS: mapping server host name to IP address ("addr=" option)
102862306a36Sopenharmony_ci *
102962306a36Sopenharmony_ci * + failure mode: how to behave if a mount request can't be handled
103062306a36Sopenharmony_ci *   immediately ("fg/bg" option)
103162306a36Sopenharmony_ci *
103262306a36Sopenharmony_ci * + retry: how often to retry a mount request ("retry=" option)
103362306a36Sopenharmony_ci *
103462306a36Sopenharmony_ci * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
103562306a36Sopenharmony_ci *   mountproto=tcp after mountproto=udp, and so on
103662306a36Sopenharmony_ci */
103762306a36Sopenharmony_cistatic int nfs23_parse_monolithic(struct fs_context *fc,
103862306a36Sopenharmony_ci				  struct nfs_mount_data *data)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
104162306a36Sopenharmony_ci	struct nfs_fh *mntfh = ctx->mntfh;
104262306a36Sopenharmony_ci	struct sockaddr_storage *sap = &ctx->nfs_server._address;
104362306a36Sopenharmony_ci	int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
104462306a36Sopenharmony_ci	int ret;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (data == NULL)
104762306a36Sopenharmony_ci		goto out_no_data;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	ctx->version = NFS_DEFAULT_VERSION;
105062306a36Sopenharmony_ci	switch (data->version) {
105162306a36Sopenharmony_ci	case 1:
105262306a36Sopenharmony_ci		data->namlen = 0;
105362306a36Sopenharmony_ci		fallthrough;
105462306a36Sopenharmony_ci	case 2:
105562306a36Sopenharmony_ci		data->bsize = 0;
105662306a36Sopenharmony_ci		fallthrough;
105762306a36Sopenharmony_ci	case 3:
105862306a36Sopenharmony_ci		if (data->flags & NFS_MOUNT_VER3)
105962306a36Sopenharmony_ci			goto out_no_v3;
106062306a36Sopenharmony_ci		data->root.size = NFS2_FHSIZE;
106162306a36Sopenharmony_ci		memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
106262306a36Sopenharmony_ci		/* Turn off security negotiation */
106362306a36Sopenharmony_ci		extra_flags |= NFS_MOUNT_SECFLAVOUR;
106462306a36Sopenharmony_ci		fallthrough;
106562306a36Sopenharmony_ci	case 4:
106662306a36Sopenharmony_ci		if (data->flags & NFS_MOUNT_SECFLAVOUR)
106762306a36Sopenharmony_ci			goto out_no_sec;
106862306a36Sopenharmony_ci		fallthrough;
106962306a36Sopenharmony_ci	case 5:
107062306a36Sopenharmony_ci		memset(data->context, 0, sizeof(data->context));
107162306a36Sopenharmony_ci		fallthrough;
107262306a36Sopenharmony_ci	case 6:
107362306a36Sopenharmony_ci		if (data->flags & NFS_MOUNT_VER3) {
107462306a36Sopenharmony_ci			if (data->root.size > NFS3_FHSIZE || data->root.size == 0)
107562306a36Sopenharmony_ci				goto out_invalid_fh;
107662306a36Sopenharmony_ci			mntfh->size = data->root.size;
107762306a36Sopenharmony_ci			ctx->version = 3;
107862306a36Sopenharmony_ci		} else {
107962306a36Sopenharmony_ci			mntfh->size = NFS2_FHSIZE;
108062306a36Sopenharmony_ci			ctx->version = 2;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci		memcpy(mntfh->data, data->root.data, mntfh->size);
108562306a36Sopenharmony_ci		if (mntfh->size < sizeof(mntfh->data))
108662306a36Sopenharmony_ci			memset(mntfh->data + mntfh->size, 0,
108762306a36Sopenharmony_ci			       sizeof(mntfh->data) - mntfh->size);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		/*
109062306a36Sopenharmony_ci		 * for proto == XPRT_TRANSPORT_UDP, which is what uses
109162306a36Sopenharmony_ci		 * to_exponential, implying shift: limit the shift value
109262306a36Sopenharmony_ci		 * to BITS_PER_LONG (majortimeo is unsigned long)
109362306a36Sopenharmony_ci		 */
109462306a36Sopenharmony_ci		if (!(data->flags & NFS_MOUNT_TCP)) /* this will be UDP */
109562306a36Sopenharmony_ci			if (data->retrans >= 64) /* shift value is too large */
109662306a36Sopenharmony_ci				goto out_invalid_data;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		/*
109962306a36Sopenharmony_ci		 * Translate to nfs_fs_context, which nfs_fill_super
110062306a36Sopenharmony_ci		 * can deal with.
110162306a36Sopenharmony_ci		 */
110262306a36Sopenharmony_ci		ctx->flags	= data->flags & NFS_MOUNT_FLAGMASK;
110362306a36Sopenharmony_ci		ctx->flags	|= extra_flags;
110462306a36Sopenharmony_ci		ctx->rsize	= data->rsize;
110562306a36Sopenharmony_ci		ctx->wsize	= data->wsize;
110662306a36Sopenharmony_ci		ctx->timeo	= data->timeo;
110762306a36Sopenharmony_ci		ctx->retrans	= data->retrans;
110862306a36Sopenharmony_ci		ctx->acregmin	= data->acregmin;
110962306a36Sopenharmony_ci		ctx->acregmax	= data->acregmax;
111062306a36Sopenharmony_ci		ctx->acdirmin	= data->acdirmin;
111162306a36Sopenharmony_ci		ctx->acdirmax	= data->acdirmax;
111262306a36Sopenharmony_ci		ctx->need_mount	= false;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		memcpy(sap, &data->addr, sizeof(data->addr));
111562306a36Sopenharmony_ci		ctx->nfs_server.addrlen = sizeof(data->addr);
111662306a36Sopenharmony_ci		ctx->nfs_server.port = ntohs(data->addr.sin_port);
111762306a36Sopenharmony_ci		if (sap->ss_family != AF_INET ||
111862306a36Sopenharmony_ci		    !nfs_verify_server_address(sap))
111962306a36Sopenharmony_ci			goto out_no_address;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci		if (!(data->flags & NFS_MOUNT_TCP))
112262306a36Sopenharmony_ci			ctx->nfs_server.protocol = XPRT_TRANSPORT_UDP;
112362306a36Sopenharmony_ci		/* N.B. caller will free nfs_server.hostname in all cases */
112462306a36Sopenharmony_ci		ctx->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
112562306a36Sopenharmony_ci		if (!ctx->nfs_server.hostname)
112662306a36Sopenharmony_ci			goto out_nomem;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		ctx->namlen		= data->namlen;
112962306a36Sopenharmony_ci		ctx->bsize		= data->bsize;
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		if (data->flags & NFS_MOUNT_SECFLAVOUR)
113262306a36Sopenharmony_ci			ctx->selected_flavor = data->pseudoflavor;
113362306a36Sopenharmony_ci		else
113462306a36Sopenharmony_ci			ctx->selected_flavor = RPC_AUTH_UNIX;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci		if (!(data->flags & NFS_MOUNT_NONLM))
113762306a36Sopenharmony_ci			ctx->flags &= ~(NFS_MOUNT_LOCAL_FLOCK|
113862306a36Sopenharmony_ci					 NFS_MOUNT_LOCAL_FCNTL);
113962306a36Sopenharmony_ci		else
114062306a36Sopenharmony_ci			ctx->flags |= (NFS_MOUNT_LOCAL_FLOCK|
114162306a36Sopenharmony_ci					NFS_MOUNT_LOCAL_FCNTL);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci		/*
114462306a36Sopenharmony_ci		 * The legacy version 6 binary mount data from userspace has a
114562306a36Sopenharmony_ci		 * field used only to transport selinux information into the
114662306a36Sopenharmony_ci		 * kernel.  To continue to support that functionality we
114762306a36Sopenharmony_ci		 * have a touch of selinux knowledge here in the NFS code. The
114862306a36Sopenharmony_ci		 * userspace code converted context=blah to just blah so we are
114962306a36Sopenharmony_ci		 * converting back to the full string selinux understands.
115062306a36Sopenharmony_ci		 */
115162306a36Sopenharmony_ci		if (data->context[0]){
115262306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX
115362306a36Sopenharmony_ci			int ret;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci			data->context[NFS_MAX_CONTEXT_LEN] = '\0';
115662306a36Sopenharmony_ci			ret = vfs_parse_fs_string(fc, "context",
115762306a36Sopenharmony_ci						  data->context, strlen(data->context));
115862306a36Sopenharmony_ci			if (ret < 0)
115962306a36Sopenharmony_ci				return ret;
116062306a36Sopenharmony_ci#else
116162306a36Sopenharmony_ci			return -EINVAL;
116262306a36Sopenharmony_ci#endif
116362306a36Sopenharmony_ci		}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		break;
116662306a36Sopenharmony_ci	default:
116762306a36Sopenharmony_ci		goto generic;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	ret = nfs_validate_transport_protocol(fc, ctx);
117162306a36Sopenharmony_ci	if (ret)
117262306a36Sopenharmony_ci		return ret;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	ctx->skip_reconfig_option_check = true;
117562306a36Sopenharmony_ci	return 0;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cigeneric:
117862306a36Sopenharmony_ci	return generic_parse_monolithic(fc, data);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ciout_no_data:
118162306a36Sopenharmony_ci	if (is_remount_fc(fc)) {
118262306a36Sopenharmony_ci		ctx->skip_reconfig_option_check = true;
118362306a36Sopenharmony_ci		return 0;
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: mount program didn't pass any mount data");
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ciout_no_v3:
118862306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: nfs_mount_data version does not support v3");
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ciout_no_sec:
119162306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: nfs_mount_data version supports only AUTH_SYS");
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ciout_nomem:
119462306a36Sopenharmony_ci	return -ENOMEM;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ciout_no_address:
119762306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: mount program didn't pass remote address");
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ciout_invalid_fh:
120062306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: invalid root filehandle");
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ciout_invalid_data:
120362306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: invalid binary mount data");
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
120762306a36Sopenharmony_cistruct compat_nfs_string {
120862306a36Sopenharmony_ci	compat_uint_t len;
120962306a36Sopenharmony_ci	compat_uptr_t data;
121062306a36Sopenharmony_ci};
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic inline void compat_nfs_string(struct nfs_string *dst,
121362306a36Sopenharmony_ci				     struct compat_nfs_string *src)
121462306a36Sopenharmony_ci{
121562306a36Sopenharmony_ci	dst->data = compat_ptr(src->data);
121662306a36Sopenharmony_ci	dst->len = src->len;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistruct compat_nfs4_mount_data_v1 {
122062306a36Sopenharmony_ci	compat_int_t version;
122162306a36Sopenharmony_ci	compat_int_t flags;
122262306a36Sopenharmony_ci	compat_int_t rsize;
122362306a36Sopenharmony_ci	compat_int_t wsize;
122462306a36Sopenharmony_ci	compat_int_t timeo;
122562306a36Sopenharmony_ci	compat_int_t retrans;
122662306a36Sopenharmony_ci	compat_int_t acregmin;
122762306a36Sopenharmony_ci	compat_int_t acregmax;
122862306a36Sopenharmony_ci	compat_int_t acdirmin;
122962306a36Sopenharmony_ci	compat_int_t acdirmax;
123062306a36Sopenharmony_ci	struct compat_nfs_string client_addr;
123162306a36Sopenharmony_ci	struct compat_nfs_string mnt_path;
123262306a36Sopenharmony_ci	struct compat_nfs_string hostname;
123362306a36Sopenharmony_ci	compat_uint_t host_addrlen;
123462306a36Sopenharmony_ci	compat_uptr_t host_addr;
123562306a36Sopenharmony_ci	compat_int_t proto;
123662306a36Sopenharmony_ci	compat_int_t auth_flavourlen;
123762306a36Sopenharmony_ci	compat_uptr_t auth_flavours;
123862306a36Sopenharmony_ci};
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic void nfs4_compat_mount_data_conv(struct nfs4_mount_data *data)
124162306a36Sopenharmony_ci{
124262306a36Sopenharmony_ci	struct compat_nfs4_mount_data_v1 *compat =
124362306a36Sopenharmony_ci			(struct compat_nfs4_mount_data_v1 *)data;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* copy the fields backwards */
124662306a36Sopenharmony_ci	data->auth_flavours = compat_ptr(compat->auth_flavours);
124762306a36Sopenharmony_ci	data->auth_flavourlen = compat->auth_flavourlen;
124862306a36Sopenharmony_ci	data->proto = compat->proto;
124962306a36Sopenharmony_ci	data->host_addr = compat_ptr(compat->host_addr);
125062306a36Sopenharmony_ci	data->host_addrlen = compat->host_addrlen;
125162306a36Sopenharmony_ci	compat_nfs_string(&data->hostname, &compat->hostname);
125262306a36Sopenharmony_ci	compat_nfs_string(&data->mnt_path, &compat->mnt_path);
125362306a36Sopenharmony_ci	compat_nfs_string(&data->client_addr, &compat->client_addr);
125462306a36Sopenharmony_ci	data->acdirmax = compat->acdirmax;
125562306a36Sopenharmony_ci	data->acdirmin = compat->acdirmin;
125662306a36Sopenharmony_ci	data->acregmax = compat->acregmax;
125762306a36Sopenharmony_ci	data->acregmin = compat->acregmin;
125862306a36Sopenharmony_ci	data->retrans = compat->retrans;
125962306a36Sopenharmony_ci	data->timeo = compat->timeo;
126062306a36Sopenharmony_ci	data->wsize = compat->wsize;
126162306a36Sopenharmony_ci	data->rsize = compat->rsize;
126262306a36Sopenharmony_ci	data->flags = compat->flags;
126362306a36Sopenharmony_ci	data->version = compat->version;
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci/*
126762306a36Sopenharmony_ci * Validate NFSv4 mount options
126862306a36Sopenharmony_ci */
126962306a36Sopenharmony_cistatic int nfs4_parse_monolithic(struct fs_context *fc,
127062306a36Sopenharmony_ci				 struct nfs4_mount_data *data)
127162306a36Sopenharmony_ci{
127262306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
127362306a36Sopenharmony_ci	struct sockaddr_storage *sap = &ctx->nfs_server._address;
127462306a36Sopenharmony_ci	int ret;
127562306a36Sopenharmony_ci	char *c;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (!data) {
127862306a36Sopenharmony_ci		if (is_remount_fc(fc))
127962306a36Sopenharmony_ci			goto done;
128062306a36Sopenharmony_ci		return nfs_invalf(fc,
128162306a36Sopenharmony_ci			"NFS4: mount program didn't pass any mount data");
128262306a36Sopenharmony_ci	}
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	ctx->version = 4;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (data->version != 1)
128762306a36Sopenharmony_ci		return generic_parse_monolithic(fc, data);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	if (in_compat_syscall())
129062306a36Sopenharmony_ci		nfs4_compat_mount_data_conv(data);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	if (data->host_addrlen > sizeof(ctx->nfs_server.address))
129362306a36Sopenharmony_ci		goto out_no_address;
129462306a36Sopenharmony_ci	if (data->host_addrlen == 0)
129562306a36Sopenharmony_ci		goto out_no_address;
129662306a36Sopenharmony_ci	ctx->nfs_server.addrlen = data->host_addrlen;
129762306a36Sopenharmony_ci	if (copy_from_user(sap, data->host_addr, data->host_addrlen))
129862306a36Sopenharmony_ci		return -EFAULT;
129962306a36Sopenharmony_ci	if (!nfs_verify_server_address(sap))
130062306a36Sopenharmony_ci		goto out_no_address;
130162306a36Sopenharmony_ci	ctx->nfs_server.port = ntohs(((struct sockaddr_in *)sap)->sin_port);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	if (data->auth_flavourlen) {
130462306a36Sopenharmony_ci		rpc_authflavor_t pseudoflavor;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci		if (data->auth_flavourlen > 1)
130762306a36Sopenharmony_ci			goto out_inval_auth;
130862306a36Sopenharmony_ci		if (copy_from_user(&pseudoflavor, data->auth_flavours,
130962306a36Sopenharmony_ci				   sizeof(pseudoflavor)))
131062306a36Sopenharmony_ci			return -EFAULT;
131162306a36Sopenharmony_ci		ctx->selected_flavor = pseudoflavor;
131262306a36Sopenharmony_ci	} else {
131362306a36Sopenharmony_ci		ctx->selected_flavor = RPC_AUTH_UNIX;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
131762306a36Sopenharmony_ci	if (IS_ERR(c))
131862306a36Sopenharmony_ci		return PTR_ERR(c);
131962306a36Sopenharmony_ci	ctx->nfs_server.hostname = c;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
132262306a36Sopenharmony_ci	if (IS_ERR(c))
132362306a36Sopenharmony_ci		return PTR_ERR(c);
132462306a36Sopenharmony_ci	ctx->nfs_server.export_path = c;
132562306a36Sopenharmony_ci	trace_nfs_mount_path(c);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	c = strndup_user(data->client_addr.data, 16);
132862306a36Sopenharmony_ci	if (IS_ERR(c))
132962306a36Sopenharmony_ci		return PTR_ERR(c);
133062306a36Sopenharmony_ci	ctx->client_address = c;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/*
133362306a36Sopenharmony_ci	 * Translate to nfs_fs_context, which nfs_fill_super
133462306a36Sopenharmony_ci	 * can deal with.
133562306a36Sopenharmony_ci	 */
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	ctx->flags	= data->flags & NFS4_MOUNT_FLAGMASK;
133862306a36Sopenharmony_ci	ctx->rsize	= data->rsize;
133962306a36Sopenharmony_ci	ctx->wsize	= data->wsize;
134062306a36Sopenharmony_ci	ctx->timeo	= data->timeo;
134162306a36Sopenharmony_ci	ctx->retrans	= data->retrans;
134262306a36Sopenharmony_ci	ctx->acregmin	= data->acregmin;
134362306a36Sopenharmony_ci	ctx->acregmax	= data->acregmax;
134462306a36Sopenharmony_ci	ctx->acdirmin	= data->acdirmin;
134562306a36Sopenharmony_ci	ctx->acdirmax	= data->acdirmax;
134662306a36Sopenharmony_ci	ctx->nfs_server.protocol = data->proto;
134762306a36Sopenharmony_ci	ret = nfs_validate_transport_protocol(fc, ctx);
134862306a36Sopenharmony_ci	if (ret)
134962306a36Sopenharmony_ci		return ret;
135062306a36Sopenharmony_cidone:
135162306a36Sopenharmony_ci	ctx->skip_reconfig_option_check = true;
135262306a36Sopenharmony_ci	return 0;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ciout_inval_auth:
135562306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS4: Invalid number of RPC auth flavours %d",
135662306a36Sopenharmony_ci		      data->auth_flavourlen);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ciout_no_address:
135962306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS4: mount program didn't pass remote address");
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci#endif
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci/*
136462306a36Sopenharmony_ci * Parse a monolithic block of data from sys_mount().
136562306a36Sopenharmony_ci */
136662306a36Sopenharmony_cistatic int nfs_fs_context_parse_monolithic(struct fs_context *fc,
136762306a36Sopenharmony_ci					   void *data)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	if (fc->fs_type == &nfs_fs_type)
137062306a36Sopenharmony_ci		return nfs23_parse_monolithic(fc, data);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
137362306a36Sopenharmony_ci	if (fc->fs_type == &nfs4_fs_type)
137462306a36Sopenharmony_ci		return nfs4_parse_monolithic(fc, data);
137562306a36Sopenharmony_ci#endif
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Unsupported monolithic data version");
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci/*
138162306a36Sopenharmony_ci * Validate the preparsed information in the config.
138262306a36Sopenharmony_ci */
138362306a36Sopenharmony_cistatic int nfs_fs_context_validate(struct fs_context *fc)
138462306a36Sopenharmony_ci{
138562306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
138662306a36Sopenharmony_ci	struct nfs_subversion *nfs_mod;
138762306a36Sopenharmony_ci	struct sockaddr_storage *sap = &ctx->nfs_server._address;
138862306a36Sopenharmony_ci	int max_namelen = PAGE_SIZE;
138962306a36Sopenharmony_ci	int max_pathlen = NFS_MAXPATHLEN;
139062306a36Sopenharmony_ci	int port = 0;
139162306a36Sopenharmony_ci	int ret;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	if (!fc->source)
139462306a36Sopenharmony_ci		goto out_no_device_name;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	/* Check for sanity first. */
139762306a36Sopenharmony_ci	if (ctx->minorversion && ctx->version != 4)
139862306a36Sopenharmony_ci		goto out_minorversion_mismatch;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (ctx->options & NFS_OPTION_MIGRATION &&
140162306a36Sopenharmony_ci	    (ctx->version != 4 || ctx->minorversion != 0))
140262306a36Sopenharmony_ci		goto out_migration_misuse;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	/* Verify that any proto=/mountproto= options match the address
140562306a36Sopenharmony_ci	 * families in the addr=/mountaddr= options.
140662306a36Sopenharmony_ci	 */
140762306a36Sopenharmony_ci	if (ctx->protofamily != AF_UNSPEC &&
140862306a36Sopenharmony_ci	    ctx->protofamily != ctx->nfs_server.address.sa_family)
140962306a36Sopenharmony_ci		goto out_proto_mismatch;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	if (ctx->mountfamily != AF_UNSPEC) {
141262306a36Sopenharmony_ci		if (ctx->mount_server.addrlen) {
141362306a36Sopenharmony_ci			if (ctx->mountfamily != ctx->mount_server.address.sa_family)
141462306a36Sopenharmony_ci				goto out_mountproto_mismatch;
141562306a36Sopenharmony_ci		} else {
141662306a36Sopenharmony_ci			if (ctx->mountfamily != ctx->nfs_server.address.sa_family)
141762306a36Sopenharmony_ci				goto out_mountproto_mismatch;
141862306a36Sopenharmony_ci		}
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	if (!nfs_verify_server_address(sap))
142262306a36Sopenharmony_ci		goto out_no_address;
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	ret = nfs_validate_transport_protocol(fc, ctx);
142562306a36Sopenharmony_ci	if (ret)
142662306a36Sopenharmony_ci		return ret;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	if (ctx->version == 4) {
142962306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_NFS_V4)) {
143062306a36Sopenharmony_ci			if (ctx->nfs_server.protocol == XPRT_TRANSPORT_RDMA)
143162306a36Sopenharmony_ci				port = NFS_RDMA_PORT;
143262306a36Sopenharmony_ci			else
143362306a36Sopenharmony_ci				port = NFS_PORT;
143462306a36Sopenharmony_ci			max_namelen = NFS4_MAXNAMLEN;
143562306a36Sopenharmony_ci			max_pathlen = NFS4_MAXPATHLEN;
143662306a36Sopenharmony_ci			ctx->flags &= ~(NFS_MOUNT_NONLM | NFS_MOUNT_NOACL |
143762306a36Sopenharmony_ci					NFS_MOUNT_VER3 | NFS_MOUNT_LOCAL_FLOCK |
143862306a36Sopenharmony_ci					NFS_MOUNT_LOCAL_FCNTL);
143962306a36Sopenharmony_ci		} else {
144062306a36Sopenharmony_ci			goto out_v4_not_compiled;
144162306a36Sopenharmony_ci		}
144262306a36Sopenharmony_ci	} else {
144362306a36Sopenharmony_ci		nfs_set_mount_transport_protocol(ctx);
144462306a36Sopenharmony_ci		if (ctx->nfs_server.protocol == XPRT_TRANSPORT_RDMA)
144562306a36Sopenharmony_ci			port = NFS_RDMA_PORT;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	nfs_set_port(sap, &ctx->nfs_server.port, port);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	ret = nfs_parse_source(fc, max_namelen, max_pathlen);
145162306a36Sopenharmony_ci	if (ret < 0)
145262306a36Sopenharmony_ci		return ret;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	/* Load the NFS protocol module if we haven't done so yet */
145562306a36Sopenharmony_ci	if (!ctx->nfs_mod) {
145662306a36Sopenharmony_ci		nfs_mod = get_nfs_version(ctx->version);
145762306a36Sopenharmony_ci		if (IS_ERR(nfs_mod)) {
145862306a36Sopenharmony_ci			ret = PTR_ERR(nfs_mod);
145962306a36Sopenharmony_ci			goto out_version_unavailable;
146062306a36Sopenharmony_ci		}
146162306a36Sopenharmony_ci		ctx->nfs_mod = nfs_mod;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	/* Ensure the filesystem context has the correct fs_type */
146562306a36Sopenharmony_ci	if (fc->fs_type != ctx->nfs_mod->nfs_fs) {
146662306a36Sopenharmony_ci		module_put(fc->fs_type->owner);
146762306a36Sopenharmony_ci		__module_get(ctx->nfs_mod->nfs_fs->owner);
146862306a36Sopenharmony_ci		fc->fs_type = ctx->nfs_mod->nfs_fs;
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci	return 0;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ciout_no_device_name:
147362306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Device name not specified");
147462306a36Sopenharmony_ciout_v4_not_compiled:
147562306a36Sopenharmony_ci	nfs_errorf(fc, "NFS: NFSv4 is not compiled into kernel");
147662306a36Sopenharmony_ci	return -EPROTONOSUPPORT;
147762306a36Sopenharmony_ciout_no_address:
147862306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: mount program didn't pass remote address");
147962306a36Sopenharmony_ciout_mountproto_mismatch:
148062306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Mount server address does not match mountproto= option");
148162306a36Sopenharmony_ciout_proto_mismatch:
148262306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Server address does not match proto= option");
148362306a36Sopenharmony_ciout_minorversion_mismatch:
148462306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: Mount option vers=%u does not support minorversion=%u",
148562306a36Sopenharmony_ci			  ctx->version, ctx->minorversion);
148662306a36Sopenharmony_ciout_migration_misuse:
148762306a36Sopenharmony_ci	return nfs_invalf(fc, "NFS: 'Migration' not supported for this NFS version");
148862306a36Sopenharmony_ciout_version_unavailable:
148962306a36Sopenharmony_ci	nfs_errorf(fc, "NFS: Version unavailable");
149062306a36Sopenharmony_ci	return ret;
149162306a36Sopenharmony_ci}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci/*
149462306a36Sopenharmony_ci * Create an NFS superblock by the appropriate method.
149562306a36Sopenharmony_ci */
149662306a36Sopenharmony_cistatic int nfs_get_tree(struct fs_context *fc)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
149962306a36Sopenharmony_ci	int err = nfs_fs_context_validate(fc);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (err)
150262306a36Sopenharmony_ci		return err;
150362306a36Sopenharmony_ci	if (!ctx->internal)
150462306a36Sopenharmony_ci		return ctx->nfs_mod->rpc_ops->try_get_tree(fc);
150562306a36Sopenharmony_ci	else
150662306a36Sopenharmony_ci		return nfs_get_tree_common(fc);
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci/*
151062306a36Sopenharmony_ci * Handle duplication of a configuration.  The caller copied *src into *sc, but
151162306a36Sopenharmony_ci * it can't deal with resource pointers in the filesystem context, so we have
151262306a36Sopenharmony_ci * to do that.  We need to clear pointers, copy data or get extra refs as
151362306a36Sopenharmony_ci * appropriate.
151462306a36Sopenharmony_ci */
151562306a36Sopenharmony_cistatic int nfs_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct nfs_fs_context *src = nfs_fc2context(src_fc), *ctx;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	ctx = kmemdup(src, sizeof(struct nfs_fs_context), GFP_KERNEL);
152062306a36Sopenharmony_ci	if (!ctx)
152162306a36Sopenharmony_ci		return -ENOMEM;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	ctx->mntfh = nfs_alloc_fhandle();
152462306a36Sopenharmony_ci	if (!ctx->mntfh) {
152562306a36Sopenharmony_ci		kfree(ctx);
152662306a36Sopenharmony_ci		return -ENOMEM;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci	nfs_copy_fh(ctx->mntfh, src->mntfh);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	__module_get(ctx->nfs_mod->owner);
153162306a36Sopenharmony_ci	ctx->client_address		= NULL;
153262306a36Sopenharmony_ci	ctx->mount_server.hostname	= NULL;
153362306a36Sopenharmony_ci	ctx->nfs_server.export_path	= NULL;
153462306a36Sopenharmony_ci	ctx->nfs_server.hostname	= NULL;
153562306a36Sopenharmony_ci	ctx->fscache_uniq		= NULL;
153662306a36Sopenharmony_ci	ctx->clone_data.fattr		= NULL;
153762306a36Sopenharmony_ci	fc->fs_private = ctx;
153862306a36Sopenharmony_ci	return 0;
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic void nfs_fs_context_free(struct fs_context *fc)
154262306a36Sopenharmony_ci{
154362306a36Sopenharmony_ci	struct nfs_fs_context *ctx = nfs_fc2context(fc);
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_ci	if (ctx) {
154662306a36Sopenharmony_ci		if (ctx->server)
154762306a36Sopenharmony_ci			nfs_free_server(ctx->server);
154862306a36Sopenharmony_ci		if (ctx->nfs_mod)
154962306a36Sopenharmony_ci			put_nfs_version(ctx->nfs_mod);
155062306a36Sopenharmony_ci		kfree(ctx->client_address);
155162306a36Sopenharmony_ci		kfree(ctx->mount_server.hostname);
155262306a36Sopenharmony_ci		kfree(ctx->nfs_server.export_path);
155362306a36Sopenharmony_ci		kfree(ctx->nfs_server.hostname);
155462306a36Sopenharmony_ci		kfree(ctx->fscache_uniq);
155562306a36Sopenharmony_ci		nfs_free_fhandle(ctx->mntfh);
155662306a36Sopenharmony_ci		nfs_free_fattr(ctx->clone_data.fattr);
155762306a36Sopenharmony_ci		kfree(ctx);
155862306a36Sopenharmony_ci	}
155962306a36Sopenharmony_ci}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic const struct fs_context_operations nfs_fs_context_ops = {
156262306a36Sopenharmony_ci	.free			= nfs_fs_context_free,
156362306a36Sopenharmony_ci	.dup			= nfs_fs_context_dup,
156462306a36Sopenharmony_ci	.parse_param		= nfs_fs_context_parse_param,
156562306a36Sopenharmony_ci	.parse_monolithic	= nfs_fs_context_parse_monolithic,
156662306a36Sopenharmony_ci	.get_tree		= nfs_get_tree,
156762306a36Sopenharmony_ci	.reconfigure		= nfs_reconfigure,
156862306a36Sopenharmony_ci};
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci/*
157162306a36Sopenharmony_ci * Prepare superblock configuration.  We use the namespaces attached to the
157262306a36Sopenharmony_ci * context.  This may be the current process's namespaces, or it may be a
157362306a36Sopenharmony_ci * container's namespaces.
157462306a36Sopenharmony_ci */
157562306a36Sopenharmony_cistatic int nfs_init_fs_context(struct fs_context *fc)
157662306a36Sopenharmony_ci{
157762306a36Sopenharmony_ci	struct nfs_fs_context *ctx;
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	ctx = kzalloc(sizeof(struct nfs_fs_context), GFP_KERNEL);
158062306a36Sopenharmony_ci	if (unlikely(!ctx))
158162306a36Sopenharmony_ci		return -ENOMEM;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	ctx->mntfh = nfs_alloc_fhandle();
158462306a36Sopenharmony_ci	if (unlikely(!ctx->mntfh)) {
158562306a36Sopenharmony_ci		kfree(ctx);
158662306a36Sopenharmony_ci		return -ENOMEM;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	ctx->protofamily	= AF_UNSPEC;
159062306a36Sopenharmony_ci	ctx->mountfamily	= AF_UNSPEC;
159162306a36Sopenharmony_ci	ctx->mount_server.port	= NFS_UNSPEC_PORT;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	if (fc->root) {
159462306a36Sopenharmony_ci		/* reconfigure, start with the current config */
159562306a36Sopenharmony_ci		struct nfs_server *nfss = fc->root->d_sb->s_fs_info;
159662306a36Sopenharmony_ci		struct net *net = nfss->nfs_client->cl_net;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci		ctx->flags		= nfss->flags;
159962306a36Sopenharmony_ci		ctx->rsize		= nfss->rsize;
160062306a36Sopenharmony_ci		ctx->wsize		= nfss->wsize;
160162306a36Sopenharmony_ci		ctx->retrans		= nfss->client->cl_timeout->to_retries;
160262306a36Sopenharmony_ci		ctx->selected_flavor	= nfss->client->cl_auth->au_flavor;
160362306a36Sopenharmony_ci		ctx->acregmin		= nfss->acregmin / HZ;
160462306a36Sopenharmony_ci		ctx->acregmax		= nfss->acregmax / HZ;
160562306a36Sopenharmony_ci		ctx->acdirmin		= nfss->acdirmin / HZ;
160662306a36Sopenharmony_ci		ctx->acdirmax		= nfss->acdirmax / HZ;
160762306a36Sopenharmony_ci		ctx->timeo		= 10U * nfss->client->cl_timeout->to_initval / HZ;
160862306a36Sopenharmony_ci		ctx->nfs_server.port	= nfss->port;
160962306a36Sopenharmony_ci		ctx->nfs_server.addrlen	= nfss->nfs_client->cl_addrlen;
161062306a36Sopenharmony_ci		ctx->version		= nfss->nfs_client->rpc_ops->version;
161162306a36Sopenharmony_ci		ctx->minorversion	= nfss->nfs_client->cl_minorversion;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci		memcpy(&ctx->nfs_server._address, &nfss->nfs_client->cl_addr,
161462306a36Sopenharmony_ci			ctx->nfs_server.addrlen);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci		if (fc->net_ns != net) {
161762306a36Sopenharmony_ci			put_net(fc->net_ns);
161862306a36Sopenharmony_ci			fc->net_ns = get_net(net);
161962306a36Sopenharmony_ci		}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci		ctx->nfs_mod = nfss->nfs_client->cl_nfs_mod;
162262306a36Sopenharmony_ci		__module_get(ctx->nfs_mod->owner);
162362306a36Sopenharmony_ci	} else {
162462306a36Sopenharmony_ci		/* defaults */
162562306a36Sopenharmony_ci		ctx->timeo		= NFS_UNSPEC_TIMEO;
162662306a36Sopenharmony_ci		ctx->retrans		= NFS_UNSPEC_RETRANS;
162762306a36Sopenharmony_ci		ctx->acregmin		= NFS_DEF_ACREGMIN;
162862306a36Sopenharmony_ci		ctx->acregmax		= NFS_DEF_ACREGMAX;
162962306a36Sopenharmony_ci		ctx->acdirmin		= NFS_DEF_ACDIRMIN;
163062306a36Sopenharmony_ci		ctx->acdirmax		= NFS_DEF_ACDIRMAX;
163162306a36Sopenharmony_ci		ctx->nfs_server.port	= NFS_UNSPEC_PORT;
163262306a36Sopenharmony_ci		ctx->nfs_server.protocol = XPRT_TRANSPORT_TCP;
163362306a36Sopenharmony_ci		ctx->selected_flavor	= RPC_AUTH_MAXFLAVOR;
163462306a36Sopenharmony_ci		ctx->minorversion	= 0;
163562306a36Sopenharmony_ci		ctx->need_mount		= true;
163662306a36Sopenharmony_ci		ctx->xprtsec.policy	= RPC_XPRTSEC_NONE;
163762306a36Sopenharmony_ci		ctx->xprtsec.cert_serial	= TLS_NO_CERT;
163862306a36Sopenharmony_ci		ctx->xprtsec.privkey_serial	= TLS_NO_PRIVKEY;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci		fc->s_iflags		|= SB_I_STABLE_WRITES;
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci	fc->fs_private = ctx;
164362306a36Sopenharmony_ci	fc->ops = &nfs_fs_context_ops;
164462306a36Sopenharmony_ci	return 0;
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_cistruct file_system_type nfs_fs_type = {
164862306a36Sopenharmony_ci	.owner			= THIS_MODULE,
164962306a36Sopenharmony_ci	.name			= "nfs",
165062306a36Sopenharmony_ci	.init_fs_context	= nfs_init_fs_context,
165162306a36Sopenharmony_ci	.parameters		= nfs_fs_parameters,
165262306a36Sopenharmony_ci	.kill_sb		= nfs_kill_super,
165362306a36Sopenharmony_ci	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
165462306a36Sopenharmony_ci};
165562306a36Sopenharmony_ciMODULE_ALIAS_FS("nfs");
165662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs_fs_type);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NFS_V4)
165962306a36Sopenharmony_cistruct file_system_type nfs4_fs_type = {
166062306a36Sopenharmony_ci	.owner			= THIS_MODULE,
166162306a36Sopenharmony_ci	.name			= "nfs4",
166262306a36Sopenharmony_ci	.init_fs_context	= nfs_init_fs_context,
166362306a36Sopenharmony_ci	.parameters		= nfs_fs_parameters,
166462306a36Sopenharmony_ci	.kill_sb		= nfs_kill_super,
166562306a36Sopenharmony_ci	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
166662306a36Sopenharmony_ci};
166762306a36Sopenharmony_ciMODULE_ALIAS_FS("nfs4");
166862306a36Sopenharmony_ciMODULE_ALIAS("nfs4");
166962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nfs4_fs_type);
167062306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4 */
1671