1/* getroot.c: get the root dentry for an NFS mount
2 *
3 * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/module.h>
13#include <linux/init.h>
14
15#include <linux/time.h>
16#include <linux/kernel.h>
17#include <linux/mm.h>
18#include <linux/string.h>
19#include <linux/stat.h>
20#include <linux/errno.h>
21#include <linux/unistd.h>
22#include <linux/sunrpc/clnt.h>
23#include <linux/sunrpc/stats.h>
24#include <linux/nfs_fs.h>
25#include <linux/nfs_mount.h>
26#include <linux/nfs4_mount.h>
27#include <linux/lockd/bind.h>
28#include <linux/seq_file.h>
29#include <linux/mount.h>
30#include <linux/nfs_idmap.h>
31#include <linux/vfs.h>
32#include <linux/namei.h>
33#include <linux/mnt_namespace.h>
34#include <linux/security.h>
35
36#include <asm/system.h>
37#include <asm/uaccess.h>
38
39#include "nfs4_fs.h"
40#include "delegation.h"
41#include "internal.h"
42
43#define NFSDBG_FACILITY		NFSDBG_CLIENT
44
45/*
46 * get an NFS2/NFS3 root dentry from the root filehandle
47 */
48struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
49{
50	struct nfs_server *server = NFS_SB(sb);
51	struct nfs_fsinfo fsinfo;
52	struct nfs_fattr fattr;
53	struct dentry *mntroot;
54	struct inode *inode;
55	int error;
56
57	/* create a dummy root dentry with dummy inode for this superblock */
58	if (!sb->s_root) {
59		struct nfs_fh dummyfh;
60		struct dentry *root;
61		struct inode *iroot;
62
63		memset(&dummyfh, 0, sizeof(dummyfh));
64		memset(&fattr, 0, sizeof(fattr));
65		nfs_fattr_init(&fattr);
66		fattr.valid = NFS_ATTR_FATTR;
67		fattr.type = NFDIR;
68		fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
69		fattr.nlink = 2;
70
71		iroot = nfs_fhget(sb, &dummyfh, &fattr);
72		if (IS_ERR(iroot))
73			return ERR_PTR(PTR_ERR(iroot));
74
75		root = d_alloc_root(iroot);
76		if (!root) {
77			iput(iroot);
78			return ERR_PTR(-ENOMEM);
79		}
80
81		sb->s_root = root;
82	}
83
84	/* get the actual root for this mount */
85	fsinfo.fattr = &fattr;
86
87	error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
88	if (error < 0) {
89		dprintk("nfs_get_root: getattr error = %d\n", -error);
90		return ERR_PTR(error);
91	}
92
93	inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
94	if (IS_ERR(inode)) {
95		dprintk("nfs_get_root: get root inode failed\n");
96		return ERR_PTR(PTR_ERR(inode));
97	}
98
99	/* root dentries normally start off anonymous and get spliced in later
100	 * if the dentry tree reaches them; however if the dentry already
101	 * exists, we'll pick it up at this point and use it as the root
102	 */
103	mntroot = d_alloc_anon(inode);
104	if (!mntroot) {
105		iput(inode);
106		dprintk("nfs_get_root: get root dentry failed\n");
107		return ERR_PTR(-ENOMEM);
108	}
109
110	security_d_instantiate(mntroot, inode);
111
112	if (!mntroot->d_op)
113		mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
114
115	return mntroot;
116}
117
118#ifdef CONFIG_NFS_V4
119
120/*
121 * Do a simple pathwalk from the root FH of the server to the nominated target
122 * of the mountpoint
123 * - give error on symlinks
124 * - give error on ".." occurring in the path
125 * - follow traversals
126 */
127int nfs4_path_walk(struct nfs_server *server,
128		   struct nfs_fh *mntfh,
129		   const char *path)
130{
131	struct nfs_fsinfo fsinfo;
132	struct nfs_fattr fattr;
133	struct nfs_fh lastfh;
134	struct qstr name;
135	int ret;
136
137	dprintk("--> nfs4_path_walk(,,%s)\n", path);
138
139	fsinfo.fattr = &fattr;
140	nfs_fattr_init(&fattr);
141
142	/* Eat leading slashes */
143	while (*path == '/')
144		path++;
145
146	/* Start by getting the root filehandle from the server */
147	ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
148	if (ret < 0) {
149		dprintk("nfs4_get_root: getroot error = %d\n", -ret);
150		return ret;
151	}
152
153	if (fattr.type != NFDIR) {
154		printk(KERN_ERR "nfs4_get_root:"
155		       " getroot encountered non-directory\n");
156		return -ENOTDIR;
157	}
158
159	if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
160		printk(KERN_ERR "nfs4_get_root:"
161		       " getroot obtained referral\n");
162		return -EREMOTE;
163	}
164
165next_component:
166	dprintk("Next: %s\n", path);
167
168	/* extract the next bit of the path */
169	if (!*path)
170		goto path_walk_complete;
171
172	name.name = path;
173	while (*path && *path != '/')
174		path++;
175	name.len = path - (const char *) name.name;
176
177eat_dot_dir:
178	while (*path == '/')
179		path++;
180
181	if (path[0] == '.' && (path[1] == '/' || !path[1])) {
182		path += 2;
183		goto eat_dot_dir;
184	}
185
186	if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2])
187	    ) {
188		printk(KERN_ERR "nfs4_get_root:"
189		       " Mount path contains reference to \"..\"\n");
190		return -EINVAL;
191	}
192
193	/* lookup the next FH in the sequence */
194	memcpy(&lastfh, mntfh, sizeof(lastfh));
195
196	dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path);
197
198	ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name,
199						    mntfh, &fattr);
200	if (ret < 0) {
201		dprintk("nfs4_get_root: getroot error = %d\n", -ret);
202		return ret;
203	}
204
205	if (fattr.type != NFDIR) {
206		printk(KERN_ERR "nfs4_get_root:"
207		       " lookupfh encountered non-directory\n");
208		return -ENOTDIR;
209	}
210
211	if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
212		printk(KERN_ERR "nfs4_get_root:"
213		       " lookupfh obtained referral\n");
214		return -EREMOTE;
215	}
216
217	goto next_component;
218
219path_walk_complete:
220	memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
221	dprintk("<-- nfs4_path_walk() = 0\n");
222	return 0;
223}
224
225/*
226 * get an NFS4 root dentry from the root filehandle
227 */
228struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
229{
230	struct nfs_server *server = NFS_SB(sb);
231	struct nfs_fattr fattr;
232	struct dentry *mntroot;
233	struct inode *inode;
234	int error;
235
236	dprintk("--> nfs4_get_root()\n");
237
238	/* create a dummy root dentry with dummy inode for this superblock */
239	if (!sb->s_root) {
240		struct nfs_fh dummyfh;
241		struct dentry *root;
242		struct inode *iroot;
243
244		memset(&dummyfh, 0, sizeof(dummyfh));
245		memset(&fattr, 0, sizeof(fattr));
246		nfs_fattr_init(&fattr);
247		fattr.valid = NFS_ATTR_FATTR;
248		fattr.type = NFDIR;
249		fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR;
250		fattr.nlink = 2;
251
252		iroot = nfs_fhget(sb, &dummyfh, &fattr);
253		if (IS_ERR(iroot))
254			return ERR_PTR(PTR_ERR(iroot));
255
256		root = d_alloc_root(iroot);
257		if (!root) {
258			iput(iroot);
259			return ERR_PTR(-ENOMEM);
260		}
261
262		sb->s_root = root;
263	}
264
265	/* get the info about the server and filesystem */
266	error = nfs4_server_capabilities(server, mntfh);
267	if (error < 0) {
268		dprintk("nfs_get_root: getcaps error = %d\n",
269			-error);
270		return ERR_PTR(error);
271	}
272
273	/* get the actual root for this mount */
274	error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
275	if (error < 0) {
276		dprintk("nfs_get_root: getattr error = %d\n", -error);
277		return ERR_PTR(error);
278	}
279
280	inode = nfs_fhget(sb, mntfh, &fattr);
281	if (IS_ERR(inode)) {
282		dprintk("nfs_get_root: get root inode failed\n");
283		return ERR_PTR(PTR_ERR(inode));
284	}
285
286	/* root dentries normally start off anonymous and get spliced in later
287	 * if the dentry tree reaches them; however if the dentry already
288	 * exists, we'll pick it up at this point and use it as the root
289	 */
290	mntroot = d_alloc_anon(inode);
291	if (!mntroot) {
292		iput(inode);
293		dprintk("nfs_get_root: get root dentry failed\n");
294		return ERR_PTR(-ENOMEM);
295	}
296
297	security_d_instantiate(mntroot, inode);
298
299	if (!mntroot->d_op)
300		mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
301
302	dprintk("<-- nfs4_get_root()\n");
303	return mntroot;
304}
305
306#endif /* CONFIG_NFS_V4 */
307