Blame view

fs/nfs/nfs4namespace.c 12.8 KB
f7b422b17   David Howells   NFS: Split fs/nfs...
1
2
3
4
  /*
   * linux/fs/nfs/nfs4namespace.c
   *
   * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
54ceac451   David Howells   NFS: Share NFS su...
5
   * - Modified by David Howells <dhowells@redhat.com>
f7b422b17   David Howells   NFS: Split fs/nfs...
6
7
8
   *
   * NFSv4 namespace
   */
f7b422b17   David Howells   NFS: Split fs/nfs...
9
10
11
12
  #include <linux/dcache.h>
  #include <linux/mount.h>
  #include <linux/namei.h>
  #include <linux/nfs_fs.h>
47040da3c   Trond Myklebust   NFSv4: Allow secu...
13
  #include <linux/nfs_mount.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
14
  #include <linux/slab.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
15
16
  #include <linux/string.h>
  #include <linux/sunrpc/clnt.h>
5976687a2   Jeff Layton   sunrpc: move addr...
17
  #include <linux/sunrpc/addr.h>
f7b422b17   David Howells   NFS: Split fs/nfs...
18
19
20
  #include <linux/vfs.h>
  #include <linux/inet.h>
  #include "internal.h"
c228fd3ae   Trond Myklebust   NFSv4: Cleanups f...
21
  #include "nfs4_fs.h"
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
22
  #include "dns_resolve.h"
f7b422b17   David Howells   NFS: Split fs/nfs...
23
24
25
26
  
  #define NFSDBG_FACILITY		NFSDBG_VFS
  
  /*
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
27
28
29
   * Convert the NFSv4 pathname components into a standard posix path.
   *
   * Note that the resulting string will be placed at the end of the buffer
f7b422b17   David Howells   NFS: Split fs/nfs...
30
   */
509de8111   David Howells   NFS: Add extra co...
31
  static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
f7b422b17   David Howells   NFS: Split fs/nfs...
32
33
34
35
36
37
38
39
40
41
  					 char *buffer, ssize_t buflen)
  {
  	char *end = buffer + buflen;
  	int n;
  
  	*--end = '\0';
  	buflen--;
  
  	n = pathname->ncomponents;
  	while (--n >= 0) {
509de8111   David Howells   NFS: Add extra co...
42
  		const struct nfs4_string *component = &pathname->components[n];
f7b422b17   David Howells   NFS: Split fs/nfs...
43
44
45
46
47
48
49
50
51
52
53
  		buflen -= component->len + 1;
  		if (buflen < 0)
  			goto Elong;
  		end -= component->len;
  		memcpy(end, component->data, component->len);
  		*--end = '/';
  	}
  	return end;
  Elong:
  	return ERR_PTR(-ENAMETOOLONG);
  }
54ceac451   David Howells   NFS: Share NFS su...
54
  /*
1aba15676   Weston Andros Adamson   nfs4: fix referra...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
   * 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...
79
80
   * Determine the mount path as a string
   */
b514f872f   Al Viro   nfs: make nfs_pat...
81
  static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
54ceac451   David Howells   NFS: Share NFS su...
82
  {
b514f872f   Al Viro   nfs: make nfs_pat...
83
  	char *limit;
97a548682   Ben Hutchings   nfs: Show origina...
84
85
  	char *path = nfs_path(&limit, dentry, buffer, buflen,
  			      NFS_PATH_CANONICAL);
b514f872f   Al Viro   nfs: make nfs_pat...
86
  	if (!IS_ERR(path)) {
1aba15676   Weston Andros Adamson   nfs4: fix referra...
87
88
89
  		char *path_component = nfs_path_component(path, limit);
  		if (path_component)
  			return path_component;
b514f872f   Al Viro   nfs: make nfs_pat...
90
91
  	}
  	return path;
54ceac451   David Howells   NFS: Share NFS su...
92
93
94
95
96
97
  }
  
  /*
   * 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...
98
  static int nfs4_validate_fspath(struct dentry *dentry,
54ceac451   David Howells   NFS: Share NFS su...
99
100
101
102
  				const struct nfs4_fs_locations *locations,
  				char *page, char *page2)
  {
  	const char *path, *fs_path;
b514f872f   Al Viro   nfs: make nfs_pat...
103
  	path = nfs4_path(dentry, page, PAGE_SIZE);
54ceac451   David Howells   NFS: Share NFS su...
104
105
106
107
108
109
110
111
112
113
  	if (IS_ERR(path))
  		return PTR_ERR(path);
  
  	fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
  	if (IS_ERR(fs_path))
  		return PTR_ERR(fs_path);
  
  	if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
  		dprintk("%s: path %s does not begin with fsroot %s
  ",
3110ff804   Harvey Harrison   nfs: replace rema...
114
  			__func__, path, fs_path);
54ceac451   David Howells   NFS: Share NFS su...
115
116
117
118
119
  		return -ENOENT;
  	}
  
  	return 0;
  }
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
120
  static size_t nfs_parse_server_name(char *string, size_t len,
292f503ca   Trond Myklebust   NFSv4: Use the co...
121
  		struct sockaddr *sa, size_t salen, struct net *net)
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
122
123
  {
  	ssize_t ret;
33faaa380   Stanislav Kinsbursky   NFS: pass transpo...
124
  	ret = rpc_pton(net, string, len, sa, salen);
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
125
  	if (ret == 0) {
33faaa380   Stanislav Kinsbursky   NFS: pass transpo...
126
  		ret = nfs_dns_resolve_name(net, string, len, sa, salen);
7d7ea8828   Trond Myklebust   NFS: Use the DNS ...
127
128
129
130
131
  		if (ret < 0)
  			ret = 0;
  	}
  	return ret;
  }
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
132
133
  /**
   * nfs_find_best_sec - Find a security mechanism supported locally
4d4b69dd8   Weston Andros Adamson   NFS: add support ...
134
   * @server: NFS server struct
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
135
136
   * @flavors: List of security tuples returned by SECINFO procedure
   *
66b068604   Andy Adamson   NFSv4: test SECIN...
137
138
   * Return an rpc client that uses the first security mechanism in
   * "flavors" that is locally supported.  The "flavors" array
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
139
   * is searched in the order returned from the server, per RFC 3530
66b068604   Andy Adamson   NFSv4: test SECIN...
140
141
   * recommendation and each flavor is checked for membership in the
   * sec= mount option list if it exists.
8445cd352   Andy Adamson   NFS Return -EPERM...
142
143
   *
   * Return -EPERM if no matching flavor is found in the array.
66b068604   Andy Adamson   NFSv4: test SECIN...
144
145
146
   *
   * Please call rpc_shutdown_client() when you are done with this rpc client.
   *
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
147
   */
66b068604   Andy Adamson   NFSv4: test SECIN...
148
149
  static struct rpc_clnt *nfs_find_best_sec(struct rpc_clnt *clnt,
  					  struct nfs_server *server,
4d4b69dd8   Weston Andros Adamson   NFS: add support ...
150
  					  struct nfs4_secinfo_flavors *flavors)
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
151
  {
66b068604   Andy Adamson   NFSv4: test SECIN...
152
  	rpc_authflavor_t pflavor;
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
153
  	struct nfs4_secinfo4 *secinfo;
fb15b26f8   Chuck Lever   SUNRPC: Define rp...
154
  	unsigned int i;
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
155
156
  
  	for (i = 0; i < flavors->num_flavors; i++) {
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
157
158
159
160
161
162
  		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...
163
  			pflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
9568c5e9a   Chuck Lever   SUNRPC: Introduce...
164
  							&secinfo->flavor_info);
66b068604   Andy Adamson   NFSv4: test SECIN...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  			/* 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...
188
189
  		}
  	}
66b068604   Andy Adamson   NFSv4: test SECIN...
190
  	return ERR_PTR(-EPERM);
2671bfc3b   Bryan Schumaker   NFS: Remove secin...
191
  }
66b068604   Andy Adamson   NFSv4: test SECIN...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  /**
   * 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,
  					struct qstr *name)
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
206
207
208
  {
  	struct page *page;
  	struct nfs4_secinfo_flavors *flavors;
66b068604   Andy Adamson   NFSv4: test SECIN...
209
  	struct rpc_clnt *new;
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
210
211
212
213
  	int err;
  
  	page = alloc_page(GFP_KERNEL);
  	if (!page)
66b068604   Andy Adamson   NFSv4: test SECIN...
214
  		return ERR_PTR(-ENOMEM);
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
215
216
217
218
  	flavors = page_address(page);
  
  	err = nfs4_proc_secinfo(inode, name, flavors);
  	if (err < 0) {
66b068604   Andy Adamson   NFSv4: test SECIN...
219
  		new = ERR_PTR(err);
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
220
221
  		goto out;
  	}
66b068604   Andy Adamson   NFSv4: test SECIN...
222
  	new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors);
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
223
224
225
  
  out:
  	put_page(page);
66b068604   Andy Adamson   NFSv4: test SECIN...
226
  	return new;
72de53ec4   Bryan Schumaker   NFS: Do secinfo a...
227
  }
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
228
229
230
231
  static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
  				     char *page, char *page2,
  				     const struct nfs4_fs_location *location)
  {
364d015e5   Trond Myklebust   NFSv4: Reduce the...
232
  	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
292f503ca   Trond Myklebust   NFSv4: Use the co...
233
  	struct net *net = rpc_net_ns(NFS_SB(mountdata->sb)->client);
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
234
235
  	struct vfsmount *mnt = ERR_PTR(-ENOENT);
  	char *mnt_path;
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
236
  	unsigned int maxbuflen;
460cdbc83   J. Bruce Fields   nfs: replace whil...
237
  	unsigned int s;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
238
239
240
  
  	mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
  	if (IS_ERR(mnt_path))
517be09de   Trond Myklebust   NFSv4: Fix the re...
241
  		return ERR_CAST(mnt_path);
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
242
  	mountdata->mnt_path = mnt_path;
ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
243
  	maxbuflen = mnt_path - 1 - page2;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
244

364d015e5   Trond Myklebust   NFSv4: Reduce the...
245
246
247
  	mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
  	if (mountdata->addr == NULL)
  		return ERR_PTR(-ENOMEM);
460cdbc83   J. Bruce Fields   nfs: replace whil...
248
  	for (s = 0; s < location->nservers; s++) {
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
249
  		const struct nfs4_string *buf = &location->servers[s];
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
250

ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
251
  		if (buf->len <= 0 || buf->len >= maxbuflen)
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
252
  			continue;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
253

ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
254
255
  		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
  			continue;
517be09de   Trond Myklebust   NFSv4: Fix the re...
256
257
  
  		mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
292f503ca   Trond Myklebust   NFSv4: Use the co...
258
  				mountdata->addr, addr_bufsize, net);
53a0b9c4c   Chuck Lever   NFS: Replace nfs_...
259
  		if (mountdata->addrlen == 0)
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
260
  			continue;
517be09de   Trond Myklebust   NFSv4: Fix the re...
261

ec6ee6125   Chuck Lever   NFS: Replace nfs_...
262
  		rpc_set_port(mountdata->addr, NFS_PORT);
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
263

ef95d31e6   Trond Myklebust   NFS: Fix misparsi...
264
265
  		memcpy(page2, buf->data, buf->len);
  		page2[buf->len] = '\0';
ea31a4437   J. Bruce Fields   nfs: Fix misparsi...
266
  		mountdata->hostname = page2;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
267
268
269
270
271
272
273
274
  
  		snprintf(page, PAGE_SIZE, "%s:%s",
  				mountdata->hostname,
  				mountdata->mnt_path);
  
  		mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata);
  		if (!IS_ERR(mnt))
  			break;
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
275
  	}
364d015e5   Trond Myklebust   NFSv4: Reduce the...
276
  	kfree(mountdata->addr);
4ada29d5c   J. Bruce Fields   nfs: break up nfs...
277
278
  	return mnt;
  }
f7b422b17   David Howells   NFS: Split fs/nfs...
279
280
  /**
   * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
f7b422b17   David Howells   NFS: Split fs/nfs...
281
   * @dentry - parent directory
3f43c6667   Chuck Lever   NFS: Address a co...
282
   * @locations - array of NFSv4 server location information
f7b422b17   David Howells   NFS: Split fs/nfs...
283
284
   *
   */
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
285
  static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
509de8111   David Howells   NFS: Add extra co...
286
  					    const struct nfs4_fs_locations *locations)
f7b422b17   David Howells   NFS: Split fs/nfs...
287
288
289
  {
  	struct vfsmount *mnt = ERR_PTR(-ENOENT);
  	struct nfs_clone_mount mountdata = {
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
290
  		.sb = dentry->d_sb,
f7b422b17   David Howells   NFS: Split fs/nfs...
291
  		.dentry = dentry,
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
292
  		.authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
f7b422b17   David Howells   NFS: Split fs/nfs...
293
  	};
54ceac451   David Howells   NFS: Share NFS su...
294
  	char *page = NULL, *page2 = NULL;
3f43c6667   Chuck Lever   NFS: Address a co...
295
  	int loc, error;
f7b422b17   David Howells   NFS: Split fs/nfs...
296
297
298
  
  	if (locations == NULL || locations->nlocations <= 0)
  		goto out;
6de1472f1   Al Viro   nfs: use %p[dD] i...
299
300
  	dprintk("%s: referral at %pd2
  ", __func__, dentry);
f7b422b17   David Howells   NFS: Split fs/nfs...
301

f7b422b17   David Howells   NFS: Split fs/nfs...
302
  	page = (char *) __get_free_page(GFP_USER);
54ceac451   David Howells   NFS: Share NFS su...
303
  	if (!page)
f7b422b17   David Howells   NFS: Split fs/nfs...
304
  		goto out;
54ceac451   David Howells   NFS: Share NFS su...
305

f7b422b17   David Howells   NFS: Split fs/nfs...
306
  	page2 = (char *) __get_free_page(GFP_USER);
54ceac451   David Howells   NFS: Share NFS su...
307
  	if (!page2)
f7b422b17   David Howells   NFS: Split fs/nfs...
308
  		goto out;
54ceac451   David Howells   NFS: Share NFS su...
309
  	/* Ensure fs path is a prefix of current dentry path */
b514f872f   Al Viro   nfs: make nfs_pat...
310
  	error = nfs4_validate_fspath(dentry, locations, page, page2);
54ceac451   David Howells   NFS: Share NFS su...
311
312
313
  	if (error < 0) {
  		mnt = ERR_PTR(error);
  		goto out;
f7b422b17   David Howells   NFS: Split fs/nfs...
314
  	}
460cdbc83   J. Bruce Fields   nfs: replace whil...
315
  	for (loc = 0; loc < locations->nlocations; loc++) {
509de8111   David Howells   NFS: Add extra co...
316
  		const struct nfs4_fs_location *location = &locations->locations[loc];
f7b422b17   David Howells   NFS: Split fs/nfs...
317
318
  
  		if (location == NULL || location->nservers <= 0 ||
460cdbc83   J. Bruce Fields   nfs: replace whil...
319
  		    location->rootpath.ncomponents == 0)
f7b422b17   David Howells   NFS: Split fs/nfs...
320
  			continue;
f7b422b17   David Howells   NFS: Split fs/nfs...
321

4ada29d5c   J. Bruce Fields   nfs: break up nfs...
322
323
324
  		mnt = try_location(&mountdata, page, page2, location);
  		if (!IS_ERR(mnt))
  			break;
f7b422b17   David Howells   NFS: Split fs/nfs...
325
  	}
f7b422b17   David Howells   NFS: Split fs/nfs...
326
  out:
54ceac451   David Howells   NFS: Share NFS su...
327
328
  	free_page((unsigned long) page);
  	free_page((unsigned long) page2);
3110ff804   Harvey Harrison   nfs: replace rema...
329
330
  	dprintk("%s: done
  ", __func__);
f7b422b17   David Howells   NFS: Split fs/nfs...
331
332
333
334
335
336
  	return mnt;
  }
  
  /*
   * nfs_do_refmount - handle crossing a referral on server
   * @dentry - dentry of referral
f7b422b17   David Howells   NFS: Split fs/nfs...
337
338
   *
   */
281cad46b   Bryan Schumaker   NFS: Create a sub...
339
  static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
f7b422b17   David Howells   NFS: Split fs/nfs...
340
  {
54ceac451   David Howells   NFS: Share NFS su...
341
  	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
f7b422b17   David Howells   NFS: Split fs/nfs...
342
343
344
345
346
347
  	struct dentry *parent;
  	struct nfs4_fs_locations *fs_locations = NULL;
  	struct page *page;
  	int err;
  
  	/* BUG_ON(IS_ROOT(dentry)); */
3110ff804   Harvey Harrison   nfs: replace rema...
348
349
  	dprintk("%s: enter
  ", __func__);
f7b422b17   David Howells   NFS: Split fs/nfs...
350
351
352
353
354
355
356
357
358
359
  
  	page = alloc_page(GFP_KERNEL);
  	if (page == NULL)
  		goto out;
  
  	fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
  	if (fs_locations == NULL)
  		goto out_free;
  
  	/* Get locations */
54ceac451   David Howells   NFS: Share NFS su...
360
  	mnt = ERR_PTR(-ENOENT);
f7b422b17   David Howells   NFS: Split fs/nfs...
361
  	parent = dget_parent(dentry);
6de1472f1   Al Viro   nfs: use %p[dD] i...
362
363
364
  	dprintk("%s: getting locations for %pd2
  ",
  		__func__, dentry);
54ceac451   David Howells   NFS: Share NFS su...
365

2b0143b5c   David Howells   VFS: normal files...
366
  	err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page);
f7b422b17   David Howells   NFS: Split fs/nfs...
367
  	dput(parent);
54ceac451   David Howells   NFS: Share NFS su...
368
369
  	if (err != 0 ||
  	    fs_locations->nlocations <= 0 ||
f7b422b17   David Howells   NFS: Split fs/nfs...
370
371
  	    fs_locations->fs_path.ncomponents <= 0)
  		goto out_free;
f8ad9c4ba   Al Viro   nfs: nfs_do_{ref,...
372
  	mnt = nfs_follow_referral(dentry, fs_locations);
f7b422b17   David Howells   NFS: Split fs/nfs...
373
374
375
376
  out_free:
  	__free_page(page);
  	kfree(fs_locations);
  out:
3110ff804   Harvey Harrison   nfs: replace rema...
377
378
  	dprintk("%s: done
  ", __func__);
f7b422b17   David Howells   NFS: Split fs/nfs...
379
380
  	return mnt;
  }
281cad46b   Bryan Schumaker   NFS: Create a sub...
381
382
383
384
  
  struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
  			       struct nfs_fh *fh, struct nfs_fattr *fattr)
  {
47040da3c   Trond Myklebust   NFSv4: Allow secu...
385
  	rpc_authflavor_t flavor = server->client->cl_auth->au_flavor;
281cad46b   Bryan Schumaker   NFS: Create a sub...
386
  	struct dentry *parent = dget_parent(dentry);
2b0143b5c   David Howells   VFS: normal files...
387
  	struct inode *dir = d_inode(parent);
47040da3c   Trond Myklebust   NFSv4: Allow secu...
388
  	struct qstr *name = &dentry->d_name;
281cad46b   Bryan Schumaker   NFS: Create a sub...
389
390
391
392
  	struct rpc_clnt *client;
  	struct vfsmount *mnt;
  
  	/* Look it up again to get its attributes and sec flavor */
47040da3c   Trond Myklebust   NFSv4: Allow secu...
393
  	client = nfs4_proc_lookup_mountpoint(dir, name, fh, fattr);
281cad46b   Bryan Schumaker   NFS: Create a sub...
394
395
396
  	dput(parent);
  	if (IS_ERR(client))
  		return ERR_CAST(client);
47040da3c   Trond Myklebust   NFSv4: Allow secu...
397
  	if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
281cad46b   Bryan Schumaker   NFS: Create a sub...
398
  		mnt = nfs_do_refmount(client, dentry);
47040da3c   Trond Myklebust   NFSv4: Allow secu...
399
400
  		goto out;
  	}
281cad46b   Bryan Schumaker   NFS: Create a sub...
401

47040da3c   Trond Myklebust   NFSv4: Allow secu...
402
403
  	if (client->cl_auth->au_flavor != flavor)
  		flavor = client->cl_auth->au_flavor;
47040da3c   Trond Myklebust   NFSv4: Allow secu...
404
405
  	mnt = nfs_do_submount(dentry, fh, fattr, flavor);
  out:
281cad46b   Bryan Schumaker   NFS: Create a sub...
406
407
408
  	rpc_shutdown_client(client);
  	return mnt;
  }
800c06a5b   Chuck Lever   NFS: Add function...
409
410
411
412
413
414
415
416
417
418
419
  
  /*
   * 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...
420
  	struct net *net = rpc_net_ns(server->client);
800c06a5b   Chuck Lever   NFS: Add function...
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
  	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...
442
  						sap, addr_bufsize, net);
800c06a5b   Chuck Lever   NFS: Add function...
443
444
445
446
447
448
449
450
  		if (salen == 0)
  			continue;
  		rpc_set_port(sap, NFS_PORT);
  
  		error = -ENOMEM;
  		hostname = kstrndup(buf->data, buf->len, GFP_KERNEL);
  		if (hostname == NULL)
  			break;
292f503ca   Trond Myklebust   NFSv4: Use the co...
451
  		error = nfs4_update_server(server, hostname, sap, salen, net);
800c06a5b   Chuck Lever   NFS: Add function...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  		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;
  }