xref: /openbmc/linux/fs/nfs/getroot.c (revision 7de9c6ee)
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