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> 17f7b422b1SDavid Howells #include <linux/vfs.h> 18f7b422b1SDavid Howells #include <linux/inet.h> 19f7b422b1SDavid Howells #include "internal.h" 20c228fd3aSTrond Myklebust #include "nfs4_fs.h" 217d7ea882STrond Myklebust #include "dns_resolve.h" 22f7b422b1SDavid Howells 23f7b422b1SDavid Howells #define NFSDBG_FACILITY NFSDBG_VFS 24f7b422b1SDavid Howells 25f7b422b1SDavid Howells /* 26ef95d31eSTrond Myklebust * Convert the NFSv4 pathname components into a standard posix path. 27ef95d31eSTrond Myklebust * 28ef95d31eSTrond Myklebust * Note that the resulting string will be placed at the end of the buffer 29f7b422b1SDavid Howells */ 30509de811SDavid Howells static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname, 31f7b422b1SDavid Howells char *buffer, ssize_t buflen) 32f7b422b1SDavid Howells { 33f7b422b1SDavid Howells char *end = buffer + buflen; 34f7b422b1SDavid Howells int n; 35f7b422b1SDavid Howells 36f7b422b1SDavid Howells *--end = '\0'; 37f7b422b1SDavid Howells buflen--; 38f7b422b1SDavid Howells 39f7b422b1SDavid Howells n = pathname->ncomponents; 40f7b422b1SDavid Howells while (--n >= 0) { 41509de811SDavid Howells const struct nfs4_string *component = &pathname->components[n]; 42f7b422b1SDavid Howells buflen -= component->len + 1; 43f7b422b1SDavid Howells if (buflen < 0) 44f7b422b1SDavid Howells goto Elong; 45f7b422b1SDavid Howells end -= component->len; 46f7b422b1SDavid Howells memcpy(end, component->data, component->len); 47f7b422b1SDavid Howells *--end = '/'; 48f7b422b1SDavid Howells } 49f7b422b1SDavid Howells return end; 50f7b422b1SDavid Howells Elong: 51f7b422b1SDavid Howells return ERR_PTR(-ENAMETOOLONG); 52f7b422b1SDavid Howells } 53f7b422b1SDavid Howells 5454ceac45SDavid Howells /* 551aba1567SWeston Andros Adamson * return the path component of "<server>:<path>" 561aba1567SWeston Andros Adamson * nfspath - the "<server>:<path>" string 571aba1567SWeston Andros Adamson * end - one past the last char that could contain "<server>:" 581aba1567SWeston Andros Adamson * returns NULL on failure 591aba1567SWeston Andros Adamson */ 601aba1567SWeston Andros Adamson static char *nfs_path_component(const char *nfspath, const char *end) 611aba1567SWeston Andros Adamson { 621aba1567SWeston Andros Adamson char *p; 631aba1567SWeston Andros Adamson 641aba1567SWeston Andros Adamson if (*nfspath == '[') { 651aba1567SWeston Andros Adamson /* parse [] escaped IPv6 addrs */ 661aba1567SWeston Andros Adamson p = strchr(nfspath, ']'); 671aba1567SWeston Andros Adamson if (p != NULL && ++p < end && *p == ':') 681aba1567SWeston Andros Adamson return p + 1; 691aba1567SWeston Andros Adamson } else { 701aba1567SWeston Andros Adamson /* otherwise split on first colon */ 711aba1567SWeston Andros Adamson p = strchr(nfspath, ':'); 721aba1567SWeston Andros Adamson if (p != NULL && p < end) 731aba1567SWeston Andros Adamson return p + 1; 741aba1567SWeston Andros Adamson } 751aba1567SWeston Andros Adamson return NULL; 761aba1567SWeston Andros Adamson } 771aba1567SWeston Andros Adamson 781aba1567SWeston Andros Adamson /* 7954ceac45SDavid Howells * Determine the mount path as a string 8054ceac45SDavid Howells */ 81b514f872SAl Viro static char *nfs4_path(struct dentry *dentry, char *buffer, ssize_t buflen) 8254ceac45SDavid Howells { 83b514f872SAl Viro char *limit; 84b514f872SAl Viro char *path = nfs_path(&limit, dentry, buffer, buflen); 85b514f872SAl Viro if (!IS_ERR(path)) { 861aba1567SWeston Andros Adamson char *path_component = nfs_path_component(path, limit); 871aba1567SWeston Andros Adamson if (path_component) 881aba1567SWeston Andros Adamson return path_component; 89b514f872SAl Viro } 90b514f872SAl Viro return path; 9154ceac45SDavid Howells } 9254ceac45SDavid Howells 9354ceac45SDavid Howells /* 9454ceac45SDavid Howells * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we 9554ceac45SDavid Howells * believe to be the server path to this dentry 9654ceac45SDavid Howells */ 97b514f872SAl Viro static int nfs4_validate_fspath(struct dentry *dentry, 9854ceac45SDavid Howells const struct nfs4_fs_locations *locations, 9954ceac45SDavid Howells char *page, char *page2) 10054ceac45SDavid Howells { 10154ceac45SDavid Howells const char *path, *fs_path; 10254ceac45SDavid Howells 103b514f872SAl Viro path = nfs4_path(dentry, page, PAGE_SIZE); 10454ceac45SDavid Howells if (IS_ERR(path)) 10554ceac45SDavid Howells return PTR_ERR(path); 10654ceac45SDavid Howells 10754ceac45SDavid Howells fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); 10854ceac45SDavid Howells if (IS_ERR(fs_path)) 10954ceac45SDavid Howells return PTR_ERR(fs_path); 11054ceac45SDavid Howells 11154ceac45SDavid Howells if (strncmp(path, fs_path, strlen(fs_path)) != 0) { 11254ceac45SDavid Howells dprintk("%s: path %s does not begin with fsroot %s\n", 1133110ff80SHarvey Harrison __func__, path, fs_path); 11454ceac45SDavid Howells return -ENOENT; 11554ceac45SDavid Howells } 11654ceac45SDavid Howells 11754ceac45SDavid Howells return 0; 11854ceac45SDavid Howells } 11954ceac45SDavid Howells 1207d7ea882STrond Myklebust static size_t nfs_parse_server_name(char *string, size_t len, 1211b340d01SStanislav Kinsbursky struct sockaddr *sa, size_t salen, struct nfs_server *server) 1227d7ea882STrond Myklebust { 1232446ab60STrond Myklebust struct net *net = rpc_net_ns(server->client); 1247d7ea882STrond Myklebust ssize_t ret; 1257d7ea882STrond Myklebust 12633faaa38SStanislav Kinsbursky ret = rpc_pton(net, string, len, sa, salen); 1277d7ea882STrond Myklebust if (ret == 0) { 12833faaa38SStanislav Kinsbursky ret = nfs_dns_resolve_name(net, string, len, sa, salen); 1297d7ea882STrond Myklebust if (ret < 0) 1307d7ea882STrond Myklebust ret = 0; 1317d7ea882STrond Myklebust } 1327d7ea882STrond Myklebust return ret; 1337d7ea882STrond Myklebust } 1347d7ea882STrond Myklebust 1352671bfc3SBryan Schumaker rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) 1362671bfc3SBryan Schumaker { 1372671bfc3SBryan Schumaker struct gss_api_mech *mech; 1382671bfc3SBryan Schumaker struct xdr_netobj oid; 1392671bfc3SBryan Schumaker int i; 1402671bfc3SBryan Schumaker rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX; 1412671bfc3SBryan Schumaker 1422671bfc3SBryan Schumaker for (i = 0; i < flavors->num_flavors; i++) { 1432671bfc3SBryan Schumaker struct nfs4_secinfo_flavor *flavor; 1442671bfc3SBryan Schumaker flavor = &flavors->flavors[i]; 1452671bfc3SBryan Schumaker 1462671bfc3SBryan Schumaker if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) { 1472671bfc3SBryan Schumaker pseudoflavor = flavor->flavor; 1482671bfc3SBryan Schumaker break; 1492671bfc3SBryan Schumaker } else if (flavor->flavor == RPC_AUTH_GSS) { 1502671bfc3SBryan Schumaker oid.len = flavor->gss.sec_oid4.len; 1512671bfc3SBryan Schumaker oid.data = flavor->gss.sec_oid4.data; 1522671bfc3SBryan Schumaker mech = gss_mech_get_by_OID(&oid); 1532671bfc3SBryan Schumaker if (!mech) 1542671bfc3SBryan Schumaker continue; 1552671bfc3SBryan Schumaker pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service); 1562671bfc3SBryan Schumaker gss_mech_put(mech); 1572671bfc3SBryan Schumaker break; 1582671bfc3SBryan Schumaker } 1592671bfc3SBryan Schumaker } 1602671bfc3SBryan Schumaker 1612671bfc3SBryan Schumaker return pseudoflavor; 1622671bfc3SBryan Schumaker } 1632671bfc3SBryan Schumaker 16472de53ecSBryan Schumaker static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) 16572de53ecSBryan Schumaker { 16672de53ecSBryan Schumaker struct page *page; 16772de53ecSBryan Schumaker struct nfs4_secinfo_flavors *flavors; 16872de53ecSBryan Schumaker rpc_authflavor_t flavor; 16972de53ecSBryan Schumaker int err; 17072de53ecSBryan Schumaker 17172de53ecSBryan Schumaker page = alloc_page(GFP_KERNEL); 17272de53ecSBryan Schumaker if (!page) 17372de53ecSBryan Schumaker return -ENOMEM; 17472de53ecSBryan Schumaker flavors = page_address(page); 17572de53ecSBryan Schumaker 17672de53ecSBryan Schumaker err = nfs4_proc_secinfo(inode, name, flavors); 17772de53ecSBryan Schumaker if (err < 0) { 17872de53ecSBryan Schumaker flavor = err; 17972de53ecSBryan Schumaker goto out; 18072de53ecSBryan Schumaker } 18172de53ecSBryan Schumaker 18272de53ecSBryan Schumaker flavor = nfs_find_best_sec(flavors); 18372de53ecSBryan Schumaker 18472de53ecSBryan Schumaker out: 18572de53ecSBryan Schumaker put_page(page); 18672de53ecSBryan Schumaker return flavor; 18772de53ecSBryan Schumaker } 18872de53ecSBryan Schumaker 18972de53ecSBryan Schumaker /* 19072de53ecSBryan Schumaker * Please call rpc_shutdown_client() when you are done with this client. 19172de53ecSBryan Schumaker */ 19272de53ecSBryan Schumaker struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *clnt, struct inode *inode, 19372de53ecSBryan Schumaker struct qstr *name) 19472de53ecSBryan Schumaker { 19572de53ecSBryan Schumaker struct rpc_clnt *clone; 19672de53ecSBryan Schumaker struct rpc_auth *auth; 19772de53ecSBryan Schumaker rpc_authflavor_t flavor; 19872de53ecSBryan Schumaker 19972de53ecSBryan Schumaker flavor = nfs4_negotiate_security(inode, name); 20072de53ecSBryan Schumaker if (flavor < 0) 20172de53ecSBryan Schumaker return ERR_PTR(flavor); 20272de53ecSBryan Schumaker 20372de53ecSBryan Schumaker clone = rpc_clone_client(clnt); 20472de53ecSBryan Schumaker if (IS_ERR(clone)) 20572de53ecSBryan Schumaker return clone; 20672de53ecSBryan Schumaker 20772de53ecSBryan Schumaker auth = rpcauth_create(flavor, clone); 20872de53ecSBryan Schumaker if (!auth) { 20972de53ecSBryan Schumaker rpc_shutdown_client(clone); 21072de53ecSBryan Schumaker clone = ERR_PTR(-EIO); 21172de53ecSBryan Schumaker } 21272de53ecSBryan Schumaker 21372de53ecSBryan Schumaker return clone; 21472de53ecSBryan Schumaker } 21572de53ecSBryan Schumaker 2164ada29d5SJ. Bruce Fields static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, 2174ada29d5SJ. Bruce Fields char *page, char *page2, 2184ada29d5SJ. Bruce Fields const struct nfs4_fs_location *location) 2194ada29d5SJ. Bruce Fields { 220364d015eSTrond Myklebust const size_t addr_bufsize = sizeof(struct sockaddr_storage); 2214ada29d5SJ. Bruce Fields struct vfsmount *mnt = ERR_PTR(-ENOENT); 2224ada29d5SJ. Bruce Fields char *mnt_path; 223ef95d31eSTrond Myklebust unsigned int maxbuflen; 224460cdbc8SJ. Bruce Fields unsigned int s; 2254ada29d5SJ. Bruce Fields 2264ada29d5SJ. Bruce Fields mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); 2274ada29d5SJ. Bruce Fields if (IS_ERR(mnt_path)) 228517be09dSTrond Myklebust return ERR_CAST(mnt_path); 2294ada29d5SJ. Bruce Fields mountdata->mnt_path = mnt_path; 230ef95d31eSTrond Myklebust maxbuflen = mnt_path - 1 - page2; 2314ada29d5SJ. Bruce Fields 232364d015eSTrond Myklebust mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL); 233364d015eSTrond Myklebust if (mountdata->addr == NULL) 234364d015eSTrond Myklebust return ERR_PTR(-ENOMEM); 235364d015eSTrond Myklebust 236460cdbc8SJ. Bruce Fields for (s = 0; s < location->nservers; s++) { 237ea31a443SJ. Bruce Fields const struct nfs4_string *buf = &location->servers[s]; 2384ada29d5SJ. Bruce Fields 239ef95d31eSTrond Myklebust if (buf->len <= 0 || buf->len >= maxbuflen) 2404ada29d5SJ. Bruce Fields continue; 2414ada29d5SJ. Bruce Fields 242ea31a443SJ. Bruce Fields if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) 243ea31a443SJ. Bruce Fields continue; 244517be09dSTrond Myklebust 245517be09dSTrond Myklebust mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, 2461b340d01SStanislav Kinsbursky mountdata->addr, addr_bufsize, 2471b340d01SStanislav Kinsbursky NFS_SB(mountdata->sb)); 24853a0b9c4SChuck Lever if (mountdata->addrlen == 0) 249ea31a443SJ. Bruce Fields continue; 250517be09dSTrond Myklebust 251ec6ee612SChuck Lever rpc_set_port(mountdata->addr, NFS_PORT); 252ea31a443SJ. Bruce Fields 253ef95d31eSTrond Myklebust memcpy(page2, buf->data, buf->len); 254ef95d31eSTrond Myklebust page2[buf->len] = '\0'; 255ea31a443SJ. Bruce Fields mountdata->hostname = page2; 2564ada29d5SJ. Bruce Fields 2574ada29d5SJ. Bruce Fields snprintf(page, PAGE_SIZE, "%s:%s", 2584ada29d5SJ. Bruce Fields mountdata->hostname, 2594ada29d5SJ. Bruce Fields mountdata->mnt_path); 2604ada29d5SJ. Bruce Fields 2614ada29d5SJ. Bruce Fields mnt = vfs_kern_mount(&nfs4_referral_fs_type, 0, page, mountdata); 2624ada29d5SJ. Bruce Fields if (!IS_ERR(mnt)) 2634ada29d5SJ. Bruce Fields break; 2644ada29d5SJ. Bruce Fields } 265364d015eSTrond Myklebust kfree(mountdata->addr); 2664ada29d5SJ. Bruce Fields return mnt; 2674ada29d5SJ. Bruce Fields } 2684ada29d5SJ. Bruce Fields 269f7b422b1SDavid Howells /** 270f7b422b1SDavid Howells * nfs_follow_referral - set up mountpoint when hitting a referral on moved error 271f7b422b1SDavid Howells * @dentry - parent directory 2723f43c666SChuck Lever * @locations - array of NFSv4 server location information 273f7b422b1SDavid Howells * 274f7b422b1SDavid Howells */ 275f8ad9c4bSAl Viro static struct vfsmount *nfs_follow_referral(struct dentry *dentry, 276509de811SDavid Howells const struct nfs4_fs_locations *locations) 277f7b422b1SDavid Howells { 278f7b422b1SDavid Howells struct vfsmount *mnt = ERR_PTR(-ENOENT); 279f7b422b1SDavid Howells struct nfs_clone_mount mountdata = { 280f8ad9c4bSAl Viro .sb = dentry->d_sb, 281f7b422b1SDavid Howells .dentry = dentry, 282f8ad9c4bSAl Viro .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor, 283f7b422b1SDavid Howells }; 28454ceac45SDavid Howells char *page = NULL, *page2 = NULL; 2853f43c666SChuck Lever int loc, error; 286f7b422b1SDavid Howells 287f7b422b1SDavid Howells if (locations == NULL || locations->nlocations <= 0) 288f7b422b1SDavid Howells goto out; 289f7b422b1SDavid Howells 2903110ff80SHarvey Harrison dprintk("%s: referral at %s/%s\n", __func__, 291f7b422b1SDavid Howells dentry->d_parent->d_name.name, dentry->d_name.name); 292f7b422b1SDavid Howells 293f7b422b1SDavid Howells page = (char *) __get_free_page(GFP_USER); 29454ceac45SDavid Howells if (!page) 295f7b422b1SDavid Howells goto out; 29654ceac45SDavid Howells 297f7b422b1SDavid Howells page2 = (char *) __get_free_page(GFP_USER); 29854ceac45SDavid Howells if (!page2) 299f7b422b1SDavid Howells goto out; 300f7b422b1SDavid Howells 30154ceac45SDavid Howells /* Ensure fs path is a prefix of current dentry path */ 302b514f872SAl Viro error = nfs4_validate_fspath(dentry, locations, page, page2); 30354ceac45SDavid Howells if (error < 0) { 30454ceac45SDavid Howells mnt = ERR_PTR(error); 30554ceac45SDavid Howells goto out; 306f7b422b1SDavid Howells } 307f7b422b1SDavid Howells 308460cdbc8SJ. Bruce Fields for (loc = 0; loc < locations->nlocations; loc++) { 309509de811SDavid Howells const struct nfs4_fs_location *location = &locations->locations[loc]; 310f7b422b1SDavid Howells 311f7b422b1SDavid Howells if (location == NULL || location->nservers <= 0 || 312460cdbc8SJ. Bruce Fields location->rootpath.ncomponents == 0) 313f7b422b1SDavid Howells continue; 314f7b422b1SDavid Howells 3154ada29d5SJ. Bruce Fields mnt = try_location(&mountdata, page, page2, location); 3164ada29d5SJ. Bruce Fields if (!IS_ERR(mnt)) 317f7b422b1SDavid Howells break; 318f7b422b1SDavid Howells } 319f7b422b1SDavid Howells 32054ceac45SDavid Howells out: 321f7b422b1SDavid Howells free_page((unsigned long) page); 322f7b422b1SDavid Howells free_page((unsigned long) page2); 3233110ff80SHarvey Harrison dprintk("%s: done\n", __func__); 324f7b422b1SDavid Howells return mnt; 325f7b422b1SDavid Howells } 326f7b422b1SDavid Howells 327f7b422b1SDavid Howells /* 328f7b422b1SDavid Howells * nfs_do_refmount - handle crossing a referral on server 329f7b422b1SDavid Howells * @dentry - dentry of referral 330f7b422b1SDavid Howells * 331f7b422b1SDavid Howells */ 332f05d147fSBryan Schumaker struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) 333f7b422b1SDavid Howells { 33454ceac45SDavid Howells struct vfsmount *mnt = ERR_PTR(-ENOMEM); 335f7b422b1SDavid Howells struct dentry *parent; 336f7b422b1SDavid Howells struct nfs4_fs_locations *fs_locations = NULL; 337f7b422b1SDavid Howells struct page *page; 338f7b422b1SDavid Howells int err; 339f7b422b1SDavid Howells 340f7b422b1SDavid Howells /* BUG_ON(IS_ROOT(dentry)); */ 3413110ff80SHarvey Harrison dprintk("%s: enter\n", __func__); 342f7b422b1SDavid Howells 343f7b422b1SDavid Howells page = alloc_page(GFP_KERNEL); 344f7b422b1SDavid Howells if (page == NULL) 345f7b422b1SDavid Howells goto out; 346f7b422b1SDavid Howells 347f7b422b1SDavid Howells fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); 348f7b422b1SDavid Howells if (fs_locations == NULL) 349f7b422b1SDavid Howells goto out_free; 350f7b422b1SDavid Howells 351f7b422b1SDavid Howells /* Get locations */ 35254ceac45SDavid Howells mnt = ERR_PTR(-ENOENT); 35354ceac45SDavid Howells 354f7b422b1SDavid Howells parent = dget_parent(dentry); 35554ceac45SDavid Howells dprintk("%s: getting locations for %s/%s\n", 3563110ff80SHarvey Harrison __func__, parent->d_name.name, dentry->d_name.name); 35754ceac45SDavid Howells 358f05d147fSBryan Schumaker err = nfs4_proc_fs_locations(client, parent->d_inode, &dentry->d_name, fs_locations, page); 359f7b422b1SDavid Howells dput(parent); 36054ceac45SDavid Howells if (err != 0 || 36154ceac45SDavid Howells fs_locations->nlocations <= 0 || 362f7b422b1SDavid Howells fs_locations->fs_path.ncomponents <= 0) 363f7b422b1SDavid Howells goto out_free; 364f7b422b1SDavid Howells 365f8ad9c4bSAl Viro mnt = nfs_follow_referral(dentry, fs_locations); 366f7b422b1SDavid Howells out_free: 367f7b422b1SDavid Howells __free_page(page); 368f7b422b1SDavid Howells kfree(fs_locations); 369f7b422b1SDavid Howells out: 3703110ff80SHarvey Harrison dprintk("%s: done\n", __func__); 371f7b422b1SDavid Howells return mnt; 372f7b422b1SDavid Howells } 373