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