diff options
Diffstat (limited to 'fs/nfs/getroot.c')
-rw-r--r-- | fs/nfs/getroot.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c new file mode 100644 index 000000000000..977e59088eeb --- /dev/null +++ b/fs/nfs/getroot.c | |||
@@ -0,0 +1,306 @@ | |||
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 | |||
37 | #include <asm/system.h> | ||
38 | #include <asm/uaccess.h> | ||
39 | |||
40 | #include "nfs4_fs.h" | ||
41 | #include "delegation.h" | ||
42 | #include "internal.h" | ||
43 | |||
44 | #define NFSDBG_FACILITY NFSDBG_CLIENT | ||
45 | #define NFS_PARANOIA 1 | ||
46 | |||
47 | /* | ||
48 | * get an NFS2/NFS3 root dentry from the root filehandle | ||
49 | */ | ||
50 | struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) | ||
51 | { | ||
52 | struct nfs_server *server = NFS_SB(sb); | ||
53 | struct nfs_fsinfo fsinfo; | ||
54 | struct nfs_fattr fattr; | ||
55 | struct dentry *mntroot; | ||
56 | struct inode *inode; | ||
57 | int error; | ||
58 | |||
59 | /* create a dummy root dentry with dummy inode for this superblock */ | ||
60 | if (!sb->s_root) { | ||
61 | struct nfs_fh dummyfh; | ||
62 | struct dentry *root; | ||
63 | struct inode *iroot; | ||
64 | |||
65 | memset(&dummyfh, 0, sizeof(dummyfh)); | ||
66 | memset(&fattr, 0, sizeof(fattr)); | ||
67 | nfs_fattr_init(&fattr); | ||
68 | fattr.valid = NFS_ATTR_FATTR; | ||
69 | fattr.type = NFDIR; | ||
70 | fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; | ||
71 | fattr.nlink = 2; | ||
72 | |||
73 | iroot = nfs_fhget(sb, &dummyfh, &fattr); | ||
74 | if (IS_ERR(iroot)) | ||
75 | return ERR_PTR(PTR_ERR(iroot)); | ||
76 | |||
77 | root = d_alloc_root(iroot); | ||
78 | if (!root) { | ||
79 | iput(iroot); | ||
80 | return ERR_PTR(-ENOMEM); | ||
81 | } | ||
82 | |||
83 | sb->s_root = root; | ||
84 | } | ||
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_PTR(PTR_ERR(inode)); | ||
99 | } | ||
100 | |||
101 | /* root dentries normally start off anonymous and get spliced in later | ||
102 | * if the dentry tree reaches them; however if the dentry already | ||
103 | * exists, we'll pick it up at this point and use it as the root | ||
104 | */ | ||
105 | mntroot = d_alloc_anon(inode); | ||
106 | if (!mntroot) { | ||
107 | iput(inode); | ||
108 | dprintk("nfs_get_root: get root dentry failed\n"); | ||
109 | return ERR_PTR(-ENOMEM); | ||
110 | } | ||
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 | */ | ||
127 | int 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 | //int referral_count = 0; | ||
137 | |||
138 | dprintk("--> nfs4_path_walk(,,%s)\n", path); | ||
139 | |||
140 | fsinfo.fattr = &fattr; | ||
141 | nfs_fattr_init(&fattr); | ||
142 | |||
143 | if (*path++ != '/') { | ||
144 | dprintk("nfs4_get_root: Path does not begin with a slash\n"); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | /* Start by getting the root filehandle from the server */ | ||
149 | ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo); | ||
150 | if (ret < 0) { | ||
151 | dprintk("nfs4_get_root: getroot error = %d\n", -ret); | ||
152 | return ret; | ||
153 | } | ||
154 | |||
155 | if (fattr.type != NFDIR) { | ||
156 | printk(KERN_ERR "nfs4_get_root:" | ||
157 | " getroot encountered non-directory\n"); | ||
158 | return -ENOTDIR; | ||
159 | } | ||
160 | |||
161 | if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { | ||
162 | printk(KERN_ERR "nfs4_get_root:" | ||
163 | " getroot obtained referral\n"); | ||
164 | return -EREMOTE; | ||
165 | } | ||
166 | |||
167 | next_component: | ||
168 | dprintk("Next: %s\n", path); | ||
169 | |||
170 | /* extract the next bit of the path */ | ||
171 | if (!*path) | ||
172 | goto path_walk_complete; | ||
173 | |||
174 | name.name = path; | ||
175 | while (*path && *path != '/') | ||
176 | path++; | ||
177 | name.len = path - (const char *) name.name; | ||
178 | |||
179 | eat_dot_dir: | ||
180 | while (*path == '/') | ||
181 | path++; | ||
182 | |||
183 | if (path[0] == '.' && (path[1] == '/' || !path[1])) { | ||
184 | path += 2; | ||
185 | goto eat_dot_dir; | ||
186 | } | ||
187 | |||
188 | if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2]) | ||
189 | ) { | ||
190 | printk(KERN_ERR "nfs4_get_root:" | ||
191 | " Mount path contains reference to \"..\"\n"); | ||
192 | return -EINVAL; | ||
193 | } | ||
194 | |||
195 | /* lookup the next FH in the sequence */ | ||
196 | memcpy(&lastfh, mntfh, sizeof(lastfh)); | ||
197 | |||
198 | dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path); | ||
199 | |||
200 | ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name, | ||
201 | mntfh, &fattr); | ||
202 | if (ret < 0) { | ||
203 | dprintk("nfs4_get_root: getroot error = %d\n", -ret); | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | if (fattr.type != NFDIR) { | ||
208 | printk(KERN_ERR "nfs4_get_root:" | ||
209 | " lookupfh encountered non-directory\n"); | ||
210 | return -ENOTDIR; | ||
211 | } | ||
212 | |||
213 | if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) { | ||
214 | printk(KERN_ERR "nfs4_get_root:" | ||
215 | " lookupfh obtained referral\n"); | ||
216 | return -EREMOTE; | ||
217 | } | ||
218 | |||
219 | goto next_component; | ||
220 | |||
221 | path_walk_complete: | ||
222 | memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid)); | ||
223 | dprintk("<-- nfs4_path_walk() = 0\n"); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * get an NFS4 root dentry from the root filehandle | ||
229 | */ | ||
230 | struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) | ||
231 | { | ||
232 | struct nfs_server *server = NFS_SB(sb); | ||
233 | struct nfs_fattr fattr; | ||
234 | struct dentry *mntroot; | ||
235 | struct inode *inode; | ||
236 | int error; | ||
237 | |||
238 | dprintk("--> nfs4_get_root()\n"); | ||
239 | |||
240 | /* create a dummy root dentry with dummy inode for this superblock */ | ||
241 | if (!sb->s_root) { | ||
242 | struct nfs_fh dummyfh; | ||
243 | struct dentry *root; | ||
244 | struct inode *iroot; | ||
245 | |||
246 | memset(&dummyfh, 0, sizeof(dummyfh)); | ||
247 | memset(&fattr, 0, sizeof(fattr)); | ||
248 | nfs_fattr_init(&fattr); | ||
249 | fattr.valid = NFS_ATTR_FATTR; | ||
250 | fattr.type = NFDIR; | ||
251 | fattr.mode = S_IFDIR | S_IRUSR | S_IWUSR; | ||
252 | fattr.nlink = 2; | ||
253 | |||
254 | iroot = nfs_fhget(sb, &dummyfh, &fattr); | ||
255 | if (IS_ERR(iroot)) | ||
256 | return ERR_PTR(PTR_ERR(iroot)); | ||
257 | |||
258 | root = d_alloc_root(iroot); | ||
259 | if (!root) { | ||
260 | iput(iroot); | ||
261 | return ERR_PTR(-ENOMEM); | ||
262 | } | ||
263 | |||
264 | sb->s_root = root; | ||
265 | } | ||
266 | |||
267 | /* get the info about the server and filesystem */ | ||
268 | error = nfs4_server_capabilities(server, mntfh); | ||
269 | if (error < 0) { | ||
270 | dprintk("nfs_get_root: getcaps error = %d\n", | ||
271 | -error); | ||
272 | return ERR_PTR(error); | ||
273 | } | ||
274 | |||
275 | /* get the actual root for this mount */ | ||
276 | error = server->nfs_client->rpc_ops->getattr(server, mntfh, &fattr); | ||
277 | if (error < 0) { | ||
278 | dprintk("nfs_get_root: getattr error = %d\n", -error); | ||
279 | return ERR_PTR(error); | ||
280 | } | ||
281 | |||
282 | inode = nfs_fhget(sb, mntfh, &fattr); | ||
283 | if (IS_ERR(inode)) { | ||
284 | dprintk("nfs_get_root: get root inode failed\n"); | ||
285 | return ERR_PTR(PTR_ERR(inode)); | ||
286 | } | ||
287 | |||
288 | /* root dentries normally start off anonymous and get spliced in later | ||
289 | * if the dentry tree reaches them; however if the dentry already | ||
290 | * exists, we'll pick it up at this point and use it as the root | ||
291 | */ | ||
292 | mntroot = d_alloc_anon(inode); | ||
293 | if (!mntroot) { | ||
294 | iput(inode); | ||
295 | dprintk("nfs_get_root: get root dentry failed\n"); | ||
296 | return ERR_PTR(-ENOMEM); | ||
297 | } | ||
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 */ | ||