xref: /openbmc/linux/fs/nfs/getroot.c (revision 815409d2)
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 		}
57b09b9417STrond Myklebust 		/* Circumvent igrab(): we know the inode is not being freed */
58b09b9417STrond Myklebust 		atomic_inc(&inode->i_count);
59a10db50aSTrond Myklebust 		/*
60a10db50aSTrond Myklebust 		 * Ensure that this dentry is invisible to d_find_alias().
61a10db50aSTrond Myklebust 		 * Otherwise, it may be spliced into the tree by
62a10db50aSTrond Myklebust 		 * d_materialise_unique if a parent directory from the same
63a10db50aSTrond Myklebust 		 * filesystem gets mounted at a later time.
64a10db50aSTrond Myklebust 		 * This again causes shrink_dcache_for_umount_subtree() to
65a10db50aSTrond Myklebust 		 * Oops, since the test for IS_ROOT() will fail.
66a10db50aSTrond Myklebust 		 */
67a10db50aSTrond Myklebust 		spin_lock(&dcache_lock);
68a10db50aSTrond Myklebust 		list_del_init(&sb->s_root->d_alias);
69a10db50aSTrond Myklebust 		spin_unlock(&dcache_lock);
70b09b9417STrond Myklebust 	}
71b09b9417STrond Myklebust 	return 0;
72b09b9417STrond Myklebust }
73b09b9417STrond Myklebust 
74b09b9417STrond Myklebust /*
7554ceac45SDavid Howells  * get an NFS2/NFS3 root dentry from the root filehandle
7654ceac45SDavid Howells  */
7754ceac45SDavid Howells struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
7854ceac45SDavid Howells {
7954ceac45SDavid Howells 	struct nfs_server *server = NFS_SB(sb);
8054ceac45SDavid Howells 	struct nfs_fsinfo fsinfo;
8154ceac45SDavid Howells 	struct nfs_fattr fattr;
8254ceac45SDavid Howells 	struct dentry *mntroot;
8354ceac45SDavid Howells 	struct inode *inode;
8454ceac45SDavid Howells 	int error;
8554ceac45SDavid Howells 
8654ceac45SDavid Howells 	/* get the actual root for this mount */
8754ceac45SDavid Howells 	fsinfo.fattr = &fattr;
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);
9254ceac45SDavid Howells 		return ERR_PTR(error);
9354ceac45SDavid Howells 	}
9454ceac45SDavid Howells 
9554ceac45SDavid Howells 	inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
9654ceac45SDavid Howells 	if (IS_ERR(inode)) {
9754ceac45SDavid Howells 		dprintk("nfs_get_root: get root inode failed\n");
98e231c2eeSDavid Howells 		return ERR_CAST(inode);
9954ceac45SDavid Howells 	}
10054ceac45SDavid Howells 
101b09b9417STrond Myklebust 	error = nfs_superblock_set_dummy_root(sb, inode);
102b09b9417STrond Myklebust 	if (error != 0)
103b09b9417STrond Myklebust 		return ERR_PTR(error);
104b09b9417STrond Myklebust 
10554ceac45SDavid Howells 	/* root dentries normally start off anonymous and get spliced in later
10654ceac45SDavid Howells 	 * if the dentry tree reaches them; however if the dentry already
10754ceac45SDavid Howells 	 * exists, we'll pick it up at this point and use it as the root
10854ceac45SDavid Howells 	 */
10944003728SChristoph Hellwig 	mntroot = d_obtain_alias(inode);
11044003728SChristoph Hellwig 	if (IS_ERR(mntroot)) {
11154ceac45SDavid Howells 		dprintk("nfs_get_root: get root dentry failed\n");
11244003728SChristoph Hellwig 		return mntroot;
11354ceac45SDavid Howells 	}
11454ceac45SDavid Howells 
115738a3519SDavid Howells 	security_d_instantiate(mntroot, inode);
116738a3519SDavid Howells 
11754ceac45SDavid Howells 	if (!mntroot->d_op)
11854ceac45SDavid Howells 		mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
11954ceac45SDavid Howells 
12054ceac45SDavid Howells 	return mntroot;
12154ceac45SDavid Howells }
12254ceac45SDavid Howells 
12354ceac45SDavid Howells #ifdef CONFIG_NFS_V4
12454ceac45SDavid Howells 
125815409d2STrond Myklebust int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
12654ceac45SDavid Howells {
12754ceac45SDavid Howells 	struct nfs_fsinfo fsinfo;
128815409d2STrond Myklebust 	int ret = -ENOMEM;
12954ceac45SDavid Howells 
130815409d2STrond Myklebust 	dprintk("--> nfs4_get_rootfh()\n");
13154ceac45SDavid Howells 
132815409d2STrond Myklebust 	fsinfo.fattr = nfs_alloc_fattr();
133815409d2STrond Myklebust 	if (fsinfo.fattr == NULL)
134815409d2STrond Myklebust 		goto out;
13554ceac45SDavid Howells 
13654ceac45SDavid Howells 	/* Start by getting the root filehandle from the server */
13754ceac45SDavid Howells 	ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
13854ceac45SDavid Howells 	if (ret < 0) {
139815409d2STrond Myklebust 		dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
140815409d2STrond Myklebust 		goto out;
14154ceac45SDavid Howells 	}
14254ceac45SDavid Howells 
143815409d2STrond Myklebust 	if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_MODE)
144815409d2STrond Myklebust 			|| !S_ISDIR(fsinfo.fattr->mode)) {
145815409d2STrond Myklebust 		printk(KERN_ERR "nfs4_get_rootfh:"
14654ceac45SDavid Howells 		       " getroot encountered non-directory\n");
147815409d2STrond Myklebust 		ret = -ENOTDIR;
148815409d2STrond Myklebust 		goto out;
14954ceac45SDavid Howells 	}
15054ceac45SDavid Howells 
151815409d2STrond Myklebust 	if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
152815409d2STrond Myklebust 		printk(KERN_ERR "nfs4_get_rootfh:"
15354ceac45SDavid Howells 		       " getroot obtained referral\n");
154815409d2STrond Myklebust 		ret = -EREMOTE;
155815409d2STrond Myklebust 		goto out;
15654ceac45SDavid Howells 	}
15754ceac45SDavid Howells 
158815409d2STrond Myklebust 	memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid));
159815409d2STrond Myklebust out:
160815409d2STrond Myklebust 	nfs_free_fattr(fsinfo.fattr);
161815409d2STrond Myklebust 	dprintk("<-- nfs4_get_rootfh() = %d\n", ret);
16254ceac45SDavid Howells 	return ret;
16354ceac45SDavid Howells }
16454ceac45SDavid Howells 
16554ceac45SDavid Howells /*
16654ceac45SDavid Howells  * get an NFS4 root dentry from the root filehandle
16754ceac45SDavid Howells  */
16854ceac45SDavid Howells struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
16954ceac45SDavid Howells {
17054ceac45SDavid Howells 	struct nfs_server *server = NFS_SB(sb);
17154ceac45SDavid Howells 	struct nfs_fattr fattr;
17254ceac45SDavid Howells 	struct dentry *mntroot;
17354ceac45SDavid Howells 	struct inode *inode;
17454ceac45SDavid Howells 	int error;
17554ceac45SDavid Howells 
17654ceac45SDavid Howells 	dprintk("--> nfs4_get_root()\n");
17754ceac45SDavid Howells 
17854ceac45SDavid Howells 	/* get the info about the server and filesystem */
17954ceac45SDavid Howells 	error = nfs4_server_capabilities(server, mntfh);
18054ceac45SDavid Howells 	if (error < 0) {
18154ceac45SDavid Howells 		dprintk("nfs_get_root: getcaps error = %d\n",
18254ceac45SDavid Howells 			-error);
18354ceac45SDavid Howells 		return ERR_PTR(error);
18454ceac45SDavid Howells 	}
18554ceac45SDavid Howells 
18654ceac45SDavid Howells 	/* get the actual root for this mount */
18754ceac45SDavid Howells 	error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
18854ceac45SDavid Howells 	if (error < 0) {
18954ceac45SDavid Howells 		dprintk("nfs_get_root: getattr error = %d\n", -error);
19054ceac45SDavid Howells 		return ERR_PTR(error);
19154ceac45SDavid Howells 	}
19254ceac45SDavid Howells 
19354ceac45SDavid Howells 	inode = nfs_fhget(sb, mntfh, &fattr);
19454ceac45SDavid Howells 	if (IS_ERR(inode)) {
19554ceac45SDavid Howells 		dprintk("nfs_get_root: get root inode failed\n");
196e231c2eeSDavid Howells 		return ERR_CAST(inode);
19754ceac45SDavid Howells 	}
19854ceac45SDavid Howells 
199b09b9417STrond Myklebust 	error = nfs_superblock_set_dummy_root(sb, inode);
200b09b9417STrond Myklebust 	if (error != 0)
201b09b9417STrond Myklebust 		return ERR_PTR(error);
202b09b9417STrond Myklebust 
20354ceac45SDavid Howells 	/* root dentries normally start off anonymous and get spliced in later
20454ceac45SDavid Howells 	 * if the dentry tree reaches them; however if the dentry already
20554ceac45SDavid Howells 	 * exists, we'll pick it up at this point and use it as the root
20654ceac45SDavid Howells 	 */
20744003728SChristoph Hellwig 	mntroot = d_obtain_alias(inode);
20844003728SChristoph Hellwig 	if (IS_ERR(mntroot)) {
20954ceac45SDavid Howells 		dprintk("nfs_get_root: get root dentry failed\n");
21044003728SChristoph Hellwig 		return mntroot;
21154ceac45SDavid Howells 	}
21254ceac45SDavid Howells 
213738a3519SDavid Howells 	security_d_instantiate(mntroot, inode);
214738a3519SDavid Howells 
21554ceac45SDavid Howells 	if (!mntroot->d_op)
21654ceac45SDavid Howells 		mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
21754ceac45SDavid Howells 
21854ceac45SDavid Howells 	dprintk("<-- nfs4_get_root()\n");
21954ceac45SDavid Howells 	return mntroot;
22054ceac45SDavid Howells }
22154ceac45SDavid Howells 
22254ceac45SDavid Howells #endif /* CONFIG_NFS_V4 */
223