xref: /openbmc/linux/fs/nfs/nfs4namespace.c (revision 6de1472f)
1f7b422b1SDavid Howells /*
2f7b422b1SDavid Howells  * linux/fs/nfs/nfs4namespace.c
3f7b422b1SDavid Howells  *
4f7b422b1SDavid Howells  * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com>
554ceac45SDavid Howells  * - Modified by David Howells <dhowells@redhat.com>
6f7b422b1SDavid Howells  *
7f7b422b1SDavid Howells  * NFSv4 namespace
8f7b422b1SDavid Howells  */
9f7b422b1SDavid Howells 
10f7b422b1SDavid Howells #include <linux/dcache.h>
11f7b422b1SDavid Howells #include <linux/mount.h>
12f7b422b1SDavid Howells #include <linux/namei.h>
13f7b422b1SDavid Howells #include <linux/nfs_fs.h>
1447040da3STrond Myklebust #include <linux/nfs_mount.h>
155a0e3ad6STejun Heo #include <linux/slab.h>
16f7b422b1SDavid Howells #include <linux/string.h>
17f7b422b1SDavid Howells #include <linux/sunrpc/clnt.h>
185976687aSJeff Layton #include <linux/sunrpc/addr.h>
19f7b422b1SDavid Howells #include <linux/vfs.h>
20f7b422b1SDavid Howells #include <linux/inet.h>
21f7b422b1SDavid Howells #include "internal.h"
22c228fd3aSTrond Myklebust #include "nfs4_fs.h"
237d7ea882STrond Myklebust #include "dns_resolve.h"
24f7b422b1SDavid Howells 
25f7b422b1SDavid Howells #define NFSDBG_FACILITY		NFSDBG_VFS
26f7b422b1SDavid Howells 
27f7b422b1SDavid Howells /*
28ef95d31eSTrond Myklebust  * Convert the NFSv4 pathname components into a standard posix path.
29ef95d31eSTrond Myklebust  *
30ef95d31eSTrond Myklebust  * Note that the resulting string will be placed at the end of the buffer
31f7b422b1SDavid Howells  */
32509de811SDavid Howells static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
33f7b422b1SDavid Howells 					 char *buffer, ssize_t buflen)
34f7b422b1SDavid Howells {
35f7b422b1SDavid Howells 	char *end = buffer + buflen;
36f7b422b1SDavid Howells 	int n;
37f7b422b1SDavid Howells 
38f7b422b1SDavid Howells 	*--end = '\0';
39f7b422b1SDavid Howells 	buflen--;
40f7b422b1SDavid Howells 
41f7b422b1SDavid Howells 	n = pathname->ncomponents;
42f7b422b1SDavid Howells 	while (--n >= 0) {
43509de811SDavid Howells 		const struct nfs4_string *component = &pathname->components[n];
44f7b422b1SDavid Howells 		buflen -= component->len + 1;
45f7b422b1SDavid Howells 		if (buflen < 0)
46f7b422b1SDavid Howells 			goto Elong;
47f7b422b1SDavid Howells 		end -= component->len;
48f7b422b1SDavid Howells 		memcpy(end, component->data, component->len);
49f7b422b1SDavid Howells 		*--end = '/';
50f7b422b1SDavid Howells 	}
51f7b422b1SDavid Howells 	return end;
52f7b422b1SDavid Howells Elong:
53f7b422b1SDavid Howells 	return ERR_PTR(-ENAMETOOLONG);
54f7b422b1SDavid Howells }
55f7b422b1SDavid Howells 
5654ceac45SDavid Howells /*
571aba1567SWeston Andros Adamson  * return the path component of "<server>:<path>"
581aba1567SWeston Andros Adamson  *  nfspath - the "<server>:<path>" string
591aba1567SWeston Andros Adamson  *  end - one past the last char that could contain "<server>:"
601aba1567SWeston Andros Adamson  * returns NULL on failure
611aba1567SWeston Andros Adamson  */
621aba1567SWeston Andros Adamson static char *nfs_path_component(const char *nfspath, const char *end)
631aba1567SWeston Andros Adamson {
641aba1567SWeston Andros Adamson 	char *p;
651aba1567SWeston Andros Adamson 
661aba1567SWeston Andros Adamson 	if (*nfspath == '[') {
671aba1567SWeston Andros Adamson 		/* parse [] escaped IPv6 addrs */
681aba1567SWeston Andros Adamson 		p = strchr(nfspath, ']');
691aba1567SWeston Andros Adamson 		if (p != NULL && ++p < end && *p == ':')
701aba1567SWeston Andros Adamson 			return p + 1;
711aba1567SWeston Andros Adamson 	} else {
721aba1567SWeston Andros Adamson 		/* otherwise split on first colon */
731aba1567SWeston Andros Adamson 		p = strchr(nfspath, ':');
741aba1567SWeston Andros Adamson 		if (p != NULL && p < end)
751aba1567SWeston Andros Adamson 			return p + 1;
761aba1567SWeston Andros Adamson 	}
771aba1567SWeston Andros Adamson 	return NULL;
781aba1567SWeston Andros Adamson }
791aba1567SWeston Andros Adamson 
801aba1567SWeston Andros Adamson /*
8154ceac45SDavid Howells  * Determine the mount path as a string
8254ceac45SDavid Howells  */
83b514f872SAl Viro static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
8454ceac45SDavid Howells {
85b514f872SAl Viro 	char *limit;
8697a54868SBen Hutchings 	char *path = nfs_path(&limit, dentry, buffer, buflen,
8797a54868SBen Hutchings 			      NFS_PATH_CANONICAL);
88b514f872SAl Viro 	if (!IS_ERR(path)) {
891aba1567SWeston Andros Adamson 		char *path_component = nfs_path_component(path, limit);
901aba1567SWeston Andros Adamson 		if (path_component)
911aba1567SWeston Andros Adamson 			return path_component;
92b514f872SAl Viro 	}
93b514f872SAl Viro 	return path;
9454ceac45SDavid Howells }
9554ceac45SDavid Howells 
9654ceac45SDavid Howells /*
9754ceac45SDavid Howells  * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
9854ceac45SDavid Howells  * believe to be the server path to this dentry
9954ceac45SDavid Howells  */
100b514f872SAl Viro static int nfs4_validate_fspath(struct dentry *dentry,
10154ceac45SDavid Howells 				const struct nfs4_fs_locations *locations,
10254ceac45SDavid Howells 				char *page, char *page2)
10354ceac45SDavid Howells {
10454ceac45SDavid Howells 	const char *path, *fs_path;
10554ceac45SDavid Howells 
106b514f872SAl Viro 	path = nfs4_path(dentry, page, PAGE_SIZE);
10754ceac45SDavid Howells 	if (IS_ERR(path))
10854ceac45SDavid Howells 		return PTR_ERR(path);
10954ceac45SDavid Howells 
11054ceac45SDavid Howells 	fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
11154ceac45SDavid Howells 	if (IS_ERR(fs_path))
11254ceac45SDavid Howells 		return PTR_ERR(fs_path);
11354ceac45SDavid Howells 
11454ceac45SDavid Howells 	if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
11554ceac45SDavid Howells 		dprintk("%s: path %s does not begin with fsroot %s\n",
1163110ff80SHarvey Harrison 			__func__, path, fs_path);
11754ceac45SDavid Howells 		return -ENOENT;
11854ceac45SDavid Howells 	}
11954ceac45SDavid Howells 
12054ceac45SDavid Howells 	return 0;
12154ceac45SDavid Howells }
12254ceac45SDavid Howells 
1237d7ea882STrond Myklebust static size_t nfs_parse_server_name(char *string, size_t len,
1241b340d01SStanislav Kinsbursky 		struct sockaddr *sa, size_t salen, struct nfs_server *server)
1257d7ea882STrond Myklebust {
1262446ab60STrond Myklebust 	struct net *net = rpc_net_ns(server->client);
1277d7ea882STrond Myklebust 	ssize_t ret;
1287d7ea882STrond Myklebust 
12933faaa38SStanislav Kinsbursky 	ret = rpc_pton(net, string, len, sa, salen);
1307d7ea882STrond Myklebust 	if (ret == 0) {
13133faaa38SStanislav Kinsbursky 		ret = nfs_dns_resolve_name(net, string, len, sa, salen);
1327d7ea882STrond Myklebust 		if (ret < 0)
1337d7ea882STrond Myklebust 			ret = 0;
1347d7ea882STrond Myklebust 	}
1357d7ea882STrond Myklebust 	return ret;
1367d7ea882STrond Myklebust }
1377d7ea882STrond Myklebust 
1389568c5e9SChuck Lever /**
1399568c5e9SChuck Lever  * nfs_find_best_sec - Find a security mechanism supported locally
1409568c5e9SChuck Lever  * @flavors: List of security tuples returned by SECINFO procedure
1419568c5e9SChuck Lever  *
1429568c5e9SChuck Lever  * Return the pseudoflavor of the first security mechanism in
1439568c5e9SChuck Lever  * "flavors" that is locally supported.  Return RPC_AUTH_UNIX if
1449568c5e9SChuck Lever  * no matching flavor is found in the array.  The "flavors" array
1459568c5e9SChuck Lever  * is searched in the order returned from the server, per RFC 3530
1469568c5e9SChuck Lever  * recommendation.
1479568c5e9SChuck Lever  */
1482671bfc3SBryan Schumaker rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
1492671bfc3SBryan Schumaker {
1509568c5e9SChuck Lever 	rpc_authflavor_t pseudoflavor;
1519568c5e9SChuck Lever 	struct nfs4_secinfo4 *secinfo;
152fb15b26fSChuck Lever 	unsigned int i;
1532671bfc3SBryan Schumaker 
1542671bfc3SBryan Schumaker 	for (i = 0; i < flavors->num_flavors; i++) {
1559568c5e9SChuck Lever 		secinfo = &flavors->flavors[i];
1562671bfc3SBryan Schumaker 
1579568c5e9SChuck Lever 		switch (secinfo->flavor) {
1589568c5e9SChuck Lever 		case RPC_AUTH_NULL:
1599568c5e9SChuck Lever 		case RPC_AUTH_UNIX:
1609568c5e9SChuck Lever 		case RPC_AUTH_GSS:
1619568c5e9SChuck Lever 			pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
1629568c5e9SChuck Lever 							&secinfo->flavor_info);
1639568c5e9SChuck Lever 			if (pseudoflavor != RPC_AUTH_MAXFLAVOR)
1642671bfc3SBryan Schumaker 				return pseudoflavor;
1659568c5e9SChuck Lever 			break;
1669568c5e9SChuck Lever 		}
1679568c5e9SChuck Lever 	}
1689568c5e9SChuck Lever 
1699568c5e9SChuck Lever 	return RPC_AUTH_UNIX;
1702671bfc3SBryan Schumaker }
1712671bfc3SBryan Schumaker 
17272de53ecSBryan Schumaker static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
17372de53ecSBryan Schumaker {
17472de53ecSBryan Schumaker 	struct page *page;
17572de53ecSBryan Schumaker 	struct nfs4_secinfo_flavors *flavors;
17672de53ecSBryan Schumaker 	rpc_authflavor_t flavor;
17772de53ecSBryan Schumaker 	int err;
17872de53ecSBryan Schumaker 
17972de53ecSBryan Schumaker 	page = alloc_page(GFP_KERNEL);
18072de53ecSBryan Schumaker 	if (!page)
18172de53ecSBryan Schumaker 		return -ENOMEM;
18272de53ecSBryan Schumaker 	flavors = page_address(page);
18372de53ecSBryan Schumaker 
18472de53ecSBryan Schumaker 	err = nfs4_proc_secinfo(inode, name, flavors);
18572de53ecSBryan Schumaker 	if (err < 0) {
18672de53ecSBryan Schumaker 		flavor = err;
18772de53ecSBryan Schumaker 		goto out;
18872de53ecSBryan Schumaker 	}
18972de53ecSBryan Schumaker 
19072de53ecSBryan Schumaker 	flavor = nfs_find_best_sec(flavors);
19172de53ecSBryan Schumaker 
19272de53ecSBryan Schumaker out:
19372de53ecSBryan Schumaker 	put_page(page);
19472de53ecSBryan Schumaker 	return flavor;
19572de53ecSBryan Schumaker }
19672de53ecSBryan Schumaker 
19772de53ecSBryan Schumaker /*
19872de53ecSBryan Schumaker  * Please call rpc_shutdown_client() when you are done with this client.
19972de53ecSBryan Schumaker  */
20072de53ecSBryan Schumaker struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode,
20172de53ecSBryan Schumaker 					struct qstr *name)
20272de53ecSBryan Schumaker {
20372de53ecSBryan Schumaker 	rpc_authflavor_t flavor;
20472de53ecSBryan Schumaker 
20572de53ecSBryan Schumaker 	flavor = nfs4_negotiate_security(inode, name);
2065f23eff3SBenny Halevy 	if ((int)flavor < 0)
20762d98c93SNeilBrown 		return ERR_PTR((int)flavor);
20872de53ecSBryan Schumaker 
209ba9b584cSChuck Lever 	return rpc_clone_client_set_auth(clnt, flavor);
21072de53ecSBryan Schumaker }
21172de53ecSBryan Schumaker 
2124ada29d5SJ. Bruce Fields static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
2134ada29d5SJ. Bruce Fields 				     char *page, char *page2,
2144ada29d5SJ. Bruce Fields 				     const struct nfs4_fs_location *location)
2154ada29d5SJ. Bruce Fields {
216364d015eSTrond Myklebust 	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
2174ada29d5SJ. Bruce Fields 	struct vfsmount *mnt = ERR_PTR(-ENOENT);
2184ada29d5SJ. Bruce Fields 	char *mnt_path;
219ef95d31eSTrond Myklebust 	unsigned int maxbuflen;
220460cdbc8SJ. Bruce Fields 	unsigned int s;
2214ada29d5SJ. Bruce Fields 
2224ada29d5SJ. Bruce Fields 	mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
2234ada29d5SJ. Bruce Fields 	if (IS_ERR(mnt_path))
224517be09dSTrond Myklebust 		return ERR_CAST(mnt_path);
2254ada29d5SJ. Bruce Fields 	mountdata->mnt_path = mnt_path;
226ef95d31eSTrond Myklebust 	maxbuflen = mnt_path - 1 - page2;
2274ada29d5SJ. Bruce Fields 
228364d015eSTrond Myklebust 	mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
229364d015eSTrond Myklebust 	if (mountdata->addr == NULL)
230364d015eSTrond Myklebust 		return ERR_PTR(-ENOMEM);
231364d015eSTrond Myklebust 
232460cdbc8SJ. Bruce Fields 	for (s = 0; s < location->nservers; s++) {
233ea31a443SJ. Bruce Fields 		const struct nfs4_string *buf = &location->servers[s];
2344ada29d5SJ. Bruce Fields 
235ef95d31eSTrond Myklebust 		if (buf->len <= 0 || buf->len >= maxbuflen)
2364ada29d5SJ. Bruce Fields 			continue;
2374ada29d5SJ. Bruce Fields 
238ea31a443SJ. Bruce Fields 		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
239ea31a443SJ. Bruce Fields 			continue;
240517be09dSTrond Myklebust 
241517be09dSTrond Myklebust 		mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
2421b340d01SStanislav Kinsbursky 				mountdata->addr, addr_bufsize,
2431b340d01SStanislav Kinsbursky 				NFS_SB(mountdata->sb));
24453a0b9c4SChuck Lever 		if (mountdata->addrlen == 0)
245ea31a443SJ. Bruce Fields 			continue;
246517be09dSTrond Myklebust 
247ec6ee612SChuck Lever 		rpc_set_port(mountdata->addr, NFS_PORT);
248ea31a443SJ. Bruce Fields 
249ef95d31eSTrond Myklebust 		memcpy(page2, buf->data, buf->len);
250ef95d31eSTrond Myklebust 		page2[buf->len] = '\0';
251ea31a443SJ. Bruce Fields 		mountdata->hostname = page2;
2524ada29d5SJ. Bruce Fields 
2534ada29d5SJ. Bruce Fields 		snprintf(page, PAGE_SIZE, "%s:%s",
2544ada29d5SJ. Bruce Fields 				mountdata->hostname,
2554ada29d5SJ. Bruce Fields 				mountdata->mnt_path);
2564ada29d5SJ. Bruce Fields 
2574ada29d5SJ. Bruce Fields 		mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata);
2584ada29d5SJ. Bruce Fields 		if (!IS_ERR(mnt))
2594ada29d5SJ. Bruce Fields 			break;
2604ada29d5SJ. Bruce Fields 	}
261364d015eSTrond Myklebust 	kfree(mountdata->addr);
2624ada29d5SJ. Bruce Fields 	return mnt;
2634ada29d5SJ. Bruce Fields }
2644ada29d5SJ. Bruce Fields 
265f7b422b1SDavid Howells /**
266f7b422b1SDavid Howells  * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
267f7b422b1SDavid Howells  * @dentry - parent directory
2683f43c666SChuck Lever  * @locations - array of NFSv4 server location information
269f7b422b1SDavid Howells  *
270f7b422b1SDavid Howells  */
271f8ad9c4bSAl Viro static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
272509de811SDavid Howells 					    const struct nfs4_fs_locations *locations)
273f7b422b1SDavid Howells {
274f7b422b1SDavid Howells 	struct vfsmount *mnt = ERR_PTR(-ENOENT);
275f7b422b1SDavid Howells 	struct nfs_clone_mount mountdata = {
276f8ad9c4bSAl Viro 		.sb = dentry->d_sb,
277f7b422b1SDavid Howells 		.dentry = dentry,
278f8ad9c4bSAl Viro 		.authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
279f7b422b1SDavid Howells 	};
28054ceac45SDavid Howells 	char *page = NULL, *page2 = NULL;
2813f43c666SChuck Lever 	int loc, error;
282f7b422b1SDavid Howells 
283f7b422b1SDavid Howells 	if (locations == NULL || locations->nlocations <= 0)
284f7b422b1SDavid Howells 		goto out;
285f7b422b1SDavid Howells 
2866de1472fSAl Viro 	dprintk("%s: referral at %pd2\n", __func__, dentry);
287f7b422b1SDavid Howells 
288f7b422b1SDavid Howells 	page = (char *) __get_free_page(GFP_USER);
28954ceac45SDavid Howells 	if (!page)
290f7b422b1SDavid Howells 		goto out;
29154ceac45SDavid Howells 
292f7b422b1SDavid Howells 	page2 = (char *) __get_free_page(GFP_USER);
29354ceac45SDavid Howells 	if (!page2)
294f7b422b1SDavid Howells 		goto out;
295f7b422b1SDavid Howells 
29654ceac45SDavid Howells 	/* Ensure fs path is a prefix of current dentry path */
297b514f872SAl Viro 	error = nfs4_validate_fspath(dentry, locations, page, page2);
29854ceac45SDavid Howells 	if (error < 0) {
29954ceac45SDavid Howells 		mnt = ERR_PTR(error);
30054ceac45SDavid Howells 		goto out;
301f7b422b1SDavid Howells 	}
302f7b422b1SDavid Howells 
303460cdbc8SJ. Bruce Fields 	for (loc = 0; loc < locations->nlocations; loc++) {
304509de811SDavid Howells 		const struct nfs4_fs_location *location = &locations->locations[loc];
305f7b422b1SDavid Howells 
306f7b422b1SDavid Howells 		if (location == NULL || location->nservers <= 0 ||
307460cdbc8SJ. Bruce Fields 		    location->rootpath.ncomponents == 0)
308f7b422b1SDavid Howells 			continue;
309f7b422b1SDavid Howells 
3104ada29d5SJ. Bruce Fields 		mnt = try_location(&mountdata, page, page2, location);
3114ada29d5SJ. Bruce Fields 		if (!IS_ERR(mnt))
312f7b422b1SDavid Howells 			break;
313f7b422b1SDavid Howells 	}
314f7b422b1SDavid Howells 
31554ceac45SDavid Howells out:
316f7b422b1SDavid Howells 	free_page((unsigned long) page);
317f7b422b1SDavid Howells 	free_page((unsigned long) page2);
3183110ff80SHarvey Harrison 	dprintk("%s: done\n", __func__);
319f7b422b1SDavid Howells 	return mnt;
320f7b422b1SDavid Howells }
321f7b422b1SDavid Howells 
322f7b422b1SDavid Howells /*
323f7b422b1SDavid Howells  * nfs_do_refmount - handle crossing a referral on server
324f7b422b1SDavid Howells  * @dentry - dentry of referral
325f7b422b1SDavid Howells  *
326f7b422b1SDavid Howells  */
327281cad46SBryan Schumaker static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
328f7b422b1SDavid Howells {
32954ceac45SDavid Howells 	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
330f7b422b1SDavid Howells 	struct dentry *parent;
331f7b422b1SDavid Howells 	struct nfs4_fs_locations *fs_locations = NULL;
332f7b422b1SDavid Howells 	struct page *page;
333f7b422b1SDavid Howells 	int err;
334f7b422b1SDavid Howells 
335f7b422b1SDavid Howells 	/* BUG_ON(IS_ROOT(dentry)); */
3363110ff80SHarvey Harrison 	dprintk("%s: enter\n", __func__);
337f7b422b1SDavid Howells 
338f7b422b1SDavid Howells 	page = alloc_page(GFP_KERNEL);
339f7b422b1SDavid Howells 	if (page == NULL)
340f7b422b1SDavid Howells 		goto out;
341f7b422b1SDavid Howells 
342f7b422b1SDavid Howells 	fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
343f7b422b1SDavid Howells 	if (fs_locations == NULL)
344f7b422b1SDavid Howells 		goto out_free;
345f7b422b1SDavid Howells 
346f7b422b1SDavid Howells 	/* Get locations */
34754ceac45SDavid Howells 	mnt = ERR_PTR(-ENOENT);
34854ceac45SDavid Howells 
349f7b422b1SDavid Howells 	parent = dget_parent(dentry);
3506de1472fSAl Viro 	dprintk("%s: getting locations for %pd2\n",
3516de1472fSAl Viro 		__func__, dentry);
35254ceac45SDavid Howells 
353f05d147fSBryan Schumaker 	err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page);
354f7b422b1SDavid Howells 	dput(parent);
35554ceac45SDavid Howells 	if (err != 0 ||
35654ceac45SDavid Howells 	    fs_locations->nlocations <= 0 ||
357f7b422b1SDavid Howells 	    fs_locations->fs_path.ncomponents <= 0)
358f7b422b1SDavid Howells 		goto out_free;
359f7b422b1SDavid Howells 
360f8ad9c4bSAl Viro 	mnt = nfs_follow_referral(dentry, fs_locations);
361f7b422b1SDavid Howells out_free:
362f7b422b1SDavid Howells 	__free_page(page);
363f7b422b1SDavid Howells 	kfree(fs_locations);
364f7b422b1SDavid Howells out:
3653110ff80SHarvey Harrison 	dprintk("%s: done\n", __func__);
366f7b422b1SDavid Howells 	return mnt;
367f7b422b1SDavid Howells }
368281cad46SBryan Schumaker 
369281cad46SBryan Schumaker struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
370281cad46SBryan Schumaker 			       struct nfs_fh *fh, struct nfs_fattr *fattr)
371281cad46SBryan Schumaker {
37247040da3STrond Myklebust 	rpc_authflavor_t flavor = server->client->cl_auth->au_flavor;
373281cad46SBryan Schumaker 	struct dentry *parent = dget_parent(dentry);
37447040da3STrond Myklebust 	struct inode *dir = parent->d_inode;
37547040da3STrond Myklebust 	struct qstr *name = &dentry->d_name;
376281cad46SBryan Schumaker 	struct rpc_clnt *client;
377281cad46SBryan Schumaker 	struct vfsmount *mnt;
378281cad46SBryan Schumaker 
379281cad46SBryan Schumaker 	/* Look it up again to get its attributes and sec flavor */
38047040da3STrond Myklebust 	client = nfs4_proc_lookup_mountpoint(dir, name, fh, fattr);
381281cad46SBryan Schumaker 	dput(parent);
382281cad46SBryan Schumaker 	if (IS_ERR(client))
383281cad46SBryan Schumaker 		return ERR_CAST(client);
384281cad46SBryan Schumaker 
38547040da3STrond Myklebust 	if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
386281cad46SBryan Schumaker 		mnt = nfs_do_refmount(client, dentry);
38747040da3STrond Myklebust 		goto out;
38847040da3STrond Myklebust 	}
389281cad46SBryan Schumaker 
39047040da3STrond Myklebust 	if (client->cl_auth->au_flavor != flavor)
39147040da3STrond Myklebust 		flavor = client->cl_auth->au_flavor;
39247040da3STrond Myklebust 	else if (!(server->flags & NFS_MOUNT_SECFLAVOUR)) {
39347040da3STrond Myklebust 		rpc_authflavor_t new = nfs4_negotiate_security(dir, name);
39447040da3STrond Myklebust 		if ((int)new >= 0)
39547040da3STrond Myklebust 			flavor = new;
39647040da3STrond Myklebust 	}
39747040da3STrond Myklebust 	mnt = nfs_do_submount(dentry, fh, fattr, flavor);
39847040da3STrond Myklebust out:
399281cad46SBryan Schumaker 	rpc_shutdown_client(client);
400281cad46SBryan Schumaker 	return mnt;
401281cad46SBryan Schumaker }
402