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 */
nfs4_pathname_len(const struct nfs4_pathname * pathname)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 */
nfs4_pathname_string(const struct nfs4_pathname * pathname,unsigned short * _len)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 */
nfs_path_component(const char * nfspath,const char * end)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 */
nfs4_path(struct dentry * dentry,char * buffer,ssize_t buflen)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 */
nfs4_validate_fspath(struct dentry * dentry,const struct nfs4_fs_locations * locations,struct nfs_fs_context * ctx)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
nfs_parse_server_name(char * string,size_t len,struct sockaddr_storage * ss,size_t salen,struct net * net,int port)167*cf0d7e7fSKees Cook size_t nfs_parse_server_name(char *string, size_t len, struct sockaddr_storage *ss,
168a8d54babSOlga Kornievskaia size_t salen, struct net *net, int port)
1697d7ea882STrond Myklebust {
170*cf0d7e7fSKees Cook struct sockaddr *sa = (struct sockaddr *)ss;
1717d7ea882STrond Myklebust ssize_t ret;
1727d7ea882STrond Myklebust
17333faaa38SStanislav Kinsbursky ret = rpc_pton(net, string, len, sa, salen);
1747d7ea882STrond Myklebust if (ret == 0) {
175a8d54babSOlga Kornievskaia ret = rpc_uaddr2sockaddr(net, string, len, sa, salen);
176a8d54babSOlga Kornievskaia if (ret == 0) {
177*cf0d7e7fSKees Cook ret = nfs_dns_resolve_name(net, string, len, ss, salen);
1787d7ea882STrond Myklebust if (ret < 0)
1797d7ea882STrond Myklebust ret = 0;
1807d7ea882STrond Myklebust }
181a8d54babSOlga Kornievskaia } else if (port) {
182a8d54babSOlga Kornievskaia rpc_set_port(sa, port);
183a8d54babSOlga Kornievskaia }
1847d7ea882STrond Myklebust return ret;
1857d7ea882STrond Myklebust }
1867d7ea882STrond Myklebust
1879568c5e9SChuck Lever /**
1889568c5e9SChuck Lever * nfs_find_best_sec - Find a security mechanism supported locally
189302fad7bSTrond Myklebust * @clnt: pointer to rpc_clnt
1904d4b69ddSWeston Andros Adamson * @server: NFS server struct
1919568c5e9SChuck Lever * @flavors: List of security tuples returned by SECINFO procedure
1929568c5e9SChuck Lever *
19366b06860SAndy Adamson * Return an rpc client that uses the first security mechanism in
1948445cd35SAndy Adamson * "flavors" that is locally supported. The "flavors" array
1959568c5e9SChuck Lever * is searched in the order returned from the server, per RFC 3530
19666b06860SAndy Adamson * recommendation and each flavor is checked for membership in the
19766b06860SAndy Adamson * sec= mount option list if it exists.
1988445cd35SAndy Adamson *
1998445cd35SAndy Adamson * Return -EPERM if no matching flavor is found in the array.
20066b06860SAndy Adamson *
20166b06860SAndy Adamson * Please call rpc_shutdown_client() when you are done with this rpc client.
20266b06860SAndy Adamson *
2039568c5e9SChuck Lever */
nfs_find_best_sec(struct rpc_clnt * clnt,struct nfs_server * server,struct nfs4_secinfo_flavors * flavors)20466b06860SAndy Adamson static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt,
20566b06860SAndy Adamson struct nfs_server *server,
2064d4b69ddSWeston Andros Adamson struct nfs4_secinfo_flavors *flavors)
2072671bfc3SBryan Schumaker {
20866b06860SAndy Adamson rpc_authflavor_t pflavor;
2099568c5e9SChuck Lever struct nfs4_secinfo4 *secinfo;
210fb15b26fSChuck Lever unsigned int i;
2112671bfc3SBryan Schumaker
2122671bfc3SBryan Schumaker for (i = 0; i < flavors->num_flavors; i++) {
2139568c5e9SChuck Lever secinfo = &flavors->flavors[i];
2142671bfc3SBryan Schumaker
2159568c5e9SChuck Lever switch (secinfo->flavor) {
2169568c5e9SChuck Lever case RPC_AUTH_NULL:
2179568c5e9SChuck Lever case RPC_AUTH_UNIX:
2189568c5e9SChuck Lever case RPC_AUTH_GSS:
21966b06860SAndy Adamson pflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
2209568c5e9SChuck Lever &secinfo->flavor_info);
22166b06860SAndy Adamson /* does the pseudoflavor match a sec= mount opt? */
22266b06860SAndy Adamson if (pflavor != RPC_AUTH_MAXFLAVOR &&
22366b06860SAndy Adamson nfs_auth_info_match(&server->auth_info, pflavor)) {
22466b06860SAndy Adamson struct rpc_clnt *new;
22566b06860SAndy Adamson struct rpc_cred *cred;
22666b06860SAndy Adamson
22766b06860SAndy Adamson /* Cloning creates an rpc_auth for the flavor */
22866b06860SAndy Adamson new = rpc_clone_client_set_auth(clnt, pflavor);
22966b06860SAndy Adamson if (IS_ERR(new))
23066b06860SAndy Adamson continue;
23166b06860SAndy Adamson /**
23266b06860SAndy Adamson * Check that the user actually can use the
23366b06860SAndy Adamson * flavor. This is mostly for RPC_AUTH_GSS
23466b06860SAndy Adamson * where cr_init obtains a gss context
23566b06860SAndy Adamson */
23666b06860SAndy Adamson cred = rpcauth_lookupcred(new->cl_auth, 0);
23766b06860SAndy Adamson if (IS_ERR(cred)) {
23866b06860SAndy Adamson rpc_shutdown_client(new);
23966b06860SAndy Adamson continue;
2409568c5e9SChuck Lever }
24166b06860SAndy Adamson put_rpccred(cred);
24266b06860SAndy Adamson return new;
24366b06860SAndy Adamson }
24466b06860SAndy Adamson }
24566b06860SAndy Adamson }
24666b06860SAndy Adamson return ERR_PTR(-EPERM);
2479568c5e9SChuck Lever }
2489568c5e9SChuck Lever
24966b06860SAndy Adamson /**
25066b06860SAndy Adamson * nfs4_negotiate_security - in response to an NFS4ERR_WRONGSEC on lookup,
25166b06860SAndy Adamson * return an rpc_clnt that uses the best available security flavor with
25266b06860SAndy Adamson * respect to the secinfo flavor list and the sec= mount options.
25366b06860SAndy Adamson *
25466b06860SAndy Adamson * @clnt: RPC client to clone
25566b06860SAndy Adamson * @inode: directory inode
25666b06860SAndy Adamson * @name: lookup name
25766b06860SAndy Adamson *
25866b06860SAndy Adamson * Please call rpc_shutdown_client() when you are done with this rpc client.
25966b06860SAndy Adamson */
26066b06860SAndy Adamson struct rpc_clnt *
nfs4_negotiate_security(struct rpc_clnt * clnt,struct inode * inode,const struct qstr * name)26166b06860SAndy Adamson nfs4_negotiate_security(struct rpc_clnt *clnt, struct inode *inode,
262beffb8feSAl Viro const struct qstr *name)
26372de53ecSBryan Schumaker {
26472de53ecSBryan Schumaker struct page *page;
26572de53ecSBryan Schumaker struct nfs4_secinfo_flavors *flavors;
26666b06860SAndy Adamson struct rpc_clnt *new;
26772de53ecSBryan Schumaker int err;
26872de53ecSBryan Schumaker
26972de53ecSBryan Schumaker page = alloc_page(GFP_KERNEL);
27072de53ecSBryan Schumaker if (!page)
27166b06860SAndy Adamson return ERR_PTR(-ENOMEM);
27266b06860SAndy Adamson
27372de53ecSBryan Schumaker flavors = page_address(page);
27472de53ecSBryan Schumaker
27572de53ecSBryan Schumaker err = nfs4_proc_secinfo(inode, name, flavors);
27672de53ecSBryan Schumaker if (err < 0) {
27766b06860SAndy Adamson new = ERR_PTR(err);
27872de53ecSBryan Schumaker goto out;
27972de53ecSBryan Schumaker }
28072de53ecSBryan Schumaker
28166b06860SAndy Adamson new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors);
28272de53ecSBryan Schumaker
28372de53ecSBryan Schumaker out:
28472de53ecSBryan Schumaker put_page(page);
28566b06860SAndy Adamson return new;
28672de53ecSBryan Schumaker }
28772de53ecSBryan Schumaker
try_location(struct fs_context * fc,const struct nfs4_fs_location * location)288f2aedb71SDavid Howells static int try_location(struct fs_context *fc,
2894ada29d5SJ. Bruce Fields const struct nfs4_fs_location *location)
2904ada29d5SJ. Bruce Fields {
291f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc);
292f2aedb71SDavid Howells unsigned int len, s;
293f2aedb71SDavid Howells char *export_path, *source, *p;
294f2aedb71SDavid Howells int ret = -ENOENT;
2954ada29d5SJ. Bruce Fields
296f2aedb71SDavid Howells /* Allocate a buffer big enough to hold any of the hostnames plus a
297f2aedb71SDavid Howells * terminating char and also a buffer big enough to hold the hostname
298f2aedb71SDavid Howells * plus a colon plus the path.
299f2aedb71SDavid Howells */
300f2aedb71SDavid Howells len = 0;
301460cdbc8SJ. Bruce Fields for (s = 0; s < location->nservers; s++) {
302ea31a443SJ. Bruce Fields const struct nfs4_string *buf = &location->servers[s];
303f2aedb71SDavid Howells if (buf->len > len)
304f2aedb71SDavid Howells len = buf->len;
305f2aedb71SDavid Howells }
3064ada29d5SJ. Bruce Fields
307f2aedb71SDavid Howells kfree(ctx->nfs_server.hostname);
308f2aedb71SDavid Howells ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL);
309f2aedb71SDavid Howells if (!ctx->nfs_server.hostname)
310f2aedb71SDavid Howells return -ENOMEM;
311f2aedb71SDavid Howells
312f2aedb71SDavid Howells export_path = nfs4_pathname_string(&location->rootpath,
313f2aedb71SDavid Howells &ctx->nfs_server.export_path_len);
314f2aedb71SDavid Howells if (IS_ERR(export_path))
315f2aedb71SDavid Howells return PTR_ERR(export_path);
316f2aedb71SDavid Howells
3174659ed7cSTom Rix kfree(ctx->nfs_server.export_path);
318f2aedb71SDavid Howells ctx->nfs_server.export_path = export_path;
319f2aedb71SDavid Howells
320f2aedb71SDavid Howells source = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1,
321f2aedb71SDavid Howells GFP_KERNEL);
322f2aedb71SDavid Howells if (!source)
323f2aedb71SDavid Howells return -ENOMEM;
324f2aedb71SDavid Howells
325f2aedb71SDavid Howells kfree(fc->source);
326f2aedb71SDavid Howells fc->source = source;
327f2aedb71SDavid Howells for (s = 0; s < location->nservers; s++) {
328f2aedb71SDavid Howells const struct nfs4_string *buf = &location->servers[s];
3294ada29d5SJ. Bruce Fields
330ea31a443SJ. Bruce Fields if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
331ea31a443SJ. Bruce Fields continue;
332517be09dSTrond Myklebust
33362a55d08SScott Mayhew ctx->nfs_server.addrlen =
334f2aedb71SDavid Howells nfs_parse_server_name(buf->data, buf->len,
335*cf0d7e7fSKees Cook &ctx->nfs_server._address,
33662a55d08SScott Mayhew sizeof(ctx->nfs_server._address),
337a8d54babSOlga Kornievskaia fc->net_ns, 0);
33862a55d08SScott Mayhew if (ctx->nfs_server.addrlen == 0)
339ea31a443SJ. Bruce Fields continue;
340517be09dSTrond Myklebust
34162a55d08SScott Mayhew rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
3424ada29d5SJ. Bruce Fields
343f2aedb71SDavid Howells memcpy(ctx->nfs_server.hostname, buf->data, buf->len);
344f2aedb71SDavid Howells ctx->nfs_server.hostname[buf->len] = '\0';
3454ada29d5SJ. Bruce Fields
346f2aedb71SDavid Howells p = source;
347f2aedb71SDavid Howells memcpy(p, buf->data, buf->len);
348f2aedb71SDavid Howells p += buf->len;
349f2aedb71SDavid Howells *p++ = ':';
350f2aedb71SDavid Howells memcpy(p, ctx->nfs_server.export_path, ctx->nfs_server.export_path_len);
351f2aedb71SDavid Howells p += ctx->nfs_server.export_path_len;
352f2aedb71SDavid Howells *p = 0;
353f2aedb71SDavid Howells
354f2aedb71SDavid Howells ret = nfs4_get_referral_tree(fc);
355f2aedb71SDavid Howells if (ret == 0)
356f2aedb71SDavid Howells return 0;
3574ada29d5SJ. Bruce Fields }
358f2aedb71SDavid Howells
359f2aedb71SDavid Howells return ret;
3604ada29d5SJ. Bruce Fields }
3614ada29d5SJ. Bruce Fields
362f7b422b1SDavid Howells /**
363f7b422b1SDavid Howells * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
3643cab1854STrond Myklebust * @fc: pointer to struct nfs_fs_context
365302fad7bSTrond Myklebust * @locations: array of NFSv4 server location information
366f7b422b1SDavid Howells *
367f7b422b1SDavid Howells */
nfs_follow_referral(struct fs_context * fc,const struct nfs4_fs_locations * locations)368f2aedb71SDavid Howells static int nfs_follow_referral(struct fs_context *fc,
369509de811SDavid Howells const struct nfs4_fs_locations *locations)
370f7b422b1SDavid Howells {
371f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc);
3723f43c666SChuck Lever int loc, error;
373f7b422b1SDavid Howells
374f7b422b1SDavid Howells if (locations == NULL || locations->nlocations <= 0)
375f2aedb71SDavid Howells return -ENOENT;
376f7b422b1SDavid Howells
377f2aedb71SDavid Howells dprintk("%s: referral at %pd2\n", __func__, ctx->clone_data.dentry);
378f7b422b1SDavid Howells
37954ceac45SDavid Howells /* Ensure fs path is a prefix of current dentry path */
380f2aedb71SDavid Howells error = nfs4_validate_fspath(ctx->clone_data.dentry, locations, ctx);
381f2aedb71SDavid Howells if (error < 0)
382f2aedb71SDavid Howells return error;
383f7b422b1SDavid Howells
384f2aedb71SDavid Howells error = -ENOENT;
385460cdbc8SJ. Bruce Fields for (loc = 0; loc < locations->nlocations; loc++) {
386509de811SDavid Howells const struct nfs4_fs_location *location = &locations->locations[loc];
387f7b422b1SDavid Howells
388f7b422b1SDavid Howells if (location == NULL || location->nservers <= 0 ||
389460cdbc8SJ. Bruce Fields location->rootpath.ncomponents == 0)
390f7b422b1SDavid Howells continue;
391f7b422b1SDavid Howells
392f2aedb71SDavid Howells error = try_location(fc, location);
393f2aedb71SDavid Howells if (error == 0)
394f2aedb71SDavid Howells return 0;
395f7b422b1SDavid Howells }
396f7b422b1SDavid Howells
397f2aedb71SDavid Howells return error;
398f7b422b1SDavid Howells }
399f7b422b1SDavid Howells
400f7b422b1SDavid Howells /*
401f7b422b1SDavid Howells * nfs_do_refmount - handle crossing a referral on server
402f7b422b1SDavid Howells * @dentry - dentry of referral
403f7b422b1SDavid Howells *
404f7b422b1SDavid Howells */
nfs_do_refmount(struct fs_context * fc,struct rpc_clnt * client)405f2aedb71SDavid Howells static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client)
406f7b422b1SDavid Howells {
407f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc);
408f2aedb71SDavid Howells struct dentry *dentry, *parent;
409f7b422b1SDavid Howells struct nfs4_fs_locations *fs_locations = NULL;
410f7b422b1SDavid Howells struct page *page;
411f2aedb71SDavid Howells int err = -ENOMEM;
412f7b422b1SDavid Howells
413f7b422b1SDavid Howells /* BUG_ON(IS_ROOT(dentry)); */
414f7b422b1SDavid Howells page = alloc_page(GFP_KERNEL);
415f2aedb71SDavid Howells if (!page)
416f2aedb71SDavid Howells return -ENOMEM;
417f7b422b1SDavid Howells
418f7b422b1SDavid Howells fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
419f2aedb71SDavid Howells if (!fs_locations)
420f7b422b1SDavid Howells goto out_free;
421c3ed2227SBenjamin Coddington fs_locations->fattr = nfs_alloc_fattr();
422c3ed2227SBenjamin Coddington if (!fs_locations->fattr)
423c3ed2227SBenjamin Coddington goto out_free_2;
424f7b422b1SDavid Howells
425f7b422b1SDavid Howells /* Get locations */
426f2aedb71SDavid Howells dentry = ctx->clone_data.dentry;
427f7b422b1SDavid Howells parent = dget_parent(dentry);
4286de1472fSAl Viro dprintk("%s: getting locations for %pd2\n",
4296de1472fSAl Viro __func__, dentry);
43054ceac45SDavid Howells
4312b0143b5SDavid Howells err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page);
432f7b422b1SDavid Howells dput(parent);
433f2aedb71SDavid Howells if (err != 0)
434c3ed2227SBenjamin Coddington goto out_free_3;
435f7b422b1SDavid Howells
436f2aedb71SDavid Howells err = -ENOENT;
437f2aedb71SDavid Howells if (fs_locations->nlocations <= 0 ||
438f2aedb71SDavid Howells fs_locations->fs_path.ncomponents <= 0)
439c3ed2227SBenjamin Coddington goto out_free_3;
440f2aedb71SDavid Howells
441f2aedb71SDavid Howells err = nfs_follow_referral(fc, fs_locations);
442c3ed2227SBenjamin Coddington out_free_3:
443c3ed2227SBenjamin Coddington kfree(fs_locations->fattr);
444f2aedb71SDavid Howells out_free_2:
445f2aedb71SDavid Howells kfree(fs_locations);
446f7b422b1SDavid Howells out_free:
447f7b422b1SDavid Howells __free_page(page);
448f2aedb71SDavid Howells return err;
449f7b422b1SDavid Howells }
450281cad46SBryan Schumaker
nfs4_submount(struct fs_context * fc,struct nfs_server * server)451f2aedb71SDavid Howells int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
452281cad46SBryan Schumaker {
453f2aedb71SDavid Howells struct nfs_fs_context *ctx = nfs_fc2context(fc);
454f2aedb71SDavid Howells struct dentry *dentry = ctx->clone_data.dentry;
455281cad46SBryan Schumaker struct dentry *parent = dget_parent(dentry);
4562b0143b5SDavid Howells struct inode *dir = d_inode(parent);
457281cad46SBryan Schumaker struct rpc_clnt *client;
458f2aedb71SDavid Howells int ret;
459281cad46SBryan Schumaker
460281cad46SBryan Schumaker /* Look it up again to get its attributes and sec flavor */
461f7b37b8bSTrond Myklebust client = nfs4_proc_lookup_mountpoint(dir, dentry, ctx->mntfh,
462f2aedb71SDavid Howells ctx->clone_data.fattr);
463281cad46SBryan Schumaker dput(parent);
464281cad46SBryan Schumaker if (IS_ERR(client))
465f2aedb71SDavid Howells return PTR_ERR(client);
466281cad46SBryan Schumaker
467f2aedb71SDavid Howells ctx->selected_flavor = client->cl_auth->au_flavor;
468f2aedb71SDavid Howells if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
469f2aedb71SDavid Howells ret = nfs_do_refmount(fc, client);
470f2aedb71SDavid Howells } else {
471f2aedb71SDavid Howells ret = nfs_do_submount(fc);
47247040da3STrond Myklebust }
473281cad46SBryan Schumaker
474281cad46SBryan Schumaker rpc_shutdown_client(client);
475f2aedb71SDavid Howells return ret;
476281cad46SBryan Schumaker }
477800c06a5SChuck Lever
478800c06a5SChuck Lever /*
479800c06a5SChuck Lever * Try one location from the fs_locations array.
480800c06a5SChuck Lever *
481800c06a5SChuck Lever * Returns zero on success, or a negative errno value.
482800c06a5SChuck Lever */
nfs4_try_replacing_one_location(struct nfs_server * server,char * page,char * page2,const struct nfs4_fs_location * location)483800c06a5SChuck Lever static int nfs4_try_replacing_one_location(struct nfs_server *server,
484800c06a5SChuck Lever char *page, char *page2,
485800c06a5SChuck Lever const struct nfs4_fs_location *location)
486800c06a5SChuck Lever {
487292f503cSTrond Myklebust struct net *net = rpc_net_ns(server->client);
488*cf0d7e7fSKees Cook struct sockaddr_storage *sap;
489800c06a5SChuck Lever unsigned int s;
490800c06a5SChuck Lever size_t salen;
491800c06a5SChuck Lever int error;
492800c06a5SChuck Lever
493*cf0d7e7fSKees Cook sap = kmalloc(sizeof(*sap), GFP_KERNEL);
494800c06a5SChuck Lever if (sap == NULL)
495800c06a5SChuck Lever return -ENOMEM;
496800c06a5SChuck Lever
497800c06a5SChuck Lever error = -ENOENT;
498800c06a5SChuck Lever for (s = 0; s < location->nservers; s++) {
499800c06a5SChuck Lever const struct nfs4_string *buf = &location->servers[s];
500800c06a5SChuck Lever char *hostname;
501800c06a5SChuck Lever
502800c06a5SChuck Lever if (buf->len <= 0 || buf->len > PAGE_SIZE)
503800c06a5SChuck Lever continue;
504800c06a5SChuck Lever
505800c06a5SChuck Lever if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL)
506800c06a5SChuck Lever continue;
507800c06a5SChuck Lever
508800c06a5SChuck Lever salen = nfs_parse_server_name(buf->data, buf->len,
509*cf0d7e7fSKees Cook sap, sizeof(*sap), net, 0);
510800c06a5SChuck Lever if (salen == 0)
511800c06a5SChuck Lever continue;
512*cf0d7e7fSKees Cook rpc_set_port((struct sockaddr *)sap, NFS_PORT);
513800c06a5SChuck Lever
514800c06a5SChuck Lever error = -ENOMEM;
515a8bd9ddfSTrond Myklebust hostname = kmemdup_nul(buf->data, buf->len, GFP_KERNEL);
516800c06a5SChuck Lever if (hostname == NULL)
517800c06a5SChuck Lever break;
518800c06a5SChuck Lever
519292f503cSTrond Myklebust error = nfs4_update_server(server, hostname, sap, salen, net);
520800c06a5SChuck Lever kfree(hostname);
521800c06a5SChuck Lever if (error == 0)
522800c06a5SChuck Lever break;
523800c06a5SChuck Lever }
524800c06a5SChuck Lever
525800c06a5SChuck Lever kfree(sap);
526800c06a5SChuck Lever return error;
527800c06a5SChuck Lever }
528800c06a5SChuck Lever
529800c06a5SChuck Lever /**
530800c06a5SChuck Lever * nfs4_replace_transport - set up transport to destination server
531800c06a5SChuck Lever *
532800c06a5SChuck Lever * @server: export being migrated
533800c06a5SChuck Lever * @locations: fs_locations array
534800c06a5SChuck Lever *
535800c06a5SChuck Lever * Returns zero on success, or a negative errno value.
536800c06a5SChuck Lever *
537800c06a5SChuck Lever * The client tries all the entries in the "locations" array, in the
538800c06a5SChuck Lever * order returned by the server, until one works or the end of the
539800c06a5SChuck Lever * array is reached.
540800c06a5SChuck Lever */
nfs4_replace_transport(struct nfs_server * server,const struct nfs4_fs_locations * locations)541800c06a5SChuck Lever int nfs4_replace_transport(struct nfs_server *server,
542800c06a5SChuck Lever const struct nfs4_fs_locations *locations)
543800c06a5SChuck Lever {
544800c06a5SChuck Lever char *page = NULL, *page2 = NULL;
545800c06a5SChuck Lever int loc, error;
546800c06a5SChuck Lever
547800c06a5SChuck Lever error = -ENOENT;
548800c06a5SChuck Lever if (locations == NULL || locations->nlocations <= 0)
549800c06a5SChuck Lever goto out;
550800c06a5SChuck Lever
551800c06a5SChuck Lever error = -ENOMEM;
552800c06a5SChuck Lever page = (char *) __get_free_page(GFP_USER);
553800c06a5SChuck Lever if (!page)
554800c06a5SChuck Lever goto out;
555800c06a5SChuck Lever page2 = (char *) __get_free_page(GFP_USER);
556800c06a5SChuck Lever if (!page2)
557800c06a5SChuck Lever goto out;
558800c06a5SChuck Lever
559800c06a5SChuck Lever for (loc = 0; loc < locations->nlocations; loc++) {
560800c06a5SChuck Lever const struct nfs4_fs_location *location =
561800c06a5SChuck Lever &locations->locations[loc];
562800c06a5SChuck Lever
563800c06a5SChuck Lever if (location == NULL || location->nservers <= 0 ||
564800c06a5SChuck Lever location->rootpath.ncomponents == 0)
565800c06a5SChuck Lever continue;
566800c06a5SChuck Lever
567800c06a5SChuck Lever error = nfs4_try_replacing_one_location(server, page,
568800c06a5SChuck Lever page2, location);
569800c06a5SChuck Lever if (error == 0)
570800c06a5SChuck Lever break;
571800c06a5SChuck Lever }
572800c06a5SChuck Lever
573800c06a5SChuck Lever out:
574800c06a5SChuck Lever free_page((unsigned long)page);
575800c06a5SChuck Lever free_page((unsigned long)page2);
576800c06a5SChuck Lever return error;
577800c06a5SChuck Lever }
578