xref: /openbmc/linux/fs/nfs/nfs4namespace.c (revision fb15b26f)
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>
145a0e3ad6STejun Heo #include <linux/slab.h>
15f7b422b1SDavid Howells #include <linux/string.h>
16f7b422b1SDavid Howells #include <linux/sunrpc/clnt.h>
175976687aSJeff Layton #include <linux/sunrpc/addr.h>
18f7b422b1SDavid Howells #include <linux/vfs.h>
19f7b422b1SDavid Howells #include <linux/inet.h>
20f7b422b1SDavid Howells #include "internal.h"
21c228fd3aSTrond Myklebust #include "nfs4_fs.h"
227d7ea882STrond Myklebust #include "dns_resolve.h"
23f7b422b1SDavid Howells 
24f7b422b1SDavid Howells #define NFSDBG_FACILITY		NFSDBG_VFS
25f7b422b1SDavid Howells 
26f7b422b1SDavid Howells /*
27ef95d31eSTrond Myklebust  * Convert the NFSv4 pathname components into a standard posix path.
28ef95d31eSTrond Myklebust  *
29ef95d31eSTrond Myklebust  * Note that the resulting string will be placed at the end of the buffer
30f7b422b1SDavid Howells  */
31509de811SDavid Howells static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
32f7b422b1SDavid Howells 					 char *buffer, ssize_t buflen)
33f7b422b1SDavid Howells {
34f7b422b1SDavid Howells 	char *end = buffer + buflen;
35f7b422b1SDavid Howells 	int n;
36f7b422b1SDavid Howells 
37f7b422b1SDavid Howells 	*--end = '\0';
38f7b422b1SDavid Howells 	buflen--;
39f7b422b1SDavid Howells 
40f7b422b1SDavid Howells 	n = pathname->ncomponents;
41f7b422b1SDavid Howells 	while (--n >= 0) {
42509de811SDavid Howells 		const struct nfs4_string *component = &pathname->components[n];
43f7b422b1SDavid Howells 		buflen -= component->len + 1;
44f7b422b1SDavid Howells 		if (buflen < 0)
45f7b422b1SDavid Howells 			goto Elong;
46f7b422b1SDavid Howells 		end -= component->len;
47f7b422b1SDavid Howells 		memcpy(end, component->data, component->len);
48f7b422b1SDavid Howells 		*--end = '/';
49f7b422b1SDavid Howells 	}
50f7b422b1SDavid Howells 	return end;
51f7b422b1SDavid Howells Elong:
52f7b422b1SDavid Howells 	return ERR_PTR(-ENAMETOOLONG);
53f7b422b1SDavid Howells }
54f7b422b1SDavid Howells 
5554ceac45SDavid Howells /*
561aba1567SWeston Andros Adamson  * return the path component of "<server>:<path>"
571aba1567SWeston Andros Adamson  *  nfspath - the "<server>:<path>" string
581aba1567SWeston Andros Adamson  *  end - one past the last char that could contain "<server>:"
591aba1567SWeston Andros Adamson  * returns NULL on failure
601aba1567SWeston Andros Adamson  */
611aba1567SWeston Andros Adamson static char *nfs_path_component(const char *nfspath, const char *end)
621aba1567SWeston Andros Adamson {
631aba1567SWeston Andros Adamson 	char *p;
641aba1567SWeston Andros Adamson 
651aba1567SWeston Andros Adamson 	if (*nfspath == '[') {
661aba1567SWeston Andros Adamson 		/* parse [] escaped IPv6 addrs */
671aba1567SWeston Andros Adamson 		p = strchr(nfspath, ']');
681aba1567SWeston Andros Adamson 		if (p != NULL && ++p < end && *p == ':')
691aba1567SWeston Andros Adamson 			return p + 1;
701aba1567SWeston Andros Adamson 	} else {
711aba1567SWeston Andros Adamson 		/* otherwise split on first colon */
721aba1567SWeston Andros Adamson 		p = strchr(nfspath, ':');
731aba1567SWeston Andros Adamson 		if (p != NULL && p < end)
741aba1567SWeston Andros Adamson 			return p + 1;
751aba1567SWeston Andros Adamson 	}
761aba1567SWeston Andros Adamson 	return NULL;
771aba1567SWeston Andros Adamson }
781aba1567SWeston Andros Adamson 
791aba1567SWeston Andros Adamson /*
8054ceac45SDavid Howells  * Determine the mount path as a string
8154ceac45SDavid Howells  */
82b514f872SAl Viro static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen)
8354ceac45SDavid Howells {
84b514f872SAl Viro 	char *limit;
8597a54868SBen Hutchings 	char *path = nfs_path(&limit, dentry, buffer, buflen,
8697a54868SBen Hutchings 			      NFS_PATH_CANONICAL);
87b514f872SAl Viro 	if (!IS_ERR(path)) {
881aba1567SWeston Andros Adamson 		char *path_component = nfs_path_component(path, limit);
891aba1567SWeston Andros Adamson 		if (path_component)
901aba1567SWeston Andros Adamson 			return path_component;
91b514f872SAl Viro 	}
92b514f872SAl Viro 	return path;
9354ceac45SDavid Howells }
9454ceac45SDavid Howells 
9554ceac45SDavid Howells /*
9654ceac45SDavid Howells  * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we
9754ceac45SDavid Howells  * believe to be the server path to this dentry
9854ceac45SDavid Howells  */
99b514f872SAl Viro static int nfs4_validate_fspath(struct dentry *dentry,
10054ceac45SDavid Howells 				const struct nfs4_fs_locations *locations,
10154ceac45SDavid Howells 				char *page, char *page2)
10254ceac45SDavid Howells {
10354ceac45SDavid Howells 	const char *path, *fs_path;
10454ceac45SDavid Howells 
105b514f872SAl Viro 	path = nfs4_path(dentry, page, PAGE_SIZE);
10654ceac45SDavid Howells 	if (IS_ERR(path))
10754ceac45SDavid Howells 		return PTR_ERR(path);
10854ceac45SDavid Howells 
10954ceac45SDavid Howells 	fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
11054ceac45SDavid Howells 	if (IS_ERR(fs_path))
11154ceac45SDavid Howells 		return PTR_ERR(fs_path);
11254ceac45SDavid Howells 
11354ceac45SDavid Howells 	if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
11454ceac45SDavid Howells 		dprintk("%s: path %s does not begin with fsroot %s\n",
1153110ff80SHarvey Harrison 			__func__, path, fs_path);
11654ceac45SDavid Howells 		return -ENOENT;
11754ceac45SDavid Howells 	}
11854ceac45SDavid Howells 
11954ceac45SDavid Howells 	return 0;
12054ceac45SDavid Howells }
12154ceac45SDavid Howells 
1227d7ea882STrond Myklebust static size_t nfs_parse_server_name(char *string, size_t len,
1231b340d01SStanislav Kinsbursky 		struct sockaddr *sa, size_t salen, struct nfs_server *server)
1247d7ea882STrond Myklebust {
1252446ab60STrond Myklebust 	struct net *net = rpc_net_ns(server->client);
1267d7ea882STrond Myklebust 	ssize_t ret;
1277d7ea882STrond Myklebust 
12833faaa38SStanislav Kinsbursky 	ret = rpc_pton(net, string, len, sa, salen);
1297d7ea882STrond Myklebust 	if (ret == 0) {
13033faaa38SStanislav Kinsbursky 		ret = nfs_dns_resolve_name(net, string, len, sa, salen);
1317d7ea882STrond Myklebust 		if (ret < 0)
1327d7ea882STrond Myklebust 			ret = 0;
1337d7ea882STrond Myklebust 	}
1347d7ea882STrond Myklebust 	return ret;
1357d7ea882STrond Myklebust }
1367d7ea882STrond Myklebust 
1372671bfc3SBryan Schumaker rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
1382671bfc3SBryan Schumaker {
1392671bfc3SBryan Schumaker 	struct gss_api_mech *mech;
1402671bfc3SBryan Schumaker 	struct xdr_netobj oid;
141fb15b26fSChuck Lever 	unsigned int i;
1422671bfc3SBryan Schumaker 	rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
1432671bfc3SBryan Schumaker 
1442671bfc3SBryan Schumaker 	for (i = 0; i < flavors->num_flavors; i++) {
145fb15b26fSChuck Lever 		struct nfs4_secinfo4 *flavor = &flavors->flavors[i];
1462671bfc3SBryan Schumaker 
1472671bfc3SBryan Schumaker 		if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) {
1482671bfc3SBryan Schumaker 			pseudoflavor = flavor->flavor;
1492671bfc3SBryan Schumaker 			break;
1502671bfc3SBryan Schumaker 		} else if (flavor->flavor == RPC_AUTH_GSS) {
151fb15b26fSChuck Lever 			oid.len  = flavor->flavor_info.oid.len;
152fb15b26fSChuck Lever 			oid.data = flavor->flavor_info.oid.data;
1532671bfc3SBryan Schumaker 			mech = gss_mech_get_by_OID(&oid);
1542671bfc3SBryan Schumaker 			if (!mech)
1552671bfc3SBryan Schumaker 				continue;
156fb15b26fSChuck Lever 			pseudoflavor = gss_svc_to_pseudoflavor(mech,
157fb15b26fSChuck Lever 						flavor->flavor_info.service);
1582671bfc3SBryan Schumaker 			gss_mech_put(mech);
1592671bfc3SBryan Schumaker 			break;
1602671bfc3SBryan Schumaker 		}
1612671bfc3SBryan Schumaker 	}
1622671bfc3SBryan Schumaker 
1632671bfc3SBryan Schumaker 	return pseudoflavor;
1642671bfc3SBryan Schumaker }
1652671bfc3SBryan Schumaker 
16672de53ecSBryan Schumaker static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
16772de53ecSBryan Schumaker {
16872de53ecSBryan Schumaker 	struct page *page;
16972de53ecSBryan Schumaker 	struct nfs4_secinfo_flavors *flavors;
17072de53ecSBryan Schumaker 	rpc_authflavor_t flavor;
17172de53ecSBryan Schumaker 	int err;
17272de53ecSBryan Schumaker 
17372de53ecSBryan Schumaker 	page = alloc_page(GFP_KERNEL);
17472de53ecSBryan Schumaker 	if (!page)
17572de53ecSBryan Schumaker 		return -ENOMEM;
17672de53ecSBryan Schumaker 	flavors = page_address(page);
17772de53ecSBryan Schumaker 
17872de53ecSBryan Schumaker 	err = nfs4_proc_secinfo(inode, name, flavors);
17972de53ecSBryan Schumaker 	if (err < 0) {
18072de53ecSBryan Schumaker 		flavor = err;
18172de53ecSBryan Schumaker 		goto out;
18272de53ecSBryan Schumaker 	}
18372de53ecSBryan Schumaker 
18472de53ecSBryan Schumaker 	flavor = nfs_find_best_sec(flavors);
18572de53ecSBryan Schumaker 
18672de53ecSBryan Schumaker out:
18772de53ecSBryan Schumaker 	put_page(page);
18872de53ecSBryan Schumaker 	return flavor;
18972de53ecSBryan Schumaker }
19072de53ecSBryan Schumaker 
19172de53ecSBryan Schumaker /*
19272de53ecSBryan Schumaker  * Please call rpc_shutdown_client() when you are done with this client.
19372de53ecSBryan Schumaker  */
19472de53ecSBryan Schumaker struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode,
19572de53ecSBryan Schumaker 					struct qstr *name)
19672de53ecSBryan Schumaker {
19772de53ecSBryan Schumaker 	rpc_authflavor_t flavor;
19872de53ecSBryan Schumaker 
19972de53ecSBryan Schumaker 	flavor = nfs4_negotiate_security(inode, name);
2005f23eff3SBenny Halevy 	if ((int)flavor < 0)
20162d98c93SNeilBrown 		return ERR_PTR((int)flavor);
20272de53ecSBryan Schumaker 
203ba9b584cSChuck Lever 	return rpc_clone_client_set_auth(clnt, flavor);
20472de53ecSBryan Schumaker }
20572de53ecSBryan Schumaker 
2064ada29d5SJ. Bruce Fields static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
2074ada29d5SJ. Bruce Fields 				     char *page, char *page2,
2084ada29d5SJ. Bruce Fields 				     const struct nfs4_fs_location *location)
2094ada29d5SJ. Bruce Fields {
210364d015eSTrond Myklebust 	const size_t addr_bufsize = sizeof(struct sockaddr_storage);
2114ada29d5SJ. Bruce Fields 	struct vfsmount *mnt = ERR_PTR(-ENOENT);
2124ada29d5SJ. Bruce Fields 	char *mnt_path;
213ef95d31eSTrond Myklebust 	unsigned int maxbuflen;
214460cdbc8SJ. Bruce Fields 	unsigned int s;
2154ada29d5SJ. Bruce Fields 
2164ada29d5SJ. Bruce Fields 	mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
2174ada29d5SJ. Bruce Fields 	if (IS_ERR(mnt_path))
218517be09dSTrond Myklebust 		return ERR_CAST(mnt_path);
2194ada29d5SJ. Bruce Fields 	mountdata->mnt_path = mnt_path;
220ef95d31eSTrond Myklebust 	maxbuflen = mnt_path - 1 - page2;
2214ada29d5SJ. Bruce Fields 
222364d015eSTrond Myklebust 	mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
223364d015eSTrond Myklebust 	if (mountdata->addr == NULL)
224364d015eSTrond Myklebust 		return ERR_PTR(-ENOMEM);
225364d015eSTrond Myklebust 
226460cdbc8SJ. Bruce Fields 	for (s = 0; s < location->nservers; s++) {
227ea31a443SJ. Bruce Fields 		const struct nfs4_string *buf = &location->servers[s];
2284ada29d5SJ. Bruce Fields 
229ef95d31eSTrond Myklebust 		if (buf->len <= 0 || buf->len >= maxbuflen)
2304ada29d5SJ. Bruce Fields 			continue;
2314ada29d5SJ. Bruce Fields 
232ea31a443SJ. Bruce Fields 		if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
233ea31a443SJ. Bruce Fields 			continue;
234517be09dSTrond Myklebust 
235517be09dSTrond Myklebust 		mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
2361b340d01SStanislav Kinsbursky 				mountdata->addr, addr_bufsize,
2371b340d01SStanislav Kinsbursky 				NFS_SB(mountdata->sb));
23853a0b9c4SChuck Lever 		if (mountdata->addrlen == 0)
239ea31a443SJ. Bruce Fields 			continue;
240517be09dSTrond Myklebust 
241ec6ee612SChuck Lever 		rpc_set_port(mountdata->addr, NFS_PORT);
242ea31a443SJ. Bruce Fields 
243ef95d31eSTrond Myklebust 		memcpy(page2, buf->data, buf->len);
244ef95d31eSTrond Myklebust 		page2[buf->len] = '\0';
245ea31a443SJ. Bruce Fields 		mountdata->hostname = page2;
2464ada29d5SJ. Bruce Fields 
2474ada29d5SJ. Bruce Fields 		snprintf(page, PAGE_SIZE, "%s:%s",
2484ada29d5SJ. Bruce Fields 				mountdata->hostname,
2494ada29d5SJ. Bruce Fields 				mountdata->mnt_path);
2504ada29d5SJ. Bruce Fields 
2514ada29d5SJ. Bruce Fields 		mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata);
2524ada29d5SJ. Bruce Fields 		if (!IS_ERR(mnt))
2534ada29d5SJ. Bruce Fields 			break;
2544ada29d5SJ. Bruce Fields 	}
255364d015eSTrond Myklebust 	kfree(mountdata->addr);
2564ada29d5SJ. Bruce Fields 	return mnt;
2574ada29d5SJ. Bruce Fields }
2584ada29d5SJ. Bruce Fields 
259f7b422b1SDavid Howells /**
260f7b422b1SDavid Howells  * nfs_follow_referral - set up mountpoint when hitting a referral on moved error
261f7b422b1SDavid Howells  * @dentry - parent directory
2623f43c666SChuck Lever  * @locations - array of NFSv4 server location information
263f7b422b1SDavid Howells  *
264f7b422b1SDavid Howells  */
265f8ad9c4bSAl Viro static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
266509de811SDavid Howells 					    const struct nfs4_fs_locations *locations)
267f7b422b1SDavid Howells {
268f7b422b1SDavid Howells 	struct vfsmount *mnt = ERR_PTR(-ENOENT);
269f7b422b1SDavid Howells 	struct nfs_clone_mount mountdata = {
270f8ad9c4bSAl Viro 		.sb = dentry->d_sb,
271f7b422b1SDavid Howells 		.dentry = dentry,
272f8ad9c4bSAl Viro 		.authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
273f7b422b1SDavid Howells 	};
27454ceac45SDavid Howells 	char *page = NULL, *page2 = NULL;
2753f43c666SChuck Lever 	int loc, error;
276f7b422b1SDavid Howells 
277f7b422b1SDavid Howells 	if (locations == NULL || locations->nlocations <= 0)
278f7b422b1SDavid Howells 		goto out;
279f7b422b1SDavid Howells 
2803110ff80SHarvey Harrison 	dprintk("%s: referral at %s/%s\n", __func__,
281f7b422b1SDavid Howells 		dentry->d_parent->d_name.name, dentry->d_name.name);
282f7b422b1SDavid Howells 
283f7b422b1SDavid Howells 	page = (char *) __get_free_page(GFP_USER);
28454ceac45SDavid Howells 	if (!page)
285f7b422b1SDavid Howells 		goto out;
28654ceac45SDavid Howells 
287f7b422b1SDavid Howells 	page2 = (char *) __get_free_page(GFP_USER);
28854ceac45SDavid Howells 	if (!page2)
289f7b422b1SDavid Howells 		goto out;
290f7b422b1SDavid Howells 
29154ceac45SDavid Howells 	/* Ensure fs path is a prefix of current dentry path */
292b514f872SAl Viro 	error = nfs4_validate_fspath(dentry, locations, page, page2);
29354ceac45SDavid Howells 	if (error < 0) {
29454ceac45SDavid Howells 		mnt = ERR_PTR(error);
29554ceac45SDavid Howells 		goto out;
296f7b422b1SDavid Howells 	}
297f7b422b1SDavid Howells 
298460cdbc8SJ. Bruce Fields 	for (loc = 0; loc < locations->nlocations; loc++) {
299509de811SDavid Howells 		const struct nfs4_fs_location *location = &locations->locations[loc];
300f7b422b1SDavid Howells 
301f7b422b1SDavid Howells 		if (location == NULL || location->nservers <= 0 ||
302460cdbc8SJ. Bruce Fields 		    location->rootpath.ncomponents == 0)
303f7b422b1SDavid Howells 			continue;
304f7b422b1SDavid Howells 
3054ada29d5SJ. Bruce Fields 		mnt = try_location(&mountdata, page, page2, location);
3064ada29d5SJ. Bruce Fields 		if (!IS_ERR(mnt))
307f7b422b1SDavid Howells 			break;
308f7b422b1SDavid Howells 	}
309f7b422b1SDavid Howells 
31054ceac45SDavid Howells out:
311f7b422b1SDavid Howells 	free_page((unsigned long) page);
312f7b422b1SDavid Howells 	free_page((unsigned long) page2);
3133110ff80SHarvey Harrison 	dprintk("%s: done\n", __func__);
314f7b422b1SDavid Howells 	return mnt;
315f7b422b1SDavid Howells }
316f7b422b1SDavid Howells 
317f7b422b1SDavid Howells /*
318f7b422b1SDavid Howells  * nfs_do_refmount - handle crossing a referral on server
319f7b422b1SDavid Howells  * @dentry - dentry of referral
320f7b422b1SDavid Howells  *
321f7b422b1SDavid Howells  */
322281cad46SBryan Schumaker static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry)
323f7b422b1SDavid Howells {
32454ceac45SDavid Howells 	struct vfsmount *mnt = ERR_PTR(-ENOMEM);
325f7b422b1SDavid Howells 	struct dentry *parent;
326f7b422b1SDavid Howells 	struct nfs4_fs_locations *fs_locations = NULL;
327f7b422b1SDavid Howells 	struct page *page;
328f7b422b1SDavid Howells 	int err;
329f7b422b1SDavid Howells 
330f7b422b1SDavid Howells 	/* BUG_ON(IS_ROOT(dentry)); */
3313110ff80SHarvey Harrison 	dprintk("%s: enter\n", __func__);
332f7b422b1SDavid Howells 
333f7b422b1SDavid Howells 	page = alloc_page(GFP_KERNEL);
334f7b422b1SDavid Howells 	if (page == NULL)
335f7b422b1SDavid Howells 		goto out;
336f7b422b1SDavid Howells 
337f7b422b1SDavid Howells 	fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
338f7b422b1SDavid Howells 	if (fs_locations == NULL)
339f7b422b1SDavid Howells 		goto out_free;
340f7b422b1SDavid Howells 
341f7b422b1SDavid Howells 	/* Get locations */
34254ceac45SDavid Howells 	mnt = ERR_PTR(-ENOENT);
34354ceac45SDavid Howells 
344f7b422b1SDavid Howells 	parent = dget_parent(dentry);
34554ceac45SDavid Howells 	dprintk("%s: getting locations for %s/%s\n",
3463110ff80SHarvey Harrison 		__func__, parent->d_name.name, dentry->d_name.name);
34754ceac45SDavid Howells 
348f05d147fSBryan Schumaker 	err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page);
349f7b422b1SDavid Howells 	dput(parent);
35054ceac45SDavid Howells 	if (err != 0 ||
35154ceac45SDavid Howells 	    fs_locations->nlocations <= 0 ||
352f7b422b1SDavid Howells 	    fs_locations->fs_path.ncomponents <= 0)
353f7b422b1SDavid Howells 		goto out_free;
354f7b422b1SDavid Howells 
355f8ad9c4bSAl Viro 	mnt = nfs_follow_referral(dentry, fs_locations);
356f7b422b1SDavid Howells out_free:
357f7b422b1SDavid Howells 	__free_page(page);
358f7b422b1SDavid Howells 	kfree(fs_locations);
359f7b422b1SDavid Howells out:
3603110ff80SHarvey Harrison 	dprintk("%s: done\n", __func__);
361f7b422b1SDavid Howells 	return mnt;
362f7b422b1SDavid Howells }
363281cad46SBryan Schumaker 
364281cad46SBryan Schumaker struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry,
365281cad46SBryan Schumaker 			       struct nfs_fh *fh, struct nfs_fattr *fattr)
366281cad46SBryan Schumaker {
367281cad46SBryan Schumaker 	struct dentry *parent = dget_parent(dentry);
368281cad46SBryan Schumaker 	struct rpc_clnt *client;
369281cad46SBryan Schumaker 	struct vfsmount *mnt;
370281cad46SBryan Schumaker 
371281cad46SBryan Schumaker 	/* Look it up again to get its attributes and sec flavor */
372281cad46SBryan Schumaker 	client = nfs4_proc_lookup_mountpoint(parent->d_inode, &dentry->d_name, fh, fattr);
373281cad46SBryan Schumaker 	dput(parent);
374281cad46SBryan Schumaker 	if (IS_ERR(client))
375281cad46SBryan Schumaker 		return ERR_CAST(client);
376281cad46SBryan Schumaker 
377281cad46SBryan Schumaker 	if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
378281cad46SBryan Schumaker 		mnt = nfs_do_refmount(client, dentry);
379281cad46SBryan Schumaker 	else
380281cad46SBryan Schumaker 		mnt = nfs_do_submount(dentry, fh, fattr, client->cl_auth->au_flavor);
381281cad46SBryan Schumaker 
382281cad46SBryan Schumaker 	rpc_shutdown_client(client);
383281cad46SBryan Schumaker 	return mnt;
384281cad46SBryan Schumaker }
385