1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2f7b422b1SDavid Howells /* 3f7b422b1SDavid Howells * linux/fs/nfs/nfs4namespace.c 4f7b422b1SDavid Howells * 5f7b422b1SDavid Howells * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> 654ceac45SDavid Howells * - Modified by David Howells <dhowells@redhat.com> 7f7b422b1SDavid Howells * 8f7b422b1SDavid Howells * NFSv4 namespace 9f7b422b1SDavid Howells */ 10f7b422b1SDavid Howells 11f2aedb71SDavid Howells #include <linux/module.h> 12f7b422b1SDavid Howells #include <linux/dcache.h> 13f7b422b1SDavid Howells #include <linux/mount.h> 14f7b422b1SDavid Howells #include <linux/namei.h> 15f7b422b1SDavid Howells #include <linux/nfs_fs.h> 1647040da3STrond Myklebust #include <linux/nfs_mount.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 18f7b422b1SDavid Howells #include <linux/string.h> 19f7b422b1SDavid Howells #include <linux/sunrpc/clnt.h> 205976687aSJeff Layton #include <linux/sunrpc/addr.h> 21f7b422b1SDavid Howells #include <linux/vfs.h> 22f7b422b1SDavid Howells #include <linux/inet.h> 23f7b422b1SDavid Howells #include "internal.h" 24c228fd3aSTrond Myklebust #include "nfs4_fs.h" 25f2aedb71SDavid Howells #include "nfs.h" 267d7ea882STrond Myklebust #include "dns_resolve.h" 27f7b422b1SDavid Howells 28f7b422b1SDavid Howells #define NFSDBG_FACILITY NFSDBG_VFS 29f7b422b1SDavid Howells 30f7b422b1SDavid Howells /* 31f2aedb71SDavid Howells * Work out the length that an NFSv4 path would render to as a standard posix 32f2aedb71SDavid Howells * path, with a leading slash but no terminating slash. 33f7b422b1SDavid Howells */ 34f2aedb71SDavid Howells static ssize_t nfs4_pathname_len(const struct nfs4_pathname *pathname) 35f7b422b1SDavid Howells { 36f2aedb71SDavid Howells ssize_t len = 0; 37f2aedb71SDavid Howells int i; 38f7b422b1SDavid Howells 39f2aedb71SDavid Howells for (i = 0; i < pathname->ncomponents; i++) { 40f2aedb71SDavid Howells const struct nfs4_string *component = &pathname->components[i]; 41f7b422b1SDavid Howells 42f2aedb71SDavid Howells if (component->len > NAME_MAX) 43f2aedb71SDavid Howells goto too_long; 44f2aedb71SDavid Howells len += 1 + component->len; /* Adding "/foo" */ 45f2aedb71SDavid Howells if (len > PATH_MAX) 46f2aedb71SDavid Howells goto too_long; 47f7b422b1SDavid Howells } 48f2aedb71SDavid Howells return len; 49f2aedb71SDavid Howells 50f2aedb71SDavid Howells too_long: 51f2aedb71SDavid Howells return -ENAMETOOLONG; 52f2aedb71SDavid Howells } 53f2aedb71SDavid Howells 54f2aedb71SDavid Howells /* 55f2aedb71SDavid Howells * Convert the NFSv4 pathname components into a standard posix path. 56f2aedb71SDavid Howells */ 57f2aedb71SDavid Howells static char *nfs4_pathname_string(const struct nfs4_pathname *pathname, 58f2aedb71SDavid Howells unsigned short *_len) 59f2aedb71SDavid Howells { 60f2aedb71SDavid Howells ssize_t len; 61f2aedb71SDavid Howells char *buf, *p; 62f2aedb71SDavid Howells int i; 63f2aedb71SDavid Howells 64f2aedb71SDavid Howells len = nfs4_pathname_len(pathname); 65f2aedb71SDavid Howells if (len < 0) 66f2aedb71SDavid Howells return ERR_PTR(len); 67f2aedb71SDavid Howells *_len = len; 68f2aedb71SDavid Howells 69f2aedb71SDavid Howells p = buf = kmalloc(len + 1, GFP_KERNEL); 70f2aedb71SDavid Howells if (!buf) 71f2aedb71SDavid Howells return ERR_PTR(-ENOMEM); 72f2aedb71SDavid Howells 73f2aedb71SDavid Howells for (i = 0; i < pathname->ncomponents; i++) { 74f2aedb71SDavid Howells const struct nfs4_string *component = &pathname->components[i]; 75f2aedb71SDavid Howells 76f2aedb71SDavid Howells *p++ = '/'; 77f2aedb71SDavid Howells memcpy(p, component->data, component->len); 78f2aedb71SDavid Howells p += component->len; 79f2aedb71SDavid Howells } 80f2aedb71SDavid Howells 81f2aedb71SDavid Howells *p = 0; 82f2aedb71SDavid Howells return buf; 83f7b422b1SDavid Howells } 84f7b422b1SDavid Howells 8554ceac45SDavid Howells /* 861aba1567SWeston Andros Adamson * return the path component of "<server>:<path>" 871aba1567SWeston Andros Adamson * nfspath - the "<server>:<path>" string 881aba1567SWeston Andros Adamson * end - one past the last char that could contain "<server>:" 891aba1567SWeston Andros Adamson * returns NULL on failure 901aba1567SWeston Andros Adamson */ 911aba1567SWeston Andros Adamson static char *nfs_path_component(const char *nfspath, const char *end) 921aba1567SWeston Andros Adamson { 931aba1567SWeston Andros Adamson char *p; 941aba1567SWeston Andros Adamson 951aba1567SWeston Andros Adamson if (*nfspath == '[') { 961aba1567SWeston Andros Adamson /* parse [] escaped IPv6 addrs */ 971aba1567SWeston Andros Adamson p = strchr(nfspath, ']'); 981aba1567SWeston Andros Adamson if (p != NULL && ++p < end && *p == ':') 991aba1567SWeston Andros Adamson return p + 1; 1001aba1567SWeston Andros Adamson } else { 1011aba1567SWeston Andros Adamson /* otherwise split on first colon */ 1021aba1567SWeston Andros Adamson p = strchr(nfspath, ':'); 1031aba1567SWeston Andros Adamson if (p != NULL && p < end) 1041aba1567SWeston Andros Adamson return p + 1; 1051aba1567SWeston Andros Adamson } 1061aba1567SWeston Andros Adamson return NULL; 1071aba1567SWeston Andros Adamson } 1081aba1567SWeston Andros Adamson 1091aba1567SWeston Andros Adamson /* 11054ceac45SDavid Howells * Determine the mount path as a string 11154ceac45SDavid Howells */ 112b514f872SAl Viro static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) 11354ceac45SDavid Howells { 114b514f872SAl Viro char *limit; 11597a54868SBen Hutchings char *path = nfs_path(&limit, dentry, buffer, buflen, 11697a54868SBen Hutchings NFS_PATH_CANONICAL); 117b514f872SAl Viro if (!IS_ERR(path)) { 1181aba1567SWeston Andros Adamson char *path_component = nfs_path_component(path, limit); 1191aba1567SWeston Andros Adamson if (path_component) 1201aba1567SWeston Andros Adamson return path_component; 121b514f872SAl Viro } 122b514f872SAl Viro return path; 12354ceac45SDavid Howells } 12454ceac45SDavid Howells 12554ceac45SDavid Howells /* 12654ceac45SDavid Howells * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we 12754ceac45SDavid Howells * believe to be the server path to this dentry 12854ceac45SDavid Howells */ 129b514f872SAl Viro static int nfs4_validate_fspath(struct dentry *dentry, 13054ceac45SDavid Howells const struct nfs4_fs_locations *locations, 131f2aedb71SDavid Howells struct nfs_fs_context *ctx) 13254ceac45SDavid Howells { 13362a55d08SScott Mayhew const char *path; 13462a55d08SScott Mayhew char *fs_path; 135f2aedb71SDavid Howells unsigned short len; 13662a55d08SScott Mayhew char *buf; 137f2aedb71SDavid Howells int n; 13854ceac45SDavid Howells 139f2aedb71SDavid Howells buf = kmalloc(4096, GFP_KERNEL); 140e0b27d98SColin Ian King if (!buf) 141e0b27d98SColin Ian King return -ENOMEM; 142e0b27d98SColin Ian King 143f2aedb71SDavid Howells path = nfs4_path(dentry, buf, 4096); 144f2aedb71SDavid Howells if (IS_ERR(path)) { 145f2aedb71SDavid Howells kfree(buf); 14654ceac45SDavid Howells return PTR_ERR(path); 147f2aedb71SDavid Howells } 14854ceac45SDavid Howells 149f2aedb71SDavid Howells fs_path = nfs4_pathname_string(&locations->fs_path, &len); 150f2aedb71SDavid Howells if (IS_ERR(fs_path)) { 151f2aedb71SDavid Howells kfree(buf); 15254ceac45SDavid Howells return PTR_ERR(fs_path); 153f2aedb71SDavid Howells } 15454ceac45SDavid Howells 155f2aedb71SDavid Howells n = strncmp(path, fs_path, len); 156f2aedb71SDavid Howells kfree(buf); 157f2aedb71SDavid Howells kfree(fs_path); 158f2aedb71SDavid Howells if (n != 0) { 15954ceac45SDavid Howells dprintk("%s: path %s does not begin with fsroot %s\n", 160f2aedb71SDavid Howells __func__, path, ctx->nfs_server.export_path); 16154ceac45SDavid Howells return -ENOENT; 16254ceac45SDavid Howells } 16354ceac45SDavid Howells 16454ceac45SDavid Howells return 0; 16554ceac45SDavid Howells } 16654ceac45SDavid Howells 1677d7ea882STrond Myklebust static size_t nfs_parse_server_name(char *string, size_t len, 168292f503cSTrond Myklebust struct sockaddr *sa, size_t salen, struct net *net) 1697d7ea882STrond Myklebust { 1707d7ea882STrond Myklebust ssize_t ret; 1717d7ea882STrond Myklebust 17233faaa38SStanislav Kinsbursky ret = rpc_pton(net, string, len, sa, salen); 1737d7ea882STrond Myklebust if (ret == 0) { 17433faaa38SStanislav Kinsbursky ret = nfs_dns_resolve_name(net, string, len, sa, salen); 1757d7ea882STrond Myklebust if (ret < 0) 1767d7ea882STrond Myklebust ret = 0; 1777d7ea882STrond Myklebust } 1787d7ea882STrond Myklebust return ret; 1797d7ea882STrond Myklebust } 1807d7ea882STrond Myklebust 1819568c5e9SChuck Lever /** 1829568c5e9SChuck Lever * nfs_find_best_sec - Find a security mechanism supported locally 183302fad7bSTrond Myklebust * @clnt: pointer to rpc_clnt 1844d4b69ddSWeston Andros Adamson * @server: NFS server struct 1859568c5e9SChuck Lever * @flavors: List of security tuples returned by SECINFO procedure 1869568c5e9SChuck Lever * 18766b06860SAndy Adamson * Return an rpc client that uses the first security mechanism in 1888445cd35SAndy Adamson * "flavors" that is locally supported. The "flavors" array 1899568c5e9SChuck Lever * is searched in the order returned from the server, per RFC 3530 19066b06860SAndy Adamson * recommendation and each flavor is checked for membership in the 19166b06860SAndy Adamson * sec= mount option list if it exists. 1928445cd35SAndy Adamson * 1938445cd35SAndy Adamson * Return -EPERM if no matching flavor is found in the array. 19466b06860SAndy Adamson * 19566b06860SAndy Adamson * Please call rpc_shutdown_client() when you are done with this rpc client. 19666b06860SAndy Adamson * 1979568c5e9SChuck Lever */ 19866b06860SAndy Adamson static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt, 19966b06860SAndy Adamson struct nfs_server *server, 2004d4b69ddSWeston Andros Adamson struct nfs4_secinfo_flavors *flavors) 2012671bfc3SBryan Schumaker { 20266b06860SAndy Adamson rpc_authflavor_t pflavor; 2039568c5e9SChuck Lever struct nfs4_secinfo4 *secinfo; 204fb15b26fSChuck Lever unsigned int i; 2052671bfc3SBryan Schumaker 2062671bfc3SBryan Schumaker for (i = 0; i < flavors->num_flavors; i++) { 2079568c5e9SChuck Lever secinfo = &flavors->flavors[i]; 2082671bfc3SBryan Schumaker 2099568c5e9SChuck Lever switch (secinfo->flavor) { 2109568c5e9SChuck Lever case RPC_AUTH_NULL: 2119568c5e9SChuck Lever case RPC_AUTH_UNIX: 2129568c5e9SChuck Lever case RPC_AUTH_GSS: 21366b06860SAndy Adamson pflavor = rpcauth_get_pseudoflavor(secinfo->flavor, 2149568c5e9SChuck Lever &secinfo->flavor_info); 21566b06860SAndy Adamson /* does the pseudoflavor match a sec= mount opt? */ 21666b06860SAndy Adamson if (pflavor != RPC_AUTH_MAXFLAVOR && 21766b06860SAndy Adamson nfs_auth_info_match(&server->auth_info, pflavor)) { 21866b06860SAndy Adamson struct rpc_clnt *new; 21966b06860SAndy Adamson struct rpc_cred *cred; 22066b06860SAndy Adamson 22166b06860SAndy Adamson /* Cloning creates an rpc_auth for the flavor */ 22266b06860SAndy Adamson new = rpc_clone_client_set_auth(clnt, pflavor); 22366b06860SAndy Adamson if (IS_ERR(new)) 22466b06860SAndy Adamson continue; 22566b06860SAndy Adamson /** 22666b06860SAndy Adamson * Check that the user actually can use the 22766b06860SAndy Adamson * flavor. This is mostly for RPC_AUTH_GSS 22866b06860SAndy Adamson * where cr_init obtains a gss context 22966b06860SAndy Adamson */ 23066b06860SAndy Adamson cred = rpcauth_lookupcred(new->cl_auth, 0); 23166b06860SAndy Adamson if (IS_ERR(cred)) { 23266b06860SAndy Adamson rpc_shutdown_client(new); 23366b06860SAndy Adamson continue; 2349568c5e9SChuck Lever } 23566b06860SAndy Adamson put_rpccred(cred); 23666b06860SAndy Adamson return new; 23766b06860SAndy Adamson } 23866b06860SAndy Adamson } 23966b06860SAndy Adamson } 24066b06860SAndy Adamson return ERR_PTR(-EPERM); 2419568c5e9SChuck Lever } 2429568c5e9SChuck Lever 24366b06860SAndy Adamson /** 24466b06860SAndy Adamson * nfs4_negotiate_security - in response to an NFS4ERR_WRONGSEC on lookup, 24566b06860SAndy Adamson * return an rpc_clnt that uses the best available security flavor with 24666b06860SAndy Adamson * respect to the secinfo flavor list and the sec= mount options. 24766b06860SAndy Adamson * 24866b06860SAndy Adamson * @clnt: RPC client to clone 24966b06860SAndy Adamson * @inode: directory inode 25066b06860SAndy Adamson * @name: lookup name 25166b06860SAndy Adamson * 25266b06860SAndy Adamson * Please call rpc_shutdown_client() when you are done with this rpc client. 25366b06860SAndy Adamson */ 25466b06860SAndy Adamson struct rpc_clnt * 25566b06860SAndy Adamson nfs4_negotiate_security(struct rpc_clnt *clnt, struct inode *inode, 256beffb8feSAl Viro const struct qstr *name) 25772de53ecSBryan Schumaker { 25872de53ecSBryan Schumaker struct page *page; 25972de53ecSBryan Schumaker struct nfs4_secinfo_flavors *flavors; 26066b06860SAndy Adamson struct rpc_clnt *new; 26172de53ecSBryan Schumaker int err; 26272de53ecSBryan Schumaker 26372de53ecSBryan Schumaker page = alloc_page(GFP_KERNEL); 26472de53ecSBryan Schumaker if (!page) 26566b06860SAndy Adamson return ERR_PTR(-ENOMEM); 26666b06860SAndy Adamson 26772de53ecSBryan Schumaker flavors = page_address(page); 26872de53ecSBryan Schumaker 26972de53ecSBryan Schumaker err = nfs4_proc_secinfo(inode, name, flavors); 27072de53ecSBryan Schumaker if (err < 0) { 27166b06860SAndy Adamson new = ERR_PTR(err); 27272de53ecSBryan Schumaker goto out; 27372de53ecSBryan Schumaker } 27472de53ecSBryan Schumaker 27566b06860SAndy Adamson new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors); 27672de53ecSBryan Schumaker 27772de53ecSBryan Schumaker out: 27872de53ecSBryan Schumaker put_page(page); 27966b06860SAndy Adamson return new; 28072de53ecSBryan Schumaker } 28172de53ecSBryan Schumaker 282f2aedb71SDavid Howells static int try_location(struct fs_context *fc, 2834ada29d5SJ. Bruce Fields const struct nfs4_fs_location *location) 2844ada29d5SJ. Bruce Fields { 285f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc); 286f2aedb71SDavid Howells unsigned int len, s; 287f2aedb71SDavid Howells char *export_path, *source, *p; 288f2aedb71SDavid Howells int ret = -ENOENT; 2894ada29d5SJ. Bruce Fields 290f2aedb71SDavid Howells /* Allocate a buffer big enough to hold any of the hostnames plus a 291f2aedb71SDavid Howells * terminating char and also a buffer big enough to hold the hostname 292f2aedb71SDavid Howells * plus a colon plus the path. 293f2aedb71SDavid Howells */ 294f2aedb71SDavid Howells len = 0; 295460cdbc8SJ. Bruce Fields for (s = 0; s < location->nservers; s++) { 296ea31a443SJ. Bruce Fields const struct nfs4_string *buf = &location->servers[s]; 297f2aedb71SDavid Howells if (buf->len > len) 298f2aedb71SDavid Howells len = buf->len; 299f2aedb71SDavid Howells } 3004ada29d5SJ. Bruce Fields 301f2aedb71SDavid Howells kfree(ctx->nfs_server.hostname); 302f2aedb71SDavid Howells ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL); 303f2aedb71SDavid Howells if (!ctx->nfs_server.hostname) 304f2aedb71SDavid Howells return -ENOMEM; 305f2aedb71SDavid Howells 306f2aedb71SDavid Howells export_path = nfs4_pathname_string(&location->rootpath, 307f2aedb71SDavid Howells &ctx->nfs_server.export_path_len); 308f2aedb71SDavid Howells if (IS_ERR(export_path)) 309f2aedb71SDavid Howells return PTR_ERR(export_path); 310f2aedb71SDavid Howells 311f2aedb71SDavid Howells ctx->nfs_server.export_path = export_path; 312f2aedb71SDavid Howells 313f2aedb71SDavid Howells source = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1, 314f2aedb71SDavid Howells GFP_KERNEL); 315f2aedb71SDavid Howells if (!source) 316f2aedb71SDavid Howells return -ENOMEM; 317f2aedb71SDavid Howells 318f2aedb71SDavid Howells kfree(fc->source); 319f2aedb71SDavid Howells fc->source = source; 320f2aedb71SDavid Howells for (s = 0; s < location->nservers; s++) { 321f2aedb71SDavid Howells const struct nfs4_string *buf = &location->servers[s]; 3224ada29d5SJ. Bruce Fields 323ea31a443SJ. Bruce Fields if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) 324ea31a443SJ. Bruce Fields continue; 325517be09dSTrond Myklebust 32662a55d08SScott Mayhew ctx->nfs_server.addrlen = 327f2aedb71SDavid Howells nfs_parse_server_name(buf->data, buf->len, 32862a55d08SScott Mayhew &ctx->nfs_server.address, 32962a55d08SScott Mayhew sizeof(ctx->nfs_server._address), 330f2aedb71SDavid Howells fc->net_ns); 33162a55d08SScott Mayhew if (ctx->nfs_server.addrlen == 0) 332ea31a443SJ. Bruce Fields continue; 333517be09dSTrond Myklebust 33462a55d08SScott Mayhew rpc_set_port(&ctx->nfs_server.address, NFS_PORT); 3354ada29d5SJ. Bruce Fields 336f2aedb71SDavid Howells memcpy(ctx->nfs_server.hostname, buf->data, buf->len); 337f2aedb71SDavid Howells ctx->nfs_server.hostname[buf->len] = '\0'; 3384ada29d5SJ. Bruce Fields 339f2aedb71SDavid Howells p = source; 340f2aedb71SDavid Howells memcpy(p, buf->data, buf->len); 341f2aedb71SDavid Howells p += buf->len; 342f2aedb71SDavid Howells *p++ = ':'; 343f2aedb71SDavid Howells memcpy(p, ctx->nfs_server.export_path, ctx->nfs_server.export_path_len); 344f2aedb71SDavid Howells p += ctx->nfs_server.export_path_len; 345f2aedb71SDavid Howells *p = 0; 346f2aedb71SDavid Howells 347f2aedb71SDavid Howells ret = nfs4_get_referral_tree(fc); 348f2aedb71SDavid Howells if (ret == 0) 349f2aedb71SDavid Howells return 0; 3504ada29d5SJ. Bruce Fields } 351f2aedb71SDavid Howells 352f2aedb71SDavid Howells return ret; 3534ada29d5SJ. Bruce Fields } 3544ada29d5SJ. Bruce Fields 355f7b422b1SDavid Howells /** 356f7b422b1SDavid Howells * nfs_follow_referral - set up mountpoint when hitting a referral on moved error 357302fad7bSTrond Myklebust * @dentry: parent directory 358302fad7bSTrond Myklebust * @locations: array of NFSv4 server location information 359f7b422b1SDavid Howells * 360f7b422b1SDavid Howells */ 361f2aedb71SDavid Howells static int nfs_follow_referral(struct fs_context *fc, 362509de811SDavid Howells const struct nfs4_fs_locations *locations) 363f7b422b1SDavid Howells { 364f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc); 3653f43c666SChuck Lever int loc, error; 366f7b422b1SDavid Howells 367f7b422b1SDavid Howells if (locations == NULL || locations->nlocations <= 0) 368f2aedb71SDavid Howells return -ENOENT; 369f7b422b1SDavid Howells 370f2aedb71SDavid Howells dprintk("%s: referral at %pd2\n", __func__, ctx->clone_data.dentry); 371f7b422b1SDavid Howells 37254ceac45SDavid Howells /* Ensure fs path is a prefix of current dentry path */ 373f2aedb71SDavid Howells error = nfs4_validate_fspath(ctx->clone_data.dentry, locations, ctx); 374f2aedb71SDavid Howells if (error < 0) 375f2aedb71SDavid Howells return error; 376f7b422b1SDavid Howells 377f2aedb71SDavid Howells error = -ENOENT; 378460cdbc8SJ. Bruce Fields for (loc = 0; loc < locations->nlocations; loc++) { 379509de811SDavid Howells const struct nfs4_fs_location *location = &locations->locations[loc]; 380f7b422b1SDavid Howells 381f7b422b1SDavid Howells if (location == NULL || location->nservers <= 0 || 382460cdbc8SJ. Bruce Fields location->rootpath.ncomponents == 0) 383f7b422b1SDavid Howells continue; 384f7b422b1SDavid Howells 385f2aedb71SDavid Howells error = try_location(fc, location); 386f2aedb71SDavid Howells if (error == 0) 387f2aedb71SDavid Howells return 0; 388f7b422b1SDavid Howells } 389f7b422b1SDavid Howells 390f2aedb71SDavid Howells return error; 391f7b422b1SDavid Howells } 392f7b422b1SDavid Howells 393f7b422b1SDavid Howells /* 394f7b422b1SDavid Howells * nfs_do_refmount - handle crossing a referral on server 395f7b422b1SDavid Howells * @dentry - dentry of referral 396f7b422b1SDavid Howells * 397f7b422b1SDavid Howells */ 398f2aedb71SDavid Howells static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client) 399f7b422b1SDavid Howells { 400f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc); 401f2aedb71SDavid Howells struct dentry *dentry, *parent; 402f7b422b1SDavid Howells struct nfs4_fs_locations *fs_locations = NULL; 403f7b422b1SDavid Howells struct page *page; 404f2aedb71SDavid Howells int err = -ENOMEM; 405f7b422b1SDavid Howells 406f7b422b1SDavid Howells /* BUG_ON(IS_ROOT(dentry)); */ 407f7b422b1SDavid Howells page = alloc_page(GFP_KERNEL); 408f2aedb71SDavid Howells if (!page) 409f2aedb71SDavid Howells return -ENOMEM; 410f7b422b1SDavid Howells 411f7b422b1SDavid Howells fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); 412f2aedb71SDavid Howells if (!fs_locations) 413f7b422b1SDavid Howells goto out_free; 414f7b422b1SDavid Howells 415f7b422b1SDavid Howells /* Get locations */ 416f2aedb71SDavid Howells dentry = ctx->clone_data.dentry; 417f7b422b1SDavid Howells parent = dget_parent(dentry); 4186de1472fSAl Viro dprintk("%s: getting locations for %pd2\n", 4196de1472fSAl Viro __func__, dentry); 42054ceac45SDavid Howells 4212b0143b5SDavid Howells err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page); 422f7b422b1SDavid Howells dput(parent); 423f2aedb71SDavid Howells if (err != 0) 424f2aedb71SDavid Howells goto out_free_2; 425f7b422b1SDavid Howells 426f2aedb71SDavid Howells err = -ENOENT; 427f2aedb71SDavid Howells if (fs_locations->nlocations <= 0 || 428f2aedb71SDavid Howells fs_locations->fs_path.ncomponents <= 0) 429f2aedb71SDavid Howells goto out_free_2; 430f2aedb71SDavid Howells 431f2aedb71SDavid Howells err = nfs_follow_referral(fc, fs_locations); 432f2aedb71SDavid Howells out_free_2: 433f2aedb71SDavid Howells kfree(fs_locations); 434f7b422b1SDavid Howells out_free: 435f7b422b1SDavid Howells __free_page(page); 436f2aedb71SDavid Howells return err; 437f7b422b1SDavid Howells } 438281cad46SBryan Schumaker 439f2aedb71SDavid Howells int nfs4_submount(struct fs_context *fc, struct nfs_server *server) 440281cad46SBryan Schumaker { 441f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc); 442f2aedb71SDavid Howells struct dentry *dentry = ctx->clone_data.dentry; 443281cad46SBryan Schumaker struct dentry *parent = dget_parent(dentry); 4442b0143b5SDavid Howells struct inode *dir = d_inode(parent); 445beffb8feSAl Viro const struct qstr *name = &dentry->d_name; 446281cad46SBryan Schumaker struct rpc_clnt *client; 447f2aedb71SDavid Howells int ret; 448281cad46SBryan Schumaker 449281cad46SBryan Schumaker /* Look it up again to get its attributes and sec flavor */ 45062a55d08SScott Mayhew client = nfs4_proc_lookup_mountpoint(dir, name, ctx->mntfh, 451f2aedb71SDavid Howells ctx->clone_data.fattr); 452281cad46SBryan Schumaker dput(parent); 453281cad46SBryan Schumaker if (IS_ERR(client)) 454f2aedb71SDavid Howells return PTR_ERR(client); 455281cad46SBryan Schumaker 456f2aedb71SDavid Howells ctx->selected_flavor = client->cl_auth->au_flavor; 457f2aedb71SDavid Howells if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { 458f2aedb71SDavid Howells ret = nfs_do_refmount(fc, client); 459f2aedb71SDavid Howells } else { 460f2aedb71SDavid Howells ret = nfs_do_submount(fc); 46147040da3STrond Myklebust } 462281cad46SBryan Schumaker 463281cad46SBryan Schumaker rpc_shutdown_client(client); 464f2aedb71SDavid Howells return ret; 465281cad46SBryan Schumaker } 466800c06a5SChuck Lever 467800c06a5SChuck Lever /* 468800c06a5SChuck Lever * Try one location from the fs_locations array. 469800c06a5SChuck Lever * 470800c06a5SChuck Lever * Returns zero on success, or a negative errno value. 471800c06a5SChuck Lever */ 472800c06a5SChuck Lever static int nfs4_try_replacing_one_location(struct nfs_server *server, 473800c06a5SChuck Lever char *page, char *page2, 474800c06a5SChuck Lever const struct nfs4_fs_location *location) 475800c06a5SChuck Lever { 476800c06a5SChuck Lever const size_t addr_bufsize = sizeof(struct sockaddr_storage); 477292f503cSTrond Myklebust struct net *net = rpc_net_ns(server->client); 478800c06a5SChuck Lever struct sockaddr *sap; 479800c06a5SChuck Lever unsigned int s; 480800c06a5SChuck Lever size_t salen; 481800c06a5SChuck Lever int error; 482800c06a5SChuck Lever 483800c06a5SChuck Lever sap = kmalloc(addr_bufsize, GFP_KERNEL); 484800c06a5SChuck Lever if (sap == NULL) 485800c06a5SChuck Lever return -ENOMEM; 486800c06a5SChuck Lever 487800c06a5SChuck Lever error = -ENOENT; 488800c06a5SChuck Lever for (s = 0; s < location->nservers; s++) { 489800c06a5SChuck Lever const struct nfs4_string *buf = &location->servers[s]; 490800c06a5SChuck Lever char *hostname; 491800c06a5SChuck Lever 492800c06a5SChuck Lever if (buf->len <= 0 || buf->len > PAGE_SIZE) 493800c06a5SChuck Lever continue; 494800c06a5SChuck Lever 495800c06a5SChuck Lever if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL) 496800c06a5SChuck Lever continue; 497800c06a5SChuck Lever 498800c06a5SChuck Lever salen = nfs_parse_server_name(buf->data, buf->len, 499292f503cSTrond Myklebust sap, addr_bufsize, net); 500800c06a5SChuck Lever if (salen == 0) 501800c06a5SChuck Lever continue; 502800c06a5SChuck Lever rpc_set_port(sap, NFS_PORT); 503800c06a5SChuck Lever 504800c06a5SChuck Lever error = -ENOMEM; 505800c06a5SChuck Lever hostname = kstrndup(buf->data, buf->len, GFP_KERNEL); 506800c06a5SChuck Lever if (hostname == NULL) 507800c06a5SChuck Lever break; 508800c06a5SChuck Lever 509292f503cSTrond Myklebust error = nfs4_update_server(server, hostname, sap, salen, net); 510800c06a5SChuck Lever kfree(hostname); 511800c06a5SChuck Lever if (error == 0) 512800c06a5SChuck Lever break; 513800c06a5SChuck Lever } 514800c06a5SChuck Lever 515800c06a5SChuck Lever kfree(sap); 516800c06a5SChuck Lever return error; 517800c06a5SChuck Lever } 518800c06a5SChuck Lever 519800c06a5SChuck Lever /** 520800c06a5SChuck Lever * nfs4_replace_transport - set up transport to destination server 521800c06a5SChuck Lever * 522800c06a5SChuck Lever * @server: export being migrated 523800c06a5SChuck Lever * @locations: fs_locations array 524800c06a5SChuck Lever * 525800c06a5SChuck Lever * Returns zero on success, or a negative errno value. 526800c06a5SChuck Lever * 527800c06a5SChuck Lever * The client tries all the entries in the "locations" array, in the 528800c06a5SChuck Lever * order returned by the server, until one works or the end of the 529800c06a5SChuck Lever * array is reached. 530800c06a5SChuck Lever */ 531800c06a5SChuck Lever int nfs4_replace_transport(struct nfs_server *server, 532800c06a5SChuck Lever const struct nfs4_fs_locations *locations) 533800c06a5SChuck Lever { 534800c06a5SChuck Lever char *page = NULL, *page2 = NULL; 535800c06a5SChuck Lever int loc, error; 536800c06a5SChuck Lever 537800c06a5SChuck Lever error = -ENOENT; 538800c06a5SChuck Lever if (locations == NULL || locations->nlocations <= 0) 539800c06a5SChuck Lever goto out; 540800c06a5SChuck Lever 541800c06a5SChuck Lever error = -ENOMEM; 542800c06a5SChuck Lever page = (char *) __get_free_page(GFP_USER); 543800c06a5SChuck Lever if (!page) 544800c06a5SChuck Lever goto out; 545800c06a5SChuck Lever page2 = (char *) __get_free_page(GFP_USER); 546800c06a5SChuck Lever if (!page2) 547800c06a5SChuck Lever goto out; 548800c06a5SChuck Lever 549800c06a5SChuck Lever for (loc = 0; loc < locations->nlocations; loc++) { 550800c06a5SChuck Lever const struct nfs4_fs_location *location = 551800c06a5SChuck Lever &locations->locations[loc]; 552800c06a5SChuck Lever 553800c06a5SChuck Lever if (location == NULL || location->nservers <= 0 || 554800c06a5SChuck Lever location->rootpath.ncomponents == 0) 555800c06a5SChuck Lever continue; 556800c06a5SChuck Lever 557800c06a5SChuck Lever error = nfs4_try_replacing_one_location(server, page, 558800c06a5SChuck Lever page2, location); 559800c06a5SChuck Lever if (error == 0) 560800c06a5SChuck Lever break; 561800c06a5SChuck Lever } 562800c06a5SChuck Lever 563800c06a5SChuck Lever out: 564800c06a5SChuck Lever free_page((unsigned long)page); 565800c06a5SChuck Lever free_page((unsigned long)page2); 566800c06a5SChuck Lever return error; 567800c06a5SChuck Lever } 568