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