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 11f7b422b1SDavid Howells #include <linux/dcache.h> 12f7b422b1SDavid Howells #include <linux/mount.h> 13f7b422b1SDavid Howells #include <linux/namei.h> 14f7b422b1SDavid Howells #include <linux/nfs_fs.h> 1547040da3STrond Myklebust #include <linux/nfs_mount.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 17f7b422b1SDavid Howells #include <linux/string.h> 18f7b422b1SDavid Howells #include <linux/sunrpc/clnt.h> 195976687aSJeff Layton #include <linux/sunrpc/addr.h> 20f7b422b1SDavid Howells #include <linux/vfs.h> 21f7b422b1SDavid Howells #include <linux/inet.h> 22f7b422b1SDavid Howells #include "internal.h" 23c228fd3aSTrond Myklebust #include "nfs4_fs.h" 247d7ea882STrond Myklebust #include "dns_resolve.h" 25f7b422b1SDavid Howells 26f7b422b1SDavid Howells #define NFSDBG_FACILITY NFSDBG_VFS 27f7b422b1SDavid Howells 28f7b422b1SDavid Howells /* 29ef95d31eSTrond Myklebust * Convert the NFSv4 pathname components into a standard posix path. 30ef95d31eSTrond Myklebust * 31ef95d31eSTrond Myklebust * Note that the resulting string will be placed at the end of the buffer 32f7b422b1SDavid Howells */ 33509de811SDavid Howells static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname, 34f7b422b1SDavid Howells char *buffer, ssize_t buflen) 35f7b422b1SDavid Howells { 36f7b422b1SDavid Howells char *end = buffer + buflen; 37f7b422b1SDavid Howells int n; 38f7b422b1SDavid Howells 39f7b422b1SDavid Howells *--end = '\0'; 40f7b422b1SDavid Howells buflen--; 41f7b422b1SDavid Howells 42f7b422b1SDavid Howells n = pathname->ncomponents; 43f7b422b1SDavid Howells while (--n >= 0) { 44509de811SDavid Howells const struct nfs4_string *component = &pathname->components[n]; 45f7b422b1SDavid Howells buflen -= component->len + 1; 46f7b422b1SDavid Howells if (buflen < 0) 47f7b422b1SDavid Howells goto Elong; 48f7b422b1SDavid Howells end -= component->len; 49f7b422b1SDavid Howells memcpy(end, component->data, component->len); 50f7b422b1SDavid Howells *--end = '/'; 51f7b422b1SDavid Howells } 52f7b422b1SDavid Howells return end; 53f7b422b1SDavid Howells Elong: 54f7b422b1SDavid Howells return ERR_PTR(-ENAMETOOLONG); 55f7b422b1SDavid Howells } 56f7b422b1SDavid Howells 5754ceac45SDavid Howells /* 581aba1567SWeston Andros Adamson * return the path component of "<server>:<path>" 591aba1567SWeston Andros Adamson * nfspath - the "<server>:<path>" string 601aba1567SWeston Andros Adamson * end - one past the last char that could contain "<server>:" 611aba1567SWeston Andros Adamson * returns NULL on failure 621aba1567SWeston Andros Adamson */ 631aba1567SWeston Andros Adamson static char *nfs_path_component(const char *nfspath, const char *end) 641aba1567SWeston Andros Adamson { 651aba1567SWeston Andros Adamson char *p; 661aba1567SWeston Andros Adamson 671aba1567SWeston Andros Adamson if (*nfspath == '[') { 681aba1567SWeston Andros Adamson /* parse [] escaped IPv6 addrs */ 691aba1567SWeston Andros Adamson p = strchr(nfspath, ']'); 701aba1567SWeston Andros Adamson if (p != NULL && ++p < end && *p == ':') 711aba1567SWeston Andros Adamson return p + 1; 721aba1567SWeston Andros Adamson } else { 731aba1567SWeston Andros Adamson /* otherwise split on first colon */ 741aba1567SWeston Andros Adamson p = strchr(nfspath, ':'); 751aba1567SWeston Andros Adamson if (p != NULL && p < end) 761aba1567SWeston Andros Adamson return p + 1; 771aba1567SWeston Andros Adamson } 781aba1567SWeston Andros Adamson return NULL; 791aba1567SWeston Andros Adamson } 801aba1567SWeston Andros Adamson 811aba1567SWeston Andros Adamson /* 8254ceac45SDavid Howells * Determine the mount path as a string 8354ceac45SDavid Howells */ 84b514f872SAl Viro static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) 8554ceac45SDavid Howells { 86b514f872SAl Viro char *limit; 8797a54868SBen Hutchings char *path = nfs_path(&limit, dentry, buffer, buflen, 8897a54868SBen Hutchings NFS_PATH_CANONICAL); 89b514f872SAl Viro if (!IS_ERR(path)) { 901aba1567SWeston Andros Adamson char *path_component = nfs_path_component(path, limit); 911aba1567SWeston Andros Adamson if (path_component) 921aba1567SWeston Andros Adamson return path_component; 93b514f872SAl Viro } 94b514f872SAl Viro return path; 9554ceac45SDavid Howells } 9654ceac45SDavid Howells 9754ceac45SDavid Howells /* 9854ceac45SDavid Howells * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we 9954ceac45SDavid Howells * believe to be the server path to this dentry 10054ceac45SDavid Howells */ 101b514f872SAl Viro static int nfs4_validate_fspath(struct dentry *dentry, 10254ceac45SDavid Howells const struct nfs4_fs_locations *locations, 10354ceac45SDavid Howells char *page, char *page2) 10454ceac45SDavid Howells { 10554ceac45SDavid Howells const char *path, *fs_path; 10654ceac45SDavid Howells 107b514f872SAl Viro path = nfs4_path(dentry, page, PAGE_SIZE); 10854ceac45SDavid Howells if (IS_ERR(path)) 10954ceac45SDavid Howells return PTR_ERR(path); 11054ceac45SDavid Howells 11154ceac45SDavid Howells fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); 11254ceac45SDavid Howells if (IS_ERR(fs_path)) 11354ceac45SDavid Howells return PTR_ERR(fs_path); 11454ceac45SDavid Howells 11554ceac45SDavid Howells if (strncmp(path, fs_path, strlen(fs_path)) != 0) { 11654ceac45SDavid Howells dprintk("%s: path %s does not begin with fsroot %s\n", 1173110ff80SHarvey Harrison __func__, path, fs_path); 11854ceac45SDavid Howells return -ENOENT; 11954ceac45SDavid Howells } 12054ceac45SDavid Howells 12154ceac45SDavid Howells return 0; 12254ceac45SDavid Howells } 12354ceac45SDavid Howells 1247d7ea882STrond Myklebust static size_t nfs_parse_server_name(char *string, size_t len, 125292f503cSTrond Myklebust struct sockaddr *sa, size_t salen, struct net *net) 1267d7ea882STrond Myklebust { 1277d7ea882STrond Myklebust ssize_t ret; 1287d7ea882STrond Myklebust 12933faaa38SStanislav Kinsbursky ret = rpc_pton(net, string, len, sa, salen); 1307d7ea882STrond Myklebust if (ret == 0) { 13133faaa38SStanislav Kinsbursky ret = nfs_dns_resolve_name(net, string, len, sa, salen); 1327d7ea882STrond Myklebust if (ret < 0) 1337d7ea882STrond Myklebust ret = 0; 1347d7ea882STrond Myklebust } 1357d7ea882STrond Myklebust return ret; 1367d7ea882STrond Myklebust } 1377d7ea882STrond Myklebust 1389568c5e9SChuck Lever /** 1399568c5e9SChuck Lever * nfs_find_best_sec - Find a security mechanism supported locally 140302fad7bSTrond Myklebust * @clnt: pointer to rpc_clnt 1414d4b69ddSWeston Andros Adamson * @server: NFS server struct 1429568c5e9SChuck Lever * @flavors: List of security tuples returned by SECINFO procedure 1439568c5e9SChuck Lever * 14466b06860SAndy Adamson * Return an rpc client that uses the first security mechanism in 1458445cd35SAndy Adamson * "flavors" that is locally supported. The "flavors" array 1469568c5e9SChuck Lever * is searched in the order returned from the server, per RFC 3530 14766b06860SAndy Adamson * recommendation and each flavor is checked for membership in the 14866b06860SAndy Adamson * sec= mount option list if it exists. 1498445cd35SAndy Adamson * 1508445cd35SAndy Adamson * Return -EPERM if no matching flavor is found in the array. 15166b06860SAndy Adamson * 15266b06860SAndy Adamson * Please call rpc_shutdown_client() when you are done with this rpc client. 15366b06860SAndy Adamson * 1549568c5e9SChuck Lever */ 15566b06860SAndy Adamson static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt, 15666b06860SAndy Adamson struct nfs_server *server, 1574d4b69ddSWeston Andros Adamson struct nfs4_secinfo_flavors *flavors) 1582671bfc3SBryan Schumaker { 15966b06860SAndy Adamson rpc_authflavor_t pflavor; 1609568c5e9SChuck Lever struct nfs4_secinfo4 *secinfo; 161fb15b26fSChuck Lever unsigned int i; 1622671bfc3SBryan Schumaker 1632671bfc3SBryan Schumaker for (i = 0; i < flavors->num_flavors; i++) { 1649568c5e9SChuck Lever secinfo = &flavors->flavors[i]; 1652671bfc3SBryan Schumaker 1669568c5e9SChuck Lever switch (secinfo->flavor) { 1679568c5e9SChuck Lever case RPC_AUTH_NULL: 1689568c5e9SChuck Lever case RPC_AUTH_UNIX: 1699568c5e9SChuck Lever case RPC_AUTH_GSS: 17066b06860SAndy Adamson pflavor = rpcauth_get_pseudoflavor(secinfo->flavor, 1719568c5e9SChuck Lever &secinfo->flavor_info); 17266b06860SAndy Adamson /* does the pseudoflavor match a sec= mount opt? */ 17366b06860SAndy Adamson if (pflavor != RPC_AUTH_MAXFLAVOR && 17466b06860SAndy Adamson nfs_auth_info_match(&server->auth_info, pflavor)) { 17566b06860SAndy Adamson struct rpc_clnt *new; 17666b06860SAndy Adamson struct rpc_cred *cred; 17766b06860SAndy Adamson 17866b06860SAndy Adamson /* Cloning creates an rpc_auth for the flavor */ 17966b06860SAndy Adamson new = rpc_clone_client_set_auth(clnt, pflavor); 18066b06860SAndy Adamson if (IS_ERR(new)) 18166b06860SAndy Adamson continue; 18266b06860SAndy Adamson /** 18366b06860SAndy Adamson * Check that the user actually can use the 18466b06860SAndy Adamson * flavor. This is mostly for RPC_AUTH_GSS 18566b06860SAndy Adamson * where cr_init obtains a gss context 18666b06860SAndy Adamson */ 18766b06860SAndy Adamson cred = rpcauth_lookupcred(new->cl_auth, 0); 18866b06860SAndy Adamson if (IS_ERR(cred)) { 18966b06860SAndy Adamson rpc_shutdown_client(new); 19066b06860SAndy Adamson continue; 1919568c5e9SChuck Lever } 19266b06860SAndy Adamson put_rpccred(cred); 19366b06860SAndy Adamson return new; 19466b06860SAndy Adamson } 19566b06860SAndy Adamson } 19666b06860SAndy Adamson } 19766b06860SAndy Adamson return ERR_PTR(-EPERM); 1989568c5e9SChuck Lever } 1999568c5e9SChuck Lever 20066b06860SAndy Adamson /** 20166b06860SAndy Adamson * nfs4_negotiate_security - in response to an NFS4ERR_WRONGSEC on lookup, 20266b06860SAndy Adamson * return an rpc_clnt that uses the best available security flavor with 20366b06860SAndy Adamson * respect to the secinfo flavor list and the sec= mount options. 20466b06860SAndy Adamson * 20566b06860SAndy Adamson * @clnt: RPC client to clone 20666b06860SAndy Adamson * @inode: directory inode 20766b06860SAndy Adamson * @name: lookup name 20866b06860SAndy Adamson * 20966b06860SAndy Adamson * Please call rpc_shutdown_client() when you are done with this rpc client. 21066b06860SAndy Adamson */ 21166b06860SAndy Adamson struct rpc_clnt * 21266b06860SAndy Adamson nfs4_negotiate_security(struct rpc_clnt *clnt, struct inode *inode, 213beffb8feSAl Viro const struct qstr *name) 21472de53ecSBryan Schumaker { 21572de53ecSBryan Schumaker struct page *page; 21672de53ecSBryan Schumaker struct nfs4_secinfo_flavors *flavors; 21766b06860SAndy Adamson struct rpc_clnt *new; 21872de53ecSBryan Schumaker int err; 21972de53ecSBryan Schumaker 22072de53ecSBryan Schumaker page = alloc_page(GFP_KERNEL); 22172de53ecSBryan Schumaker if (!page) 22266b06860SAndy Adamson return ERR_PTR(-ENOMEM); 22366b06860SAndy Adamson 22472de53ecSBryan Schumaker flavors = page_address(page); 22572de53ecSBryan Schumaker 22672de53ecSBryan Schumaker err = nfs4_proc_secinfo(inode, name, flavors); 22772de53ecSBryan Schumaker if (err < 0) { 22866b06860SAndy Adamson new = ERR_PTR(err); 22972de53ecSBryan Schumaker goto out; 23072de53ecSBryan Schumaker } 23172de53ecSBryan Schumaker 23266b06860SAndy Adamson new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors); 23372de53ecSBryan Schumaker 23472de53ecSBryan Schumaker out: 23572de53ecSBryan Schumaker put_page(page); 23666b06860SAndy Adamson return new; 23772de53ecSBryan Schumaker } 23872de53ecSBryan Schumaker 2394ada29d5SJ. Bruce Fields static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, 2404ada29d5SJ. Bruce Fields char *page, char *page2, 2414ada29d5SJ. Bruce Fields const struct nfs4_fs_location *location) 2424ada29d5SJ. Bruce Fields { 243364d015eSTrond Myklebust const size_t addr_bufsize = sizeof(struct sockaddr_storage); 244292f503cSTrond Myklebust struct net *net = rpc_net_ns(NFS_SB(mountdata->sb)->client); 2454ada29d5SJ. Bruce Fields struct vfsmount *mnt = ERR_PTR(-ENOENT); 2464ada29d5SJ. Bruce Fields char *mnt_path; 247ef95d31eSTrond Myklebust unsigned int maxbuflen; 248460cdbc8SJ. Bruce Fields unsigned int s; 2494ada29d5SJ. Bruce Fields 2504ada29d5SJ. Bruce Fields mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); 2514ada29d5SJ. Bruce Fields if (IS_ERR(mnt_path)) 252517be09dSTrond Myklebust return ERR_CAST(mnt_path); 2534ada29d5SJ. Bruce Fields mountdata->mnt_path = mnt_path; 254ef95d31eSTrond Myklebust maxbuflen = mnt_path - 1 - page2; 2554ada29d5SJ. Bruce Fields 256364d015eSTrond Myklebust mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL); 257364d015eSTrond Myklebust if (mountdata->addr == NULL) 258364d015eSTrond Myklebust return ERR_PTR(-ENOMEM); 259364d015eSTrond Myklebust 260460cdbc8SJ. Bruce Fields for (s = 0; s < location->nservers; s++) { 261ea31a443SJ. Bruce Fields const struct nfs4_string *buf = &location->servers[s]; 2624ada29d5SJ. Bruce Fields 263ef95d31eSTrond Myklebust if (buf->len <= 0 || buf->len >= maxbuflen) 2644ada29d5SJ. Bruce Fields continue; 2654ada29d5SJ. Bruce Fields 266ea31a443SJ. Bruce Fields if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) 267ea31a443SJ. Bruce Fields continue; 268517be09dSTrond Myklebust 269517be09dSTrond Myklebust mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, 270292f503cSTrond Myklebust mountdata->addr, addr_bufsize, net); 27153a0b9c4SChuck Lever if (mountdata->addrlen == 0) 272ea31a443SJ. Bruce Fields continue; 273517be09dSTrond Myklebust 274ef95d31eSTrond Myklebust memcpy(page2, buf->data, buf->len); 275ef95d31eSTrond Myklebust page2[buf->len] = '\0'; 276ea31a443SJ. Bruce Fields mountdata->hostname = page2; 2774ada29d5SJ. Bruce Fields 2784ada29d5SJ. Bruce Fields snprintf(page, PAGE_SIZE, "%s:%s", 2794ada29d5SJ. Bruce Fields mountdata->hostname, 2804ada29d5SJ. Bruce Fields mountdata->mnt_path); 2814ada29d5SJ. Bruce Fields 28293faccbbSEric W. Biederman mnt = vfs_submount(mountdata->dentry, &nfs4_referral_fs_type, page, mountdata); 2834ada29d5SJ. Bruce Fields if (!IS_ERR(mnt)) 2844ada29d5SJ. Bruce Fields break; 2854ada29d5SJ. Bruce Fields } 286364d015eSTrond Myklebust kfree(mountdata->addr); 2874ada29d5SJ. Bruce Fields return mnt; 2884ada29d5SJ. Bruce Fields } 2894ada29d5SJ. Bruce Fields 290f7b422b1SDavid Howells /** 291f7b422b1SDavid Howells * nfs_follow_referral - set up mountpoint when hitting a referral on moved error 292302fad7bSTrond Myklebust * @dentry: parent directory 293302fad7bSTrond Myklebust * @locations: array of NFSv4 server location information 294f7b422b1SDavid Howells * 295f7b422b1SDavid Howells */ 296f8ad9c4bSAl Viro static struct vfsmount *nfs_follow_referral(struct dentry *dentry, 297509de811SDavid Howells const struct nfs4_fs_locations *locations) 298f7b422b1SDavid Howells { 299f7b422b1SDavid Howells struct vfsmount *mnt = ERR_PTR(-ENOENT); 300f7b422b1SDavid Howells struct nfs_clone_mount mountdata = { 301f8ad9c4bSAl Viro .sb = dentry->d_sb, 302f7b422b1SDavid Howells .dentry = dentry, 303f8ad9c4bSAl Viro .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor, 304f7b422b1SDavid Howells }; 30554ceac45SDavid Howells char *page = NULL, *page2 = NULL; 3063f43c666SChuck Lever int loc, error; 307f7b422b1SDavid Howells 308f7b422b1SDavid Howells if (locations == NULL || locations->nlocations <= 0) 309f7b422b1SDavid Howells goto out; 310f7b422b1SDavid Howells 3116de1472fSAl Viro dprintk("%s: referral at %pd2\n", __func__, dentry); 312f7b422b1SDavid Howells 313f7b422b1SDavid Howells page = (char *) __get_free_page(GFP_USER); 31454ceac45SDavid Howells if (!page) 315f7b422b1SDavid Howells goto out; 31654ceac45SDavid Howells 317f7b422b1SDavid Howells page2 = (char *) __get_free_page(GFP_USER); 31854ceac45SDavid Howells if (!page2) 319f7b422b1SDavid Howells goto out; 320f7b422b1SDavid Howells 32154ceac45SDavid Howells /* Ensure fs path is a prefix of current dentry path */ 322b514f872SAl Viro error = nfs4_validate_fspath(dentry, locations, page, page2); 32354ceac45SDavid Howells if (error < 0) { 32454ceac45SDavid Howells mnt = ERR_PTR(error); 32554ceac45SDavid Howells goto out; 326f7b422b1SDavid Howells } 327f7b422b1SDavid Howells 328460cdbc8SJ. Bruce Fields for (loc = 0; loc < locations->nlocations; loc++) { 329509de811SDavid Howells const struct nfs4_fs_location *location = &locations->locations[loc]; 330f7b422b1SDavid Howells 331f7b422b1SDavid Howells if (location == NULL || location->nservers <= 0 || 332460cdbc8SJ. Bruce Fields location->rootpath.ncomponents == 0) 333f7b422b1SDavid Howells continue; 334f7b422b1SDavid Howells 3354ada29d5SJ. Bruce Fields mnt = try_location(&mountdata, page, page2, location); 3364ada29d5SJ. Bruce Fields if (!IS_ERR(mnt)) 337f7b422b1SDavid Howells break; 338f7b422b1SDavid Howells } 339f7b422b1SDavid Howells 34054ceac45SDavid Howells out: 341f7b422b1SDavid Howells free_page((unsigned long) page); 342f7b422b1SDavid Howells free_page((unsigned long) page2); 343f7b422b1SDavid Howells return mnt; 344f7b422b1SDavid Howells } 345f7b422b1SDavid Howells 346f7b422b1SDavid Howells /* 347f7b422b1SDavid Howells * nfs_do_refmount - handle crossing a referral on server 348f7b422b1SDavid Howells * @dentry - dentry of referral 349f7b422b1SDavid Howells * 350f7b422b1SDavid Howells */ 351281cad46SBryan Schumaker static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) 352f7b422b1SDavid Howells { 35354ceac45SDavid Howells struct vfsmount *mnt = ERR_PTR(-ENOMEM); 354f7b422b1SDavid Howells struct dentry *parent; 355f7b422b1SDavid Howells struct nfs4_fs_locations *fs_locations = NULL; 356f7b422b1SDavid Howells struct page *page; 357f7b422b1SDavid Howells int err; 358f7b422b1SDavid Howells 359f7b422b1SDavid Howells /* BUG_ON(IS_ROOT(dentry)); */ 360f7b422b1SDavid Howells page = alloc_page(GFP_KERNEL); 361f7b422b1SDavid Howells if (page == NULL) 3623183783bSAnna Schumaker return mnt; 363f7b422b1SDavid Howells 364f7b422b1SDavid Howells fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); 365f7b422b1SDavid Howells if (fs_locations == NULL) 366f7b422b1SDavid Howells goto out_free; 367f7b422b1SDavid Howells 368f7b422b1SDavid Howells /* Get locations */ 36954ceac45SDavid Howells mnt = ERR_PTR(-ENOENT); 37054ceac45SDavid Howells 371f7b422b1SDavid Howells parent = dget_parent(dentry); 3726de1472fSAl Viro dprintk("%s: getting locations for %pd2\n", 3736de1472fSAl Viro __func__, dentry); 37454ceac45SDavid Howells 3752b0143b5SDavid Howells err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page); 376f7b422b1SDavid Howells dput(parent); 37754ceac45SDavid Howells if (err != 0 || 37854ceac45SDavid Howells fs_locations->nlocations <= 0 || 379f7b422b1SDavid Howells fs_locations->fs_path.ncomponents <= 0) 380f7b422b1SDavid Howells goto out_free; 381f7b422b1SDavid Howells 382f8ad9c4bSAl Viro mnt = nfs_follow_referral(dentry, fs_locations); 383f7b422b1SDavid Howells out_free: 384f7b422b1SDavid Howells __free_page(page); 385f7b422b1SDavid Howells kfree(fs_locations); 386f7b422b1SDavid Howells return mnt; 387f7b422b1SDavid Howells } 388281cad46SBryan Schumaker 389281cad46SBryan Schumaker struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry, 390281cad46SBryan Schumaker struct nfs_fh *fh, struct nfs_fattr *fattr) 391281cad46SBryan Schumaker { 39247040da3STrond Myklebust rpc_authflavor_t flavor = server->client->cl_auth->au_flavor; 393281cad46SBryan Schumaker struct dentry *parent = dget_parent(dentry); 3942b0143b5SDavid Howells struct inode *dir = d_inode(parent); 395beffb8feSAl Viro const struct qstr *name = &dentry->d_name; 396281cad46SBryan Schumaker struct rpc_clnt *client; 397281cad46SBryan Schumaker struct vfsmount *mnt; 398281cad46SBryan Schumaker 399281cad46SBryan Schumaker /* Look it up again to get its attributes and sec flavor */ 40047040da3STrond Myklebust client = nfs4_proc_lookup_mountpoint(dir, name, fh, fattr); 401281cad46SBryan Schumaker dput(parent); 402281cad46SBryan Schumaker if (IS_ERR(client)) 403281cad46SBryan Schumaker return ERR_CAST(client); 404281cad46SBryan Schumaker 40547040da3STrond Myklebust if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { 406281cad46SBryan Schumaker mnt = nfs_do_refmount(client, dentry); 40747040da3STrond Myklebust goto out; 40847040da3STrond Myklebust } 409281cad46SBryan Schumaker 41047040da3STrond Myklebust if (client->cl_auth->au_flavor != flavor) 41147040da3STrond Myklebust flavor = client->cl_auth->au_flavor; 41247040da3STrond Myklebust mnt = nfs_do_submount(dentry, fh, fattr, flavor); 41347040da3STrond Myklebust out: 414281cad46SBryan Schumaker rpc_shutdown_client(client); 415281cad46SBryan Schumaker return mnt; 416281cad46SBryan Schumaker } 417800c06a5SChuck Lever 418800c06a5SChuck Lever /* 419800c06a5SChuck Lever * Try one location from the fs_locations array. 420800c06a5SChuck Lever * 421800c06a5SChuck Lever * Returns zero on success, or a negative errno value. 422800c06a5SChuck Lever */ 423800c06a5SChuck Lever static int nfs4_try_replacing_one_location(struct nfs_server *server, 424800c06a5SChuck Lever char *page, char *page2, 425800c06a5SChuck Lever const struct nfs4_fs_location *location) 426800c06a5SChuck Lever { 427800c06a5SChuck Lever const size_t addr_bufsize = sizeof(struct sockaddr_storage); 428292f503cSTrond Myklebust struct net *net = rpc_net_ns(server->client); 429800c06a5SChuck Lever struct sockaddr *sap; 430800c06a5SChuck Lever unsigned int s; 431800c06a5SChuck Lever size_t salen; 432800c06a5SChuck Lever int error; 433800c06a5SChuck Lever 434800c06a5SChuck Lever sap = kmalloc(addr_bufsize, GFP_KERNEL); 435800c06a5SChuck Lever if (sap == NULL) 436800c06a5SChuck Lever return -ENOMEM; 437800c06a5SChuck Lever 438800c06a5SChuck Lever error = -ENOENT; 439800c06a5SChuck Lever for (s = 0; s < location->nservers; s++) { 440800c06a5SChuck Lever const struct nfs4_string *buf = &location->servers[s]; 441800c06a5SChuck Lever char *hostname; 442800c06a5SChuck Lever 443800c06a5SChuck Lever if (buf->len <= 0 || buf->len > PAGE_SIZE) 444800c06a5SChuck Lever continue; 445800c06a5SChuck Lever 446800c06a5SChuck Lever if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL) 447800c06a5SChuck Lever continue; 448800c06a5SChuck Lever 449800c06a5SChuck Lever salen = nfs_parse_server_name(buf->data, buf->len, 450292f503cSTrond Myklebust sap, addr_bufsize, net); 451800c06a5SChuck Lever if (salen == 0) 452800c06a5SChuck Lever continue; 453800c06a5SChuck Lever rpc_set_port(sap, NFS_PORT); 454800c06a5SChuck Lever 455800c06a5SChuck Lever error = -ENOMEM; 456800c06a5SChuck Lever hostname = kstrndup(buf->data, buf->len, GFP_KERNEL); 457800c06a5SChuck Lever if (hostname == NULL) 458800c06a5SChuck Lever break; 459800c06a5SChuck Lever 460292f503cSTrond Myklebust error = nfs4_update_server(server, hostname, sap, salen, net); 461800c06a5SChuck Lever kfree(hostname); 462800c06a5SChuck Lever if (error == 0) 463800c06a5SChuck Lever break; 464800c06a5SChuck Lever } 465800c06a5SChuck Lever 466800c06a5SChuck Lever kfree(sap); 467800c06a5SChuck Lever return error; 468800c06a5SChuck Lever } 469800c06a5SChuck Lever 470800c06a5SChuck Lever /** 471800c06a5SChuck Lever * nfs4_replace_transport - set up transport to destination server 472800c06a5SChuck Lever * 473800c06a5SChuck Lever * @server: export being migrated 474800c06a5SChuck Lever * @locations: fs_locations array 475800c06a5SChuck Lever * 476800c06a5SChuck Lever * Returns zero on success, or a negative errno value. 477800c06a5SChuck Lever * 478800c06a5SChuck Lever * The client tries all the entries in the "locations" array, in the 479800c06a5SChuck Lever * order returned by the server, until one works or the end of the 480800c06a5SChuck Lever * array is reached. 481800c06a5SChuck Lever */ 482800c06a5SChuck Lever int nfs4_replace_transport(struct nfs_server *server, 483800c06a5SChuck Lever const struct nfs4_fs_locations *locations) 484800c06a5SChuck Lever { 485800c06a5SChuck Lever char *page = NULL, *page2 = NULL; 486800c06a5SChuck Lever int loc, error; 487800c06a5SChuck Lever 488800c06a5SChuck Lever error = -ENOENT; 489800c06a5SChuck Lever if (locations == NULL || locations->nlocations <= 0) 490800c06a5SChuck Lever goto out; 491800c06a5SChuck Lever 492800c06a5SChuck Lever error = -ENOMEM; 493800c06a5SChuck Lever page = (char *) __get_free_page(GFP_USER); 494800c06a5SChuck Lever if (!page) 495800c06a5SChuck Lever goto out; 496800c06a5SChuck Lever page2 = (char *) __get_free_page(GFP_USER); 497800c06a5SChuck Lever if (!page2) 498800c06a5SChuck Lever goto out; 499800c06a5SChuck Lever 500800c06a5SChuck Lever for (loc = 0; loc < locations->nlocations; loc++) { 501800c06a5SChuck Lever const struct nfs4_fs_location *location = 502800c06a5SChuck Lever &locations->locations[loc]; 503800c06a5SChuck Lever 504800c06a5SChuck Lever if (location == NULL || location->nservers <= 0 || 505800c06a5SChuck Lever location->rootpath.ncomponents == 0) 506800c06a5SChuck Lever continue; 507800c06a5SChuck Lever 508800c06a5SChuck Lever error = nfs4_try_replacing_one_location(server, page, 509800c06a5SChuck Lever page2, location); 510800c06a5SChuck Lever if (error == 0) 511800c06a5SChuck Lever break; 512800c06a5SChuck Lever } 513800c06a5SChuck Lever 514800c06a5SChuck Lever out: 515800c06a5SChuck Lever free_page((unsigned long)page); 516800c06a5SChuck Lever free_page((unsigned long)page2); 517800c06a5SChuck Lever return error; 518800c06a5SChuck Lever } 519