xref: /openbmc/linux/fs/nfs/getroot.c (revision e8e0929d)
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/security.h>
34 
35 #include <asm/system.h>
36 #include <asm/uaccess.h>
37 
38 #include "nfs4_fs.h"
39 #include "delegation.h"
40 #include "internal.h"
41 
42 #define NFSDBG_FACILITY		NFSDBG_CLIENT
43 
44 /*
45  * Set the superblock root dentry.
46  * Note that this function frees the inode in case of error.
47  */
48 static int nfs_superblock_set_dummy_root(struct super_block *sb, struct inode *inode)
49 {
50 	/* The mntroot acts as the dummy root dentry for this superblock */
51 	if (sb->s_root == NULL) {
52 		sb->s_root = d_alloc_root(inode);
53 		if (sb->s_root == NULL) {
54 			iput(inode);
55 			return -ENOMEM;
56 		}
57 		/* Circumvent igrab(): we know the inode is not being freed */
58 		atomic_inc(&inode->i_count);
59 		/*
60 		 * Ensure that this dentry is invisible to d_find_alias().
61 		 * Otherwise, it may be spliced into the tree by
62 		 * d_materialise_unique if a parent directory from the same
63 		 * filesystem gets mounted at a later time.
64 		 * This again causes shrink_dcache_for_umount_subtree() to
65 		 * Oops, since the test for IS_ROOT() will fail.
66 		 */
67 		spin_lock(&dcache_lock);
68 		list_del_init(&sb->s_root->d_alias);
69 		spin_unlock(&dcache_lock);
70 	}
71 	return 0;
72 }
73 
74 /*
75  * get an NFS2/NFS3 root dentry from the root filehandle
76  */
77 struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh)
78 {
79 	struct nfs_server *server = NFS_SB(sb);
80 	struct nfs_fsinfo fsinfo;
81 	struct nfs_fattr fattr;
82 	struct dentry *mntroot;
83 	struct inode *inode;
84 	int error;
85 
86 	/* get the actual root for this mount */
87 	fsinfo.fattr = &fattr;
88 
89 	error = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
90 	if (error < 0) {
91 		dprintk("nfs_get_root: getattr error = %d\n", -error);
92 		return ERR_PTR(error);
93 	}
94 
95 	inode = nfs_fhget(sb, mntfh, fsinfo.fattr);
96 	if (IS_ERR(inode)) {
97 		dprintk("nfs_get_root: get root inode failed\n");
98 		return ERR_CAST(inode);
99 	}
100 
101 	error = nfs_superblock_set_dummy_root(sb, inode);
102 	if (error != 0)
103 		return ERR_PTR(error);
104 
105 	/* root dentries normally start off anonymous and get spliced in later
106 	 * if the dentry tree reaches them; however if the dentry already
107 	 * exists, we'll pick it up at this point and use it as the root
108 	 */
109 	mntroot = d_obtain_alias(inode);
110 	if (IS_ERR(mntroot)) {
111 		dprintk("nfs_get_root: get root dentry failed\n");
112 		return mntroot;
113 	}
114 
115 	security_d_instantiate(mntroot, inode);
116 
117 	if (!mntroot->d_op)
118 		mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
119 
120 	return mntroot;
121 }
122 
123 #ifdef CONFIG_NFS_V4
124 
125 /*
126  * Do a simple pathwalk from the root FH of the server to the nominated target
127  * of the mountpoint
128  * - give error on symlinks
129  * - give error on ".." occurring in the path
130  * - follow traversals
131  */
132 int nfs4_path_walk(struct nfs_server *server,
133 		   struct nfs_fh *mntfh,
134 		   const char *path)
135 {
136 	struct nfs_fsinfo fsinfo;
137 	struct nfs_fattr fattr;
138 	struct nfs_fh lastfh;
139 	struct qstr name;
140 	int ret;
141 
142 	dprintk("--> nfs4_path_walk(,,%s)\n", path);
143 
144 	fsinfo.fattr = &fattr;
145 	nfs_fattr_init(&fattr);
146 
147 	/* Eat leading slashes */
148 	while (*path == '/')
149 		path++;
150 
151 	/* Start by getting the root filehandle from the server */
152 	ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
153 	if (ret < 0) {
154 		dprintk("nfs4_get_root: getroot error = %d\n", -ret);
155 		return ret;
156 	}
157 
158 	if (!S_ISDIR(fattr.mode)) {
159 		printk(KERN_ERR "nfs4_get_root:"
160 		       " getroot encountered non-directory\n");
161 		return -ENOTDIR;
162 	}
163 
164 	/* FIXME: It is quite valid for the server to return a referral here */
165 	if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
166 		printk(KERN_ERR "nfs4_get_root:"
167 		       " getroot obtained referral\n");
168 		return -EREMOTE;
169 	}
170 
171 next_component:
172 	dprintk("Next: %s\n", path);
173 
174 	/* extract the next bit of the path */
175 	if (!*path)
176 		goto path_walk_complete;
177 
178 	name.name = path;
179 	while (*path && *path != '/')
180 		path++;
181 	name.len = path - (const char *) name.name;
182 
183 	if (name.len > NFS4_MAXNAMLEN)
184 		return -ENAMETOOLONG;
185 
186 eat_dot_dir:
187 	while (*path == '/')
188 		path++;
189 
190 	if (path[0] == '.' && (path[1] == '/' || !path[1])) {
191 		path += 2;
192 		goto eat_dot_dir;
193 	}
194 
195 	/* FIXME: Why shouldn't the user be able to use ".." in the path? */
196 	if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2])
197 	    ) {
198 		printk(KERN_ERR "nfs4_get_root:"
199 		       " Mount path contains reference to \"..\"\n");
200 		return -EINVAL;
201 	}
202 
203 	/* lookup the next FH in the sequence */
204 	memcpy(&lastfh, mntfh, sizeof(lastfh));
205 
206 	dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path);
207 
208 	ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name,
209 						    mntfh, &fattr);
210 	if (ret < 0) {
211 		dprintk("nfs4_get_root: getroot error = %d\n", -ret);
212 		return ret;
213 	}
214 
215 	if (!S_ISDIR(fattr.mode)) {
216 		printk(KERN_ERR "nfs4_get_root:"
217 		       " lookupfh encountered non-directory\n");
218 		return -ENOTDIR;
219 	}
220 
221 	/* FIXME: Referrals are quite valid here too */
222 	if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
223 		printk(KERN_ERR "nfs4_get_root:"
224 		       " lookupfh obtained referral\n");
225 		return -EREMOTE;
226 	}
227 
228 	goto next_component;
229 
230 path_walk_complete:
231 	memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
232 	dprintk("<-- nfs4_path_walk() = 0\n");
233 	return 0;
234 }
235 
236 /*
237  * get an NFS4 root dentry from the root filehandle
238  */
239 struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh)
240 {
241 	struct nfs_server *server = NFS_SB(sb);
242 	struct nfs_fattr fattr;
243 	struct dentry *mntroot;
244 	struct inode *inode;
245 	int error;
246 
247 	dprintk("--> nfs4_get_root()\n");
248 
249 	/* get the info about the server and filesystem */
250 	error = nfs4_server_capabilities(server, mntfh);
251 	if (error < 0) {
252 		dprintk("nfs_get_root: getcaps error = %d\n",
253 			-error);
254 		return ERR_PTR(error);
255 	}
256 
257 	/* get the actual root for this mount */
258 	error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr);
259 	if (error < 0) {
260 		dprintk("nfs_get_root: getattr error = %d\n", -error);
261 		return ERR_PTR(error);
262 	}
263 
264 	inode = nfs_fhget(sb, mntfh, &fattr);
265 	if (IS_ERR(inode)) {
266 		dprintk("nfs_get_root: get root inode failed\n");
267 		return ERR_CAST(inode);
268 	}
269 
270 	error = nfs_superblock_set_dummy_root(sb, inode);
271 	if (error != 0)
272 		return ERR_PTR(error);
273 
274 	/* root dentries normally start off anonymous and get spliced in later
275 	 * if the dentry tree reaches them; however if the dentry already
276 	 * exists, we'll pick it up at this point and use it as the root
277 	 */
278 	mntroot = d_obtain_alias(inode);
279 	if (IS_ERR(mntroot)) {
280 		dprintk("nfs_get_root: get root dentry failed\n");
281 		return mntroot;
282 	}
283 
284 	security_d_instantiate(mntroot, inode);
285 
286 	if (!mntroot->d_op)
287 		mntroot->d_op = server->nfs_client->rpc_ops->dentry_ops;
288 
289 	dprintk("<-- nfs4_get_root()\n");
290 	return mntroot;
291 }
292 
293 #endif /* CONFIG_NFS_V4 */
294