xref: /openbmc/linux/fs/nfs/nfs4namespace.c (revision 302fad7b)
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