154ceac45SDavid Howells /* getroot.c: get the root dentry for an NFS mount 254ceac45SDavid Howells * 354ceac45SDavid Howells * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. 454ceac45SDavid Howells * Written by David Howells (dhowells@redhat.com) 554ceac45SDavid Howells * 654ceac45SDavid Howells * This program is free software; you can redistribute it and/or 754ceac45SDavid Howells * modify it under the terms of the GNU General Public License 854ceac45SDavid Howells * as published by the Free Software Foundation; either version 954ceac45SDavid Howells * 2 of the License, or (at your option) any later version. 1054ceac45SDavid Howells */ 1154ceac45SDavid Howells 1254ceac45SDavid Howells #include <linux/module.h> 1354ceac45SDavid Howells #include <linux/init.h> 1454ceac45SDavid Howells 1554ceac45SDavid Howells #include <linux/time.h> 1654ceac45SDavid Howells #include <linux/kernel.h> 1754ceac45SDavid Howells #include <linux/mm.h> 1854ceac45SDavid Howells #include <linux/string.h> 1954ceac45SDavid Howells #include <linux/stat.h> 2054ceac45SDavid Howells #include <linux/errno.h> 2154ceac45SDavid Howells #include <linux/unistd.h> 2254ceac45SDavid Howells #include <linux/sunrpc/clnt.h> 2354ceac45SDavid Howells #include <linux/sunrpc/stats.h> 2454ceac45SDavid Howells #include <linux/nfs_fs.h> 2554ceac45SDavid Howells #include <linux/nfs_mount.h> 2654ceac45SDavid Howells #include <linux/nfs4_mount.h> 2754ceac45SDavid Howells #include <linux/lockd/bind.h> 2854ceac45SDavid Howells #include <linux/seq_file.h> 2954ceac45SDavid Howells #include <linux/mount.h> 3054ceac45SDavid Howells #include <linux/nfs_idmap.h> 3154ceac45SDavid Howells #include <linux/vfs.h> 3254ceac45SDavid Howells #include <linux/namei.h> 33738a3519SDavid Howells #include <linux/security.h> 3454ceac45SDavid Howells 3554ceac45SDavid Howells #include <asm/system.h> 3654ceac45SDavid Howells #include <asm/uaccess.h> 3754ceac45SDavid Howells 3854ceac45SDavid Howells #include "nfs4_fs.h" 3954ceac45SDavid Howells #include "delegation.h" 4054ceac45SDavid Howells #include "internal.h" 4154ceac45SDavid Howells 4254ceac45SDavid Howells #define NFSDBG_FACILITY NFSDBG_CLIENT 4354ceac45SDavid Howells 4454ceac45SDavid Howells /* 45b09b9417STrond Myklebust * Set the superblock root dentry. 46b09b9417STrond Myklebust * Note that this function frees the inode in case of error. 47b09b9417STrond Myklebust */ 48b09b9417STrond Myklebust static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode) 49b09b9417STrond Myklebust { 50b09b9417STrond Myklebust /* The mntroot acts as the dummy root dentry for this superblock */ 51b09b9417STrond Myklebust if (sb->s_root == NULL) { 52b09b9417STrond Myklebust sb->s_root = d_alloc_root(inode); 53b09b9417STrond Myklebust if (sb->s_root == NULL) { 54b09b9417STrond Myklebust iput(inode); 55b09b9417STrond Myklebust return -ENOMEM; 56b09b9417STrond Myklebust } 577de9c6eeSAl Viro ihold(inode); 58a10db50aSTrond Myklebust /* 59a10db50aSTrond Myklebust * Ensure that this dentry is invisible to d_find_alias(). 60a10db50aSTrond Myklebust * Otherwise, it may be spliced into the tree by 61a10db50aSTrond Myklebust * d_materialise_unique if a parent directory from the same 62a10db50aSTrond Myklebust * filesystem gets mounted at a later time. 63a10db50aSTrond Myklebust * This again causes shrink_dcache_for_umount_subtree() to 64a10db50aSTrond Myklebust * Oops, since the test for IS_ROOT() will fail. 65a10db50aSTrond Myklebust */ 66a10db50aSTrond Myklebust spin_lock(&dcache_lock); 67a10db50aSTrond Myklebust list_del_init(&sb->s_root->d_alias); 68a10db50aSTrond Myklebust spin_unlock(&dcache_lock); 69b09b9417STrond Myklebust } 70b09b9417STrond Myklebust return 0; 71b09b9417STrond Myklebust } 72b09b9417STrond Myklebust 73b09b9417STrond Myklebust /* 7454ceac45SDavid Howells * get an NFS2/NFS3 root dentry from the root filehandle 7554ceac45SDavid Howells */ 7654ceac45SDavid Howells struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) 7754ceac45SDavid Howells { 7854ceac45SDavid Howells struct nfs_server *server = NFS_SB(sb); 7954ceac45SDavid Howells struct nfs_fsinfo fsinfo; 808bac9db9STrond Myklebust struct dentry *ret; 8154ceac45SDavid Howells struct inode *inode; 8254ceac45SDavid Howells int error; 8354ceac45SDavid Howells 8454ceac45SDavid Howells /* get the actual root for this mount */ 858bac9db9STrond Myklebust fsinfo.fattr = nfs_alloc_fattr(); 868bac9db9STrond Myklebust if (fsinfo.fattr == NULL) 878bac9db9STrond Myklebust return ERR_PTR(-ENOMEM); 8854ceac45SDavid Howells 8954ceac45SDavid Howells error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 9054ceac45SDavid Howells if (error < 0) { 9154ceac45SDavid Howells dprintk("nfs_get_root: getattr error = %d\n", -error); 928bac9db9STrond Myklebust ret = ERR_PTR(error); 938bac9db9STrond Myklebust goto out; 9454ceac45SDavid Howells } 9554ceac45SDavid Howells 9654ceac45SDavid Howells inode = nfs_fhget(sb, mntfh, fsinfo.fattr); 9754ceac45SDavid Howells if (IS_ERR(inode)) { 9854ceac45SDavid Howells dprintk("nfs_get_root: get root inode failed\n"); 998bac9db9STrond Myklebust ret = ERR_CAST(inode); 1008bac9db9STrond Myklebust goto out; 10154ceac45SDavid Howells } 10254ceac45SDavid Howells 103b09b9417STrond Myklebust error = nfs_superblock_set_dummy_root(sb, inode); 1048bac9db9STrond Myklebust if (error != 0) { 1058bac9db9STrond Myklebust ret = ERR_PTR(error); 1068bac9db9STrond Myklebust goto out; 1078bac9db9STrond Myklebust } 108b09b9417STrond Myklebust 10954ceac45SDavid Howells /* root dentries normally start off anonymous and get spliced in later 11054ceac45SDavid Howells * if the dentry tree reaches them; however if the dentry already 11154ceac45SDavid Howells * exists, we'll pick it up at this point and use it as the root 11254ceac45SDavid Howells */ 1138bac9db9STrond Myklebust ret = d_obtain_alias(inode); 1148bac9db9STrond Myklebust if (IS_ERR(ret)) { 11554ceac45SDavid Howells dprintk("nfs_get_root: get root dentry failed\n"); 1168bac9db9STrond Myklebust goto out; 11754ceac45SDavid Howells } 11854ceac45SDavid Howells 1198bac9db9STrond Myklebust security_d_instantiate(ret, inode); 120738a3519SDavid Howells 1218bac9db9STrond Myklebust if (ret->d_op == NULL) 1228bac9db9STrond Myklebust ret->d_op = server->nfs_client->rpc_ops->dentry_ops; 1238bac9db9STrond Myklebust out: 1248bac9db9STrond Myklebust nfs_free_fattr(fsinfo.fattr); 1258bac9db9STrond Myklebust return ret; 12654ceac45SDavid Howells } 12754ceac45SDavid Howells 12854ceac45SDavid Howells #ifdef CONFIG_NFS_V4 12954ceac45SDavid Howells 130815409d2STrond Myklebust int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh) 13154ceac45SDavid Howells { 13254ceac45SDavid Howells struct nfs_fsinfo fsinfo; 133815409d2STrond Myklebust int ret = -ENOMEM; 13454ceac45SDavid Howells 135815409d2STrond Myklebust dprintk("--> nfs4_get_rootfh()\n"); 13654ceac45SDavid Howells 137815409d2STrond Myklebust fsinfo.fattr = nfs_alloc_fattr(); 138815409d2STrond Myklebust if (fsinfo.fattr == NULL) 139815409d2STrond Myklebust goto out; 14054ceac45SDavid Howells 14154ceac45SDavid Howells /* Start by getting the root filehandle from the server */ 14254ceac45SDavid Howells ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); 14354ceac45SDavid Howells if (ret < 0) { 144815409d2STrond Myklebust dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret); 145815409d2STrond Myklebust goto out; 14654ceac45SDavid Howells } 14754ceac45SDavid Howells 148f799bdb3SAndy Adamson if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE) 149815409d2STrond Myklebust || !S_ISDIR(fsinfo.fattr->mode)) { 150815409d2STrond Myklebust printk(KERN_ERR "nfs4_get_rootfh:" 15154ceac45SDavid Howells " getroot encountered non-directory\n"); 152815409d2STrond Myklebust ret = -ENOTDIR; 153815409d2STrond Myklebust goto out; 15454ceac45SDavid Howells } 15554ceac45SDavid Howells 156815409d2STrond Myklebust if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { 157815409d2STrond Myklebust printk(KERN_ERR "nfs4_get_rootfh:" 15854ceac45SDavid Howells " getroot obtained referral\n"); 159815409d2STrond Myklebust ret = -EREMOTE; 160815409d2STrond Myklebust goto out; 16154ceac45SDavid Howells } 16254ceac45SDavid Howells 163815409d2STrond Myklebust memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid)); 164815409d2STrond Myklebust out: 165815409d2STrond Myklebust nfs_free_fattr(fsinfo.fattr); 166815409d2STrond Myklebust dprintk("<-- nfs4_get_rootfh() = %d\n", ret); 16754ceac45SDavid Howells return ret; 16854ceac45SDavid Howells } 16954ceac45SDavid Howells 17054ceac45SDavid Howells /* 17154ceac45SDavid Howells * get an NFS4 root dentry from the root filehandle 17254ceac45SDavid Howells */ 17354ceac45SDavid Howells struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) 17454ceac45SDavid Howells { 17554ceac45SDavid Howells struct nfs_server *server = NFS_SB(sb); 1768bac9db9STrond Myklebust struct nfs_fattr *fattr = NULL; 1778bac9db9STrond Myklebust struct dentry *ret; 17854ceac45SDavid Howells struct inode *inode; 17954ceac45SDavid Howells int error; 18054ceac45SDavid Howells 18154ceac45SDavid Howells dprintk("--> nfs4_get_root()\n"); 18254ceac45SDavid Howells 18354ceac45SDavid Howells /* get the info about the server and filesystem */ 18454ceac45SDavid Howells error = nfs4_server_capabilities(server, mntfh); 18554ceac45SDavid Howells if (error < 0) { 18654ceac45SDavid Howells dprintk("nfs_get_root: getcaps error = %d\n", 18754ceac45SDavid Howells -error); 18854ceac45SDavid Howells return ERR_PTR(error); 18954ceac45SDavid Howells } 19054ceac45SDavid Howells 1918bac9db9STrond Myklebust fattr = nfs_alloc_fattr(); 1928bac9db9STrond Myklebust if (fattr == NULL) 1938bac9db9STrond Myklebust return ERR_PTR(-ENOMEM);; 1948bac9db9STrond Myklebust 19554ceac45SDavid Howells /* get the actual root for this mount */ 1968bac9db9STrond Myklebust error = server->nfs_client->rpc_ops->getattr(server, mntfh, fattr); 19754ceac45SDavid Howells if (error < 0) { 19854ceac45SDavid Howells dprintk("nfs_get_root: getattr error = %d\n", -error); 1998bac9db9STrond Myklebust ret = ERR_PTR(error); 2008bac9db9STrond Myklebust goto out; 20154ceac45SDavid Howells } 20254ceac45SDavid Howells 2038bac9db9STrond Myklebust inode = nfs_fhget(sb, mntfh, fattr); 20454ceac45SDavid Howells if (IS_ERR(inode)) { 20554ceac45SDavid Howells dprintk("nfs_get_root: get root inode failed\n"); 2068bac9db9STrond Myklebust ret = ERR_CAST(inode); 2078bac9db9STrond Myklebust goto out; 20854ceac45SDavid Howells } 20954ceac45SDavid Howells 210b09b9417STrond Myklebust error = nfs_superblock_set_dummy_root(sb, inode); 2118bac9db9STrond Myklebust if (error != 0) { 2128bac9db9STrond Myklebust ret = ERR_PTR(error); 2138bac9db9STrond Myklebust goto out; 2148bac9db9STrond Myklebust } 215b09b9417STrond Myklebust 21654ceac45SDavid Howells /* root dentries normally start off anonymous and get spliced in later 21754ceac45SDavid Howells * if the dentry tree reaches them; however if the dentry already 21854ceac45SDavid Howells * exists, we'll pick it up at this point and use it as the root 21954ceac45SDavid Howells */ 2208bac9db9STrond Myklebust ret = d_obtain_alias(inode); 2218bac9db9STrond Myklebust if (IS_ERR(ret)) { 22254ceac45SDavid Howells dprintk("nfs_get_root: get root dentry failed\n"); 2238bac9db9STrond Myklebust goto out; 22454ceac45SDavid Howells } 22554ceac45SDavid Howells 2268bac9db9STrond Myklebust security_d_instantiate(ret, inode); 227738a3519SDavid Howells 2288bac9db9STrond Myklebust if (ret->d_op == NULL) 2298bac9db9STrond Myklebust ret->d_op = server->nfs_client->rpc_ops->dentry_ops; 23054ceac45SDavid Howells 2318bac9db9STrond Myklebust out: 2328bac9db9STrond Myklebust nfs_free_fattr(fattr); 23354ceac45SDavid Howells dprintk("<-- nfs4_get_root()\n"); 2348bac9db9STrond Myklebust return ret; 23554ceac45SDavid Howells } 23654ceac45SDavid Howells 23754ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */ 238