xref: /openbmc/linux/fs/nfs/nfs4namespace.c (revision 3cab1854)
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
3573cab1854STrond Myklebust  * @fc: pointer to struct nfs_fs_context
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);
445281cad46SBryan Schumaker 	struct rpc_clnt *client;
446f2aedb71SDavid Howells 	int ret;
447281cad46SBryan Schumaker 
448281cad46SBryan Schumaker 	/* Look it up again to get its attributes and sec flavor */
449f7b37b8bSTrond Myklebust 	client = nfs4_proc_lookup_mountpoint(dir, dentry, ctx->mntfh,
450f2aedb71SDavid Howells 					     ctx->clone_data.fattr);
451281cad46SBryan Schumaker 	dput(parent);
452281cad46SBryan Schumaker 	if (IS_ERR(client))
453f2aedb71SDavid Howells 		return PTR_ERR(client);
454281cad46SBryan Schumaker 
455f2aedb71SDavid Howells 	ctx->selected_flavor = client->cl_auth->au_flavor;
456f2aedb71SDavid Howells 	if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
457f2aedb71SDavid Howells 		ret = nfs_do_refmount(fc, client);
458f2aedb71SDavid Howells 	} else {
459f2aedb71SDavid Howells 		ret = nfs_do_submount(fc);
46047040da3STrond Myklebust 	}
461281cad46SBryan Schumaker 
462281cad46SBryan Schumaker 	rpc_shutdown_client(client);
463f2aedb71SDavid Howells 	return ret;
464281cad46SBryan Schumaker }
465800c06a5SChuck Lever 
466800c06a5SChuck Lever /*
467800c06a5SChuck Lever  * Try one location from the fs_locations array.
468800c06a5SChuck Lever  *
469800c06a5SChuck Lever  * Returns zero on success, or a negative errno value.
470800c06a5SChuck Lever  */
471800c06a5SChuck Lever static int nfs4_try_replacing_one_location(struct nfs_server *server,
472800c06a5SChuck Lever 		char *page, char *page2,
473800c06a5SChuck Lever 		const struct nfs4_fs_location *location)
474800c06a5SChuck Lever {
475800c06a5SChuck Lever 	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
476292f503cSTrond Myklebust 	struct net *net = rpc_net_ns(server->client);
477800c06a5SChuck Lever 	struct sockaddr *sap;
478800c06a5SChuck Lever 	unsigned int s;
479800c06a5SChuck Lever 	size_t salen;
480800c06a5SChuck Lever 	int error;
481800c06a5SChuck Lever 
482800c06a5SChuck Lever 	sap = kmalloc(addr_bufsize, GFP_KERNEL);
483800c06a5SChuck Lever 	if (sap == NULL)
484800c06a5SChuck Lever 		return -ENOMEM;
485800c06a5SChuck Lever 
486800c06a5SChuck Lever 	error = -ENOENT;
487800c06a5SChuck Lever 	for (s = 0; s < location->nservers; s++) {
488800c06a5SChuck Lever 		const struct nfs4_string *buf = &location->servers[s];
489800c06a5SChuck Lever 		char *hostname;
490800c06a5SChuck Lever 
491800c06a5SChuck Lever 		if (buf->len <= 0 || buf->len > PAGE_SIZE)
492800c06a5SChuck Lever 			continue;
493800c06a5SChuck Lever 
494800c06a5SChuck Lever 		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL)
495800c06a5SChuck Lever 			continue;
496800c06a5SChuck Lever 
497800c06a5SChuck Lever 		salen = nfs_parse_server_name(buf->data, buf->len,
498292f503cSTrond Myklebust 						sap, addr_bufsize, net);
499800c06a5SChuck Lever 		if (salen == 0)
500800c06a5SChuck Lever 			continue;
501800c06a5SChuck Lever 		rpc_set_port(sap, NFS_PORT);
502800c06a5SChuck Lever 
503800c06a5SChuck Lever 		error = -ENOMEM;
504a8bd9ddfSTrond Myklebust 		hostname = kmemdup_nul(buf->data, buf->len, GFP_KERNEL);
505800c06a5SChuck Lever 		if (hostname == NULL)
506800c06a5SChuck Lever 			break;
507800c06a5SChuck Lever 
508292f503cSTrond Myklebust 		error = nfs4_update_server(server, hostname, sap, salen, net);
509800c06a5SChuck Lever 		kfree(hostname);
510800c06a5SChuck Lever 		if (error == 0)
511800c06a5SChuck Lever 			break;
512800c06a5SChuck Lever 	}
513800c06a5SChuck Lever 
514800c06a5SChuck Lever 	kfree(sap);
515800c06a5SChuck Lever 	return error;
516800c06a5SChuck Lever }
517800c06a5SChuck Lever 
518800c06a5SChuck Lever /**
519800c06a5SChuck Lever  * nfs4_replace_transport - set up transport to destination server
520800c06a5SChuck Lever  *
521800c06a5SChuck Lever  * @server: export being migrated
522800c06a5SChuck Lever  * @locations: fs_locations array
523800c06a5SChuck Lever  *
524800c06a5SChuck Lever  * Returns zero on success, or a negative errno value.
525800c06a5SChuck Lever  *
526800c06a5SChuck Lever  * The client tries all the entries in the "locations" array, in the
527800c06a5SChuck Lever  * order returned by the server, until one works or the end of the
528800c06a5SChuck Lever  * array is reached.
529800c06a5SChuck Lever  */
530800c06a5SChuck Lever int nfs4_replace_transport(struct nfs_server *server,
531800c06a5SChuck Lever 			   const struct nfs4_fs_locations *locations)
532800c06a5SChuck Lever {
533800c06a5SChuck Lever 	char *page = NULL, *page2 = NULL;
534800c06a5SChuck Lever 	int loc, error;
535800c06a5SChuck Lever 
536800c06a5SChuck Lever 	error = -ENOENT;
537800c06a5SChuck Lever 	if (locations == NULL || locations->nlocations <= 0)
538800c06a5SChuck Lever 		goto out;
539800c06a5SChuck Lever 
540800c06a5SChuck Lever 	error = -ENOMEM;
541800c06a5SChuck Lever 	page = (char *) __get_free_page(GFP_USER);
542800c06a5SChuck Lever 	if (!page)
543800c06a5SChuck Lever 		goto out;
544800c06a5SChuck Lever 	page2 = (char *) __get_free_page(GFP_USER);
545800c06a5SChuck Lever 	if (!page2)
546800c06a5SChuck Lever 		goto out;
547800c06a5SChuck Lever 
548800c06a5SChuck Lever 	for (loc = 0; loc < locations->nlocations; loc++) {
549800c06a5SChuck Lever 		const struct nfs4_fs_location *location =
550800c06a5SChuck Lever 						&locations->locations[loc];
551800c06a5SChuck Lever 
552800c06a5SChuck Lever 		if (location == NULL || location->nservers <= 0 ||
553800c06a5SChuck Lever 		    location->rootpath.ncomponents == 0)
554800c06a5SChuck Lever 			continue;
555800c06a5SChuck Lever 
556800c06a5SChuck Lever 		error = nfs4_try_replacing_one_location(server, page,
557800c06a5SChuck Lever 							page2, location);
558800c06a5SChuck Lever 		if (error == 0)
559800c06a5SChuck Lever 			break;
560800c06a5SChuck Lever 	}
561800c06a5SChuck Lever 
562800c06a5SChuck Lever out:
563800c06a5SChuck Lever 	free_page((unsigned long)page);
564800c06a5SChuck Lever 	free_page((unsigned long)page2);
565800c06a5SChuck Lever 	return error;
566800c06a5SChuck Lever }
567