Blame view

fs/nfs/nfs4namespace.c 13.5 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
f7b422b17   David Howells   NFS: Split fs/nfs...
2
3
4
5
  /*
   * linux/fs/nfs/nfs4namespace.c
   *
   * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
54ceac451   David Howells   NFS: Share NFS su...
6
   * - Modified by David Howells <dhowells@redhat.com>
f7b422b17   David Howells   NFS: Split fs/nfs...
7
8
9
   *
   * NFSv4 namespace
   */
f2aedb713   David Howells   NFS: Add fs_conte...
10
  #include <linux/module.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
11
12
13
14
  #include <linux/dcache.h>
  #include <linux/mount.h>
  #include <linux/namei.h>
  #include <linux/nfs_fs.h>
47040da3c   Trond Myklebust   NFSv4: Allow secu...
15
  #include <linux/nfs_mount.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
16
  #include <linux/slab.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
17
18
  #include <linux/string.h>
  #include <linux/sunrpc/clnt.h>
5976687a2   Jeff Layton   sunrpc: move addr...
19
  #include <linux/sunrpc/addr.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
20
21
22
  #include <linux/vfs.h>
  #include <linux/inet.h>
  #include "internal.h"
c228fd3ae   Trond Myklebust   NFSv4: Cleanups f...
23
  #include "nfs4_fs.h"
f2aedb713   David Howells   NFS: Add fs_conte...
24
  #include "nfs.h"
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
25
  #include "dns_resolve.h"
f7b422b17   David Howells   NFS: Split fs/nfs...
26
27
28
29
  
  #define NFSDBG_FACILITY		NFSDBG_VFS
  
  /*
f2aedb713   David Howells   NFS: Add fs_conte...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
   * Work out the length that an NFSv4 path would render to as a standard posix
   * path, with a leading slash but no terminating slash.
   */
  static ssize_t nfs4_pathname_len(const struct nfs4_pathname *pathname)
  {
  	ssize_t len = 0;
  	int i;
  
  	for (i = 0; i < pathname->ncomponents; i++) {
  		const struct nfs4_string *component = &pathname->components[i];
  
  		if (component->len > NAME_MAX)
  			goto too_long;
  		len += 1 + component->len; /* Adding "/foo" */
  		if (len > PATH_MAX)
  			goto too_long;
  	}
  	return len;
  
  too_long:
  	return -ENAMETOOLONG;
  }
  
  /*
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
54
   * Convert the NFSv4 pathname components into a standard posix path.
f7b422b17   David Howells   NFS: Split fs/nfs...
55
   */
f2aedb713   David Howells   NFS: Add fs_conte...
56
57
  static char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
  				  unsigned short *_len)
f7b422b17   David Howells   NFS: Split fs/nfs...
58
  {
f2aedb713   David Howells   NFS: Add fs_conte...
59
60
61
62
63
64
65
66
67
68
69
70
  	ssize_t len;
  	char *buf, *p;
  	int i;
  
  	len = nfs4_pathname_len(pathname);
  	if (len < 0)
  		return ERR_PTR(len);
  	*_len = len;
  
  	p = buf = kmalloc(len + 1, GFP_KERNEL);
  	if (!buf)
  		return ERR_PTR(-ENOMEM);
f7b422b17   David Howells   NFS: Split fs/nfs...
71

f2aedb713   David Howells   NFS: Add fs_conte...
72
73
74
75
76
77
  	for (i = 0; i < pathname->ncomponents; i++) {
  		const struct nfs4_string *component = &pathname->components[i];
  
  		*p++ = '/';
  		memcpy(p, component->data, component->len);
  		p += component->len;
f7b422b17   David Howells   NFS: Split fs/nfs...
78
  	}
f2aedb713   David Howells   NFS: Add fs_conte...
79
80
81
  
  	*p = 0;
  	return buf;
f7b422b17   David Howells   NFS: Split fs/nfs...
82
  }
54ceac451   David Howells   NFS: Share NFS su...
83
  /*
1aba15676   Weston Andros Adamson   nfs4: fix referra...
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
   * return the path component of "<server>:<path>"
   *  nfspath - the "<server>:<path>" string
   *  end - one past the last char that could contain "<server>:"
   * returns NULL on failure
   */
  static char *nfs_path_component(const char *nfspath, const char *end)
  {
  	char *p;
  
  	if (*nfspath == '[') {
  		/* parse [] escaped IPv6 addrs */
  		p = strchr(nfspath, ']');
  		if (p != NULL && ++p < end && *p == ':')
  			return p + 1;
  	} else {
  		/* otherwise split on first colon */
  		p = strchr(nfspath, ':');
  		if (p != NULL && p < end)
  			return p + 1;
  	}
  	return NULL;
  }
  
  /*
54ceac451   David Howells   NFS: Share NFS su...
108
109
   * Determine the mount path as a string
   */
b514f872f   Al Viro   nfs: make nfs_pat...
110
  static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
54ceac451   David Howells   NFS: Share NFS su...
111
  {
b514f872f   Al Viro   nfs: make nfs_pat...
112
  	char *limit;
97a548682   Ben Hutchings   nfs: Show origina...
113
114
  	char *path = nfs_path(&limit, dentry, buffer, buflen,
  			      NFS_PATH_CANONICAL);
b514f872f   Al Viro   nfs: make nfs_pat...
115
  	if (!IS_ERR(path)) {
1aba15676   Weston Andros Adamson   nfs4: fix referra...
116
117
118
  		char *path_component = nfs_path_component(path, limit);
  		if (path_component)
  			return path_component;
b514f872f   Al Viro   nfs: make nfs_pat...
119
120
  	}
  	return path;
54ceac451   David Howells   NFS: Share NFS su...
121
122
123
124
125
126
  }
  
  /*
   * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
   * believe to be the server path to this dentry
   */
b514f872f   Al Viro   nfs: make nfs_pat...
127
  static int nfs4_validate_fspath(struct dentry *dentry,
54ceac451   David Howells   NFS: Share NFS su...
128
  				const struct nfs4_fs_locations *locations,
f2aedb713   David Howells   NFS: Add fs_conte...
129
  				struct nfs_fs_context *ctx)
54ceac451   David Howells   NFS: Share NFS su...
130
  {
62a55d088   Scott Mayhew   NFS: Additional r...
131
132
  	const char *path;
  	char *fs_path;
f2aedb713   David Howells   NFS: Add fs_conte...
133
  	unsigned short len;
62a55d088   Scott Mayhew   NFS: Additional r...
134
  	char *buf;
f2aedb713   David Howells   NFS: Add fs_conte...
135
  	int n;
54ceac451   David Howells   NFS: Share NFS su...
136

f2aedb713   David Howells   NFS: Add fs_conte...
137
  	buf = kmalloc(4096, GFP_KERNEL);
e0b27d98b   Colin Ian King   NFS: Add missing ...
138
139
  	if (!buf)
  		return -ENOMEM;
f2aedb713   David Howells   NFS: Add fs_conte...
140
141
142
  	path = nfs4_path(dentry, buf, 4096);
  	if (IS_ERR(path)) {
  		kfree(buf);
54ceac451   David Howells   NFS: Share NFS su...
143
  		return PTR_ERR(path);
f2aedb713   David Howells   NFS: Add fs_conte...
144
  	}
54ceac451   David Howells   NFS: Share NFS su...
145

f2aedb713   David Howells   NFS: Add fs_conte...
146
147
148
  	fs_path = nfs4_pathname_string(&locations->fs_path, &len);
  	if (IS_ERR(fs_path)) {
  		kfree(buf);
54ceac451   David Howells   NFS: Share NFS su...
149
  		return PTR_ERR(fs_path);
f2aedb713   David Howells   NFS: Add fs_conte...
150
  	}
54ceac451   David Howells   NFS: Share NFS su...
151

f2aedb713   David Howells   NFS: Add fs_conte...
152
153
154
155
  	n = strncmp(path, fs_path, len);
  	kfree(buf);
  	kfree(fs_path);
  	if (n != 0) {
54ceac451   David Howells   NFS: Share NFS su...
156
157
  		dprintk("%s: path %s does not begin with fsroot %s
  ",
f2aedb713   David Howells   NFS: Add fs_conte...
158
  			__func__, path, ctx->nfs_server.export_path);
54ceac451   David Howells   NFS: Share NFS su...
159
160
161
162
163
  		return -ENOENT;
  	}
  
  	return 0;
  }
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
164
  static size_t nfs_parse_server_name(char *string, size_t len,
292f503ca   Trond Myklebust   NFSv4: Use the co...
165
  		struct sockaddr *sa, size_t salen, struct net *net)
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
166
167
  {
  	ssize_t ret;
33faaa380   Stanislav Kinsbursky   NFS: pass transpo...
168
  	ret = rpc_pton(net, string, len, sa, salen);
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
169
  	if (ret == 0) {
33faaa380   Stanislav Kinsbursky   NFS: pass transpo...
170
  		ret = nfs_dns_resolve_name(net, string, len, sa, salen);
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
171
172
173
174
175
  		if (ret < 0)
  			ret = 0;
  	}
  	return ret;
  }
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
176
177
  /**
   * nfs_find_best_sec - Find a security mechanism supported locally
302fad7bd   Trond Myklebust   NFS: Fix up docum...
178
   * @clnt: pointer to rpc_clnt
4d4b69dd8   Weston Andros Adamson   NFS: add support ...
179
   * @server: NFS server struct
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
180
181
   * @flavors: List of security tuples returned by SECINFO procedure
   *
66b068604   Andy Adamson   NFSv4: test SECIN...
182
183
   * Return an rpc client that uses the first security mechanism in
   * "flavors" that is locally supported.  The "flavors" array
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
184
   * is searched in the order returned from the server, per RFC 3530
66b068604   Andy Adamson   NFSv4: test SECIN...
185
186
   * recommendation and each flavor is checked for membership in the
   * sec= mount option list if it exists.
8445cd352   Andy Adamson   NFS Return -EPERM...
187
188
   *
   * Return -EPERM if no matching flavor is found in the array.
66b068604   Andy Adamson   NFSv4: test SECIN...
189
190
191
   *
   * Please call rpc_shutdown_client() when you are done with this rpc client.
   *
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
192
   */
66b068604   Andy Adamson   NFSv4: test SECIN...
193
194
  static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt,
  					  struct nfs_server *server,
4d4b69dd8   Weston Andros Adamson   NFS: add support ...
195
  					  struct nfs4_secinfo_flavors *flavors)
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
196
  {
66b068604   Andy Adamson   NFSv4: test SECIN...
197
  	rpc_authflavor_t pflavor;
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
198
  	struct nfs4_secinfo4 *secinfo;
fb15b26f8   Chuck Lever   SUNRPC: Define rp...
199
  	unsigned int i;
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
200
201
  
  	for (i = 0; i < flavors->num_flavors; i++) {
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
202
203
204
205
206
207
  		secinfo = &flavors->flavors[i];
  
  		switch (secinfo->flavor) {
  		case RPC_AUTH_NULL:
  		case RPC_AUTH_UNIX:
  		case RPC_AUTH_GSS:
66b068604   Andy Adamson   NFSv4: test SECIN...
208
  			pflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
209
  							&secinfo->flavor_info);
66b068604   Andy Adamson   NFSv4: test SECIN...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  			/* does the pseudoflavor match a sec= mount opt? */
  			if (pflavor != RPC_AUTH_MAXFLAVOR &&
  			    nfs_auth_info_match(&server->auth_info, pflavor)) {
  				struct rpc_clnt *new;
  				struct rpc_cred *cred;
  
  				/* Cloning creates an rpc_auth for the flavor */
  				new = rpc_clone_client_set_auth(clnt, pflavor);
  				if (IS_ERR(new))
  					continue;
  				/**
  				* Check that the user actually can use the
  				* flavor. This is mostly for RPC_AUTH_GSS
  				* where cr_init obtains a gss context
  				*/
  				cred = rpcauth_lookupcred(new->cl_auth, 0);
  				if (IS_ERR(cred)) {
  					rpc_shutdown_client(new);
  					continue;
  				}
  				put_rpccred(cred);
  				return new;
  			}
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
233
234
  		}
  	}
66b068604   Andy Adamson   NFSv4: test SECIN...
235
  	return ERR_PTR(-EPERM);
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
236
  }
66b068604   Andy Adamson   NFSv4: test SECIN...
237
238
239
240
241
242
243
244
245
246
247
248
249
  /**
   * nfs4_negotiate_security - in response to an NFS4ERR_WRONGSEC on lookup,
   * return an rpc_clnt that uses the best available security flavor with
   * respect to the secinfo flavor list and the sec= mount options.
   *
   * @clnt: RPC client to clone
   * @inode: directory inode
   * @name: lookup name
   *
   * Please call rpc_shutdown_client() when you are done with this rpc client.
   */
  struct rpc_clnt *
  nfs4_negotiate_security(struct rpc_clnt *clnt, struct inode *inode,
beffb8feb   Al Viro   qstr: constify in...
250
  					const struct qstr *name)
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
251
252
253
  {
  	struct page *page;
  	struct nfs4_secinfo_flavors *flavors;
66b068604   Andy Adamson   NFSv4: test SECIN...
254
  	struct rpc_clnt *new;
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
255
256
257
258
  	int err;
  
  	page = alloc_page(GFP_KERNEL);
  	if (!page)
66b068604   Andy Adamson   NFSv4: test SECIN...
259
  		return ERR_PTR(-ENOMEM);
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
260
261
262
263
  	flavors = page_address(page);
  
  	err = nfs4_proc_secinfo(inode, name, flavors);
  	if (err < 0) {
66b068604   Andy Adamson   NFSv4: test SECIN...
264
  		new = ERR_PTR(err);
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
265
266
  		goto out;
  	}
66b068604   Andy Adamson   NFSv4: test SECIN...
267
  	new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors);
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
268
269
270
  
  out:
  	put_page(page);
66b068604   Andy Adamson   NFSv4: test SECIN...
271
  	return new;
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
272
  }
f2aedb713   David Howells   NFS: Add fs_conte...
273
274
  static int try_location(struct fs_context *fc,
  			const struct nfs4_fs_location *location)
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
275
  {
f2aedb713   David Howells   NFS: Add fs_conte...
276
277
278
279
280
281
282
283
284
285
  	struct nfs_fs_context *ctx = nfs_fc2context(fc);
  	unsigned int len, s;
  	char *export_path, *source, *p;
  	int ret = -ENOENT;
  
  	/* Allocate a buffer big enough to hold any of the hostnames plus a
  	 * terminating char and also a buffer big enough to hold the hostname
  	 * plus a colon plus the path.
  	 */
  	len = 0;
460cdbc83   J. Bruce Fields   nfs: replace whil...
286
  	for (s = 0; s < location->nservers; s++) {
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
287
  		const struct nfs4_string *buf = &location->servers[s];
f2aedb713   David Howells   NFS: Add fs_conte...
288
289
290
  		if (buf->len > len)
  			len = buf->len;
  	}
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
291

f2aedb713   David Howells   NFS: Add fs_conte...
292
293
294
295
296
297
298
299
300
  	kfree(ctx->nfs_server.hostname);
  	ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL);
  	if (!ctx->nfs_server.hostname)
  		return -ENOMEM;
  
  	export_path = nfs4_pathname_string(&location->rootpath,
  					   &ctx->nfs_server.export_path_len);
  	if (IS_ERR(export_path))
  		return PTR_ERR(export_path);
4659ed7cc   Tom Rix   nfs: Fix memory l...
301
  	kfree(ctx->nfs_server.export_path);
f2aedb713   David Howells   NFS: Add fs_conte...
302
303
304
305
306
307
308
309
310
  	ctx->nfs_server.export_path = export_path;
  
  	source = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1,
  			 GFP_KERNEL);
  	if (!source)
  		return -ENOMEM;
  
  	kfree(fc->source);
  	fc->source = source;
f2aedb713   David Howells   NFS: Add fs_conte...
311
312
  	for (s = 0; s < location->nservers; s++) {
  		const struct nfs4_string *buf = &location->servers[s];
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
313

ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
314
315
  		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
  			continue;
517be09de   Trond Myklebust   NFSv4: Fix the re...
316

62a55d088   Scott Mayhew   NFS: Additional r...
317
  		ctx->nfs_server.addrlen =
f2aedb713   David Howells   NFS: Add fs_conte...
318
  			nfs_parse_server_name(buf->data, buf->len,
62a55d088   Scott Mayhew   NFS: Additional r...
319
320
  					      &ctx->nfs_server.address,
  					      sizeof(ctx->nfs_server._address),
f2aedb713   David Howells   NFS: Add fs_conte...
321
  					      fc->net_ns);
62a55d088   Scott Mayhew   NFS: Additional r...
322
  		if (ctx->nfs_server.addrlen == 0)
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
323
  			continue;
517be09de   Trond Myklebust   NFSv4: Fix the re...
324

62a55d088   Scott Mayhew   NFS: Additional r...
325
  		rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
f2aedb713   David Howells   NFS: Add fs_conte...
326
327
328
  
  		memcpy(ctx->nfs_server.hostname, buf->data, buf->len);
  		ctx->nfs_server.hostname[buf->len] = '\0';
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
329

f2aedb713   David Howells   NFS: Add fs_conte...
330
331
332
333
334
335
336
  		p = source;
  		memcpy(p, buf->data, buf->len);
  		p += buf->len;
  		*p++ = ':';
  		memcpy(p, ctx->nfs_server.export_path, ctx->nfs_server.export_path_len);
  		p += ctx->nfs_server.export_path_len;
  		*p = 0;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
337

f2aedb713   David Howells   NFS: Add fs_conte...
338
339
340
  		ret = nfs4_get_referral_tree(fc);
  		if (ret == 0)
  			return 0;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
341
  	}
f2aedb713   David Howells   NFS: Add fs_conte...
342
343
  
  	return ret;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
344
  }
f7b422b17   David Howells   NFS: Split fs/nfs...
345
346
  /**
   * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
3cab1854b   Trond Myklebust   nfs: Fix up docum...
347
   * @fc: pointer to struct nfs_fs_context
302fad7bd   Trond Myklebust   NFS: Fix up docum...
348
   * @locations: array of NFSv4 server location information
f7b422b17   David Howells   NFS: Split fs/nfs...
349
350
   *
   */
f2aedb713   David Howells   NFS: Add fs_conte...
351
352
  static int nfs_follow_referral(struct fs_context *fc,
  			       const struct nfs4_fs_locations *locations)
f7b422b17   David Howells   NFS: Split fs/nfs...
353
  {
f2aedb713   David Howells   NFS: Add fs_conte...
354
  	struct nfs_fs_context *ctx = nfs_fc2context(fc);
3f43c6667   Chuck Lever   NFS: Address a co...
355
  	int loc, error;
f7b422b17   David Howells   NFS: Split fs/nfs...
356
357
  
  	if (locations == NULL || locations->nlocations <= 0)
f2aedb713   David Howells   NFS: Add fs_conte...
358
  		return -ENOENT;
54ceac451   David Howells   NFS: Share NFS su...
359

f2aedb713   David Howells   NFS: Add fs_conte...
360
361
  	dprintk("%s: referral at %pd2
  ", __func__, ctx->clone_data.dentry);
f7b422b17   David Howells   NFS: Split fs/nfs...
362

54ceac451   David Howells   NFS: Share NFS su...
363
  	/* Ensure fs path is a prefix of current dentry path */
f2aedb713   David Howells   NFS: Add fs_conte...
364
365
366
  	error = nfs4_validate_fspath(ctx->clone_data.dentry, locations, ctx);
  	if (error < 0)
  		return error;
f7b422b17   David Howells   NFS: Split fs/nfs...
367

f2aedb713   David Howells   NFS: Add fs_conte...
368
  	error = -ENOENT;
460cdbc83   J. Bruce Fields   nfs: replace whil...
369
  	for (loc = 0; loc < locations->nlocations; loc++) {
509de8111   David Howells   NFS: Add extra co...
370
  		const struct nfs4_fs_location *location = &locations->locations[loc];
f7b422b17   David Howells   NFS: Split fs/nfs...
371
372
  
  		if (location == NULL || location->nservers <= 0 ||
460cdbc83   J. Bruce Fields   nfs: replace whil...
373
  		    location->rootpath.ncomponents == 0)
f7b422b17   David Howells   NFS: Split fs/nfs...
374
  			continue;
f7b422b17   David Howells   NFS: Split fs/nfs...
375

f2aedb713   David Howells   NFS: Add fs_conte...
376
377
378
  		error = try_location(fc, location);
  		if (error == 0)
  			return 0;
f7b422b17   David Howells   NFS: Split fs/nfs...
379
  	}
f2aedb713   David Howells   NFS: Add fs_conte...
380
  	return error;
f7b422b17   David Howells   NFS: Split fs/nfs...
381
382
383
384
385
  }
  
  /*
   * nfs_do_refmount - handle crossing a referral on server
   * @dentry - dentry of referral
f7b422b17   David Howells   NFS: Split fs/nfs...
386
387
   *
   */
f2aedb713   David Howells   NFS: Add fs_conte...
388
  static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client)
f7b422b17   David Howells   NFS: Split fs/nfs...
389
  {
f2aedb713   David Howells   NFS: Add fs_conte...
390
391
  	struct nfs_fs_context *ctx = nfs_fc2context(fc);
  	struct dentry *dentry, *parent;
f7b422b17   David Howells   NFS: Split fs/nfs...
392
393
  	struct nfs4_fs_locations *fs_locations = NULL;
  	struct page *page;
f2aedb713   David Howells   NFS: Add fs_conte...
394
  	int err = -ENOMEM;
f7b422b17   David Howells   NFS: Split fs/nfs...
395
396
  
  	/* BUG_ON(IS_ROOT(dentry)); */
f7b422b17   David Howells   NFS: Split fs/nfs...
397
  	page = alloc_page(GFP_KERNEL);
f2aedb713   David Howells   NFS: Add fs_conte...
398
399
  	if (!page)
  		return -ENOMEM;
f7b422b17   David Howells   NFS: Split fs/nfs...
400
401
  
  	fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
f2aedb713   David Howells   NFS: Add fs_conte...
402
  	if (!fs_locations)
f7b422b17   David Howells   NFS: Split fs/nfs...
403
404
405
  		goto out_free;
  
  	/* Get locations */
f2aedb713   David Howells   NFS: Add fs_conte...
406
  	dentry = ctx->clone_data.dentry;
f7b422b17   David Howells   NFS: Split fs/nfs...
407
  	parent = dget_parent(dentry);
6de1472f1   Al Viro   nfs: use %p[dD] i...
408
409
410
  	dprintk("%s: getting locations for %pd2
  ",
  		__func__, dentry);
54ceac451   David Howells   NFS: Share NFS su...
411

2b0143b5c   David Howells   VFS: normal files...
412
  	err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page);
f7b422b17   David Howells   NFS: Split fs/nfs...
413
  	dput(parent);
f2aedb713   David Howells   NFS: Add fs_conte...
414
415
416
417
418
  	if (err != 0)
  		goto out_free_2;
  
  	err = -ENOENT;
  	if (fs_locations->nlocations <= 0 ||
f7b422b17   David Howells   NFS: Split fs/nfs...
419
  	    fs_locations->fs_path.ncomponents <= 0)
f2aedb713   David Howells   NFS: Add fs_conte...
420
  		goto out_free_2;
f7b422b17   David Howells   NFS: Split fs/nfs...
421

f2aedb713   David Howells   NFS: Add fs_conte...
422
423
424
  	err = nfs_follow_referral(fc, fs_locations);
  out_free_2:
  	kfree(fs_locations);
f7b422b17   David Howells   NFS: Split fs/nfs...
425
426
  out_free:
  	__free_page(page);
f2aedb713   David Howells   NFS: Add fs_conte...
427
  	return err;
f7b422b17   David Howells   NFS: Split fs/nfs...
428
  }
281cad46b   Bryan Schumaker   NFS: Create a sub...
429

f2aedb713   David Howells   NFS: Add fs_conte...
430
  int nfs4_submount(struct fs_context *fc, struct nfs_server *server)
281cad46b   Bryan Schumaker   NFS: Create a sub...
431
  {
f2aedb713   David Howells   NFS: Add fs_conte...
432
433
  	struct nfs_fs_context *ctx = nfs_fc2context(fc);
  	struct dentry *dentry = ctx->clone_data.dentry;
281cad46b   Bryan Schumaker   NFS: Create a sub...
434
  	struct dentry *parent = dget_parent(dentry);
2b0143b5c   David Howells   VFS: normal files...
435
  	struct inode *dir = d_inode(parent);
281cad46b   Bryan Schumaker   NFS: Create a sub...
436
  	struct rpc_clnt *client;
f2aedb713   David Howells   NFS: Add fs_conte...
437
  	int ret;
281cad46b   Bryan Schumaker   NFS: Create a sub...
438
439
  
  	/* Look it up again to get its attributes and sec flavor */
f7b37b8b1   Trond Myklebust   NFS: Add softreva...
440
  	client = nfs4_proc_lookup_mountpoint(dir, dentry, ctx->mntfh,
f2aedb713   David Howells   NFS: Add fs_conte...
441
  					     ctx->clone_data.fattr);
281cad46b   Bryan Schumaker   NFS: Create a sub...
442
443
  	dput(parent);
  	if (IS_ERR(client))
f2aedb713   David Howells   NFS: Add fs_conte...
444
  		return PTR_ERR(client);
281cad46b   Bryan Schumaker   NFS: Create a sub...
445

f2aedb713   David Howells   NFS: Add fs_conte...
446
447
448
449
450
  	ctx->selected_flavor = client->cl_auth->au_flavor;
  	if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
  		ret = nfs_do_refmount(fc, client);
  	} else {
  		ret = nfs_do_submount(fc);
47040da3c   Trond Myklebust   NFSv4: Allow secu...
451
  	}
281cad46b   Bryan Schumaker   NFS: Create a sub...
452
453
  
  	rpc_shutdown_client(client);
f2aedb713   David Howells   NFS: Add fs_conte...
454
  	return ret;
281cad46b   Bryan Schumaker   NFS: Create a sub...
455
  }
800c06a5b   Chuck Lever   NFS: Add function...
456
457
458
459
460
461
462
463
464
465
466
  
  /*
   * Try one location from the fs_locations array.
   *
   * Returns zero on success, or a negative errno value.
   */
  static int nfs4_try_replacing_one_location(struct nfs_server *server,
  		char *page, char *page2,
  		const struct nfs4_fs_location *location)
  {
  	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
292f503ca   Trond Myklebust   NFSv4: Use the co...
467
  	struct net *net = rpc_net_ns(server->client);
800c06a5b   Chuck Lever   NFS: Add function...
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  	struct sockaddr *sap;
  	unsigned int s;
  	size_t salen;
  	int error;
  
  	sap = kmalloc(addr_bufsize, GFP_KERNEL);
  	if (sap == NULL)
  		return -ENOMEM;
  
  	error = -ENOENT;
  	for (s = 0; s < location->nservers; s++) {
  		const struct nfs4_string *buf = &location->servers[s];
  		char *hostname;
  
  		if (buf->len <= 0 || buf->len > PAGE_SIZE)
  			continue;
  
  		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len) != NULL)
  			continue;
  
  		salen = nfs_parse_server_name(buf->data, buf->len,
292f503ca   Trond Myklebust   NFSv4: Use the co...
489
  						sap, addr_bufsize, net);
800c06a5b   Chuck Lever   NFS: Add function...
490
491
492
493
494
  		if (salen == 0)
  			continue;
  		rpc_set_port(sap, NFS_PORT);
  
  		error = -ENOMEM;
a8bd9ddf3   Trond Myklebust   NFS: Replace vari...
495
  		hostname = kmemdup_nul(buf->data, buf->len, GFP_KERNEL);
800c06a5b   Chuck Lever   NFS: Add function...
496
497
  		if (hostname == NULL)
  			break;
292f503ca   Trond Myklebust   NFSv4: Use the co...
498
  		error = nfs4_update_server(server, hostname, sap, salen, net);
800c06a5b   Chuck Lever   NFS: Add function...
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
  		kfree(hostname);
  		if (error == 0)
  			break;
  	}
  
  	kfree(sap);
  	return error;
  }
  
  /**
   * nfs4_replace_transport - set up transport to destination server
   *
   * @server: export being migrated
   * @locations: fs_locations array
   *
   * Returns zero on success, or a negative errno value.
   *
   * The client tries all the entries in the "locations" array, in the
   * order returned by the server, until one works or the end of the
   * array is reached.
   */
  int nfs4_replace_transport(struct nfs_server *server,
  			   const struct nfs4_fs_locations *locations)
  {
  	char *page = NULL, *page2 = NULL;
  	int loc, error;
  
  	error = -ENOENT;
  	if (locations == NULL || locations->nlocations <= 0)
  		goto out;
  
  	error = -ENOMEM;
  	page = (char *) __get_free_page(GFP_USER);
  	if (!page)
  		goto out;
  	page2 = (char *) __get_free_page(GFP_USER);
  	if (!page2)
  		goto out;
  
  	for (loc = 0; loc < locations->nlocations; loc++) {
  		const struct nfs4_fs_location *location =
  						&locations->locations[loc];
  
  		if (location == NULL || location->nservers <= 0 ||
  		    location->rootpath.ncomponents == 0)
  			continue;
  
  		error = nfs4_try_replacing_one_location(server, page,
  							page2, location);
  		if (error == 0)
  			break;
  	}
  
  out:
  	free_page((unsigned long)page);
  	free_page((unsigned long)page2);
  	return error;
  }