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