]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From c02d7adf8c5429727a98bad1d039bccad4c61c50 Mon Sep 17 00:00:00 2001 |
2 | From: Trond Myklebust <Trond.Myklebust@netapp.com> | |
3 | Date: Mon, 22 Jun 2009 15:09:14 -0400 | |
4 | Subject: [PATCH 3/5] NFSv4: Replace nfs4_path_walk() with VFS path lookup in a private namespace | |
5 | ||
6 | As noted in the previous patch, the NFSv4 client mount code currently | |
7 | has several limitations. If the mount path contains symlinks, or | |
8 | referrals, or even if it just contains a '..', then the client code in | |
9 | nfs4_path_walk() will fail with an error. | |
10 | ||
11 | This patch replaces the nfs4_path_walk()-based lookup with a helper | |
12 | function that sets up a private namespace to represent the namespace on the | |
13 | server, then uses the ordinary VFS and NFS path lookup code to walk down the | |
14 | mount path in that namespace. | |
15 | ||
16 | Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> | |
17 | Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> | |
18 | Acked-by: NeilBrown <neilb@suse.de> | |
19 | --- | |
20 | fs/nfs/super.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++------- | |
21 | 1 file changed, 156 insertions(+), 20 deletions(-) | |
22 | ||
23 | --- linux-2.6.27-SLE11_BRANCH.orig/fs/nfs/super.c | |
24 | +++ linux-2.6.27-SLE11_BRANCH/fs/nfs/super.c | |
25 | @@ -42,6 +42,8 @@ | |
26 | #include <linux/smp_lock.h> | |
27 | #include <linux/seq_file.h> | |
28 | #include <linux/mount.h> | |
29 | +#include <linux/mnt_namespace.h> | |
30 | +#include <linux/namei.h> | |
31 | #include <linux/nfs_idmap.h> | |
32 | #include <linux/vfs.h> | |
33 | #include <linux/inet.h> | |
34 | @@ -244,10 +246,14 @@ static const struct super_operations nfs | |
35 | #ifdef CONFIG_NFS_V4 | |
36 | static int nfs4_get_sb(struct file_system_type *fs_type, | |
37 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | |
38 | +static int nfs4_remote_get_sb(struct file_system_type *fs_type, | |
39 | + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | |
40 | static int nfs4_xdev_get_sb(struct file_system_type *fs_type, | |
41 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | |
42 | static int nfs4_referral_get_sb(struct file_system_type *fs_type, | |
43 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | |
44 | +static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, | |
45 | + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); | |
46 | static void nfs4_kill_super(struct super_block *sb); | |
47 | ||
48 | static struct file_system_type nfs4_fs_type = { | |
49 | @@ -258,6 +264,14 @@ static struct file_system_type nfs4_fs_t | |
50 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | |
51 | }; | |
52 | ||
53 | +static struct file_system_type nfs4_remote_fs_type = { | |
54 | + .owner = THIS_MODULE, | |
55 | + .name = "nfs4", | |
56 | + .get_sb = nfs4_remote_get_sb, | |
57 | + .kill_sb = nfs4_kill_super, | |
58 | + .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | |
59 | +}; | |
60 | + | |
61 | struct file_system_type nfs4_xdev_fs_type = { | |
62 | .owner = THIS_MODULE, | |
63 | .name = "nfs4", | |
64 | @@ -266,6 +280,14 @@ struct file_system_type nfs4_xdev_fs_typ | |
65 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | |
66 | }; | |
67 | ||
68 | +static struct file_system_type nfs4_remote_referral_fs_type = { | |
69 | + .owner = THIS_MODULE, | |
70 | + .name = "nfs4", | |
71 | + .get_sb = nfs4_remote_referral_get_sb, | |
72 | + .kill_sb = nfs4_kill_super, | |
73 | + .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | |
74 | +}; | |
75 | + | |
76 | struct file_system_type nfs4_referral_fs_type = { | |
77 | .owner = THIS_MODULE, | |
78 | .name = "nfs4", | |
79 | @@ -2294,12 +2316,12 @@ out_no_client_address: | |
80 | } | |
81 | ||
82 | /* | |
83 | - * Get the superblock for an NFS4 mountpoint | |
84 | + * Get the superblock for the NFS4 root partition | |
85 | */ | |
86 | -static int nfs4_get_sb(struct file_system_type *fs_type, | |
87 | +static int nfs4_remote_get_sb(struct file_system_type *fs_type, | |
88 | int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | |
89 | { | |
90 | - struct nfs_parsed_mount_data *data; | |
91 | + struct nfs_parsed_mount_data *data = raw_data; | |
92 | struct super_block *s; | |
93 | struct nfs_server *server; | |
94 | struct nfs_fh *mntfh; | |
95 | @@ -2310,18 +2332,12 @@ static int nfs4_get_sb(struct file_syste | |
96 | }; | |
97 | int error = -ENOMEM; | |
98 | ||
99 | - data = kzalloc(sizeof(*data), GFP_KERNEL); | |
100 | mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL); | |
101 | if (data == NULL || mntfh == NULL) | |
102 | goto out_free_fh; | |
103 | ||
104 | security_init_mnt_opts(&data->lsm_opts); | |
105 | ||
106 | - /* Validate the mount data */ | |
107 | - error = nfs4_validate_mount_data(raw_data, data, dev_name); | |
108 | - if (error < 0) | |
109 | - goto out; | |
110 | - | |
111 | /* Get a volume representation */ | |
112 | server = nfs4_create_server(data, mntfh); | |
113 | if (IS_ERR(server)) { | |
114 | @@ -2334,7 +2350,7 @@ static int nfs4_get_sb(struct file_syste | |
115 | compare_super = NULL; | |
116 | ||
117 | /* Get a superblock - note that we may end up sharing one that already exists */ | |
118 | - s = sget(fs_type, compare_super, nfs_set_super, &sb_mntdata); | |
119 | + s = sget(&nfs4_fs_type, compare_super, nfs_set_super, &sb_mntdata); | |
120 | if (IS_ERR(s)) { | |
121 | error = PTR_ERR(s); | |
122 | goto out_free; | |
123 | @@ -2370,13 +2386,9 @@ static int nfs4_get_sb(struct file_syste | |
124 | error = 0; | |
125 | ||
126 | out: | |
127 | - kfree(data->client_address); | |
128 | - kfree(data->nfs_server.export_path); | |
129 | - kfree(data->nfs_server.hostname); | |
130 | security_free_mnt_opts(&data->lsm_opts); | |
131 | out_free_fh: | |
132 | kfree(mntfh); | |
133 | - kfree(data); | |
134 | return error; | |
135 | ||
136 | out_free: | |
137 | @@ -2391,6 +2403,101 @@ error_splat_super: | |
138 | goto out; | |
139 | } | |
140 | ||
141 | +static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, | |
142 | + int flags, void *data, const char *hostname) | |
143 | +{ | |
144 | + struct vfsmount *root_mnt; | |
145 | + char *root_devname; | |
146 | + size_t len; | |
147 | + | |
148 | + len = strlen(hostname) + 3; | |
149 | + root_devname = kmalloc(len, GFP_KERNEL); | |
150 | + if (root_devname == NULL) | |
151 | + return ERR_PTR(-ENOMEM); | |
152 | + snprintf(root_devname, len, "%s:/", hostname); | |
153 | + root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data); | |
154 | + kfree(root_devname); | |
155 | + return root_mnt; | |
156 | +} | |
157 | + | |
158 | +static int nfs_follow_remote_path(struct vfsmount *root_mnt, | |
159 | + const char *export_path, struct vfsmount *mnt_target) | |
160 | +{ | |
161 | + struct mnt_namespace *ns_private; | |
162 | + struct nameidata nd; | |
163 | + struct super_block *s; | |
164 | + int ret; | |
165 | + | |
166 | + ns_private = create_mnt_ns(root_mnt); | |
167 | + ret = PTR_ERR(ns_private); | |
168 | + if (IS_ERR(ns_private)) | |
169 | + goto out_mntput; | |
170 | + | |
171 | + ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt, | |
172 | + export_path, LOOKUP_FOLLOW, &nd); | |
173 | + | |
174 | + put_mnt_ns(ns_private); | |
175 | + | |
176 | + if (ret != 0) | |
177 | + goto out_err; | |
178 | + | |
179 | + s = nd.path.mnt->mnt_sb; | |
180 | + atomic_inc(&s->s_active); | |
181 | + mnt_target->mnt_sb = s; | |
182 | + mnt_target->mnt_root = dget(nd.path.dentry); | |
183 | + | |
184 | + path_put(&nd.path); | |
185 | + down_write(&s->s_umount); | |
186 | + return 0; | |
187 | +out_mntput: | |
188 | + mntput(root_mnt); | |
189 | +out_err: | |
190 | + return ret; | |
191 | +} | |
192 | + | |
193 | +/* | |
194 | + * Get the superblock for an NFS4 mountpoint | |
195 | + */ | |
196 | +static int nfs4_get_sb(struct file_system_type *fs_type, | |
197 | + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) | |
198 | +{ | |
199 | + struct nfs_parsed_mount_data *data; | |
200 | + char *export_path; | |
201 | + struct vfsmount *root_mnt; | |
202 | + int error = -ENOMEM; | |
203 | + | |
204 | + data = kzalloc(sizeof(*data), GFP_KERNEL); | |
205 | + if (data == NULL) | |
206 | + goto out_free_data; | |
207 | + | |
208 | + /* Validate the mount data */ | |
209 | + error = nfs4_validate_mount_data(raw_data, data, dev_name); | |
210 | + if (error < 0) | |
211 | + goto out; | |
212 | + | |
213 | + export_path = data->nfs_server.export_path; | |
214 | + data->nfs_server.export_path = "/"; | |
215 | + root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, data, | |
216 | + data->nfs_server.hostname); | |
217 | + data->nfs_server.export_path = export_path; | |
218 | + | |
219 | + error = PTR_ERR(root_mnt); | |
220 | + if (IS_ERR(root_mnt)) | |
221 | + goto out; | |
222 | + | |
223 | + error = nfs_follow_remote_path(root_mnt, export_path, mnt); | |
224 | + | |
225 | +out: | |
226 | + kfree(data->client_address); | |
227 | + kfree(data->nfs_server.export_path); | |
228 | + kfree(data->nfs_server.hostname); | |
229 | +out_free_data: | |
230 | + kfree(data); | |
231 | + dprintk("<-- nfs4_get_sb() = %d%s\n", error, | |
232 | + error != 0 ? " [error]" : ""); | |
233 | + return error; | |
234 | +} | |
235 | + | |
236 | static void nfs4_kill_super(struct super_block *sb) | |
237 | { | |
238 | struct nfs_server *server = NFS_SB(sb); | |
239 | @@ -2486,12 +2593,9 @@ error_splat_super: | |
240 | return error; | |
241 | } | |
242 | ||
243 | -/* | |
244 | - * Create an NFS4 server record on referral traversal | |
245 | - */ | |
246 | -static int nfs4_referral_get_sb(struct file_system_type *fs_type, int flags, | |
247 | - const char *dev_name, void *raw_data, | |
248 | - struct vfsmount *mnt) | |
249 | +static int nfs4_remote_referral_get_sb(struct file_system_type *fs_type, | |
250 | + int flags, const char *dev_name, void *raw_data, | |
251 | + struct vfsmount *mnt) | |
252 | { | |
253 | struct nfs_clone_mount *data = raw_data; | |
254 | struct super_block *s; | |
255 | @@ -2571,4 +2675,36 @@ error_splat_super: | |
256 | return error; | |
257 | } | |
258 | ||
259 | +/* | |
260 | + * Create an NFS4 server record on referral traversal | |
261 | + */ | |
262 | +static int nfs4_referral_get_sb(struct file_system_type *fs_type, | |
263 | + int flags, const char *dev_name, void *raw_data, | |
264 | + struct vfsmount *mnt) | |
265 | +{ | |
266 | + struct nfs_clone_mount *data = raw_data; | |
267 | + char *export_path; | |
268 | + struct vfsmount *root_mnt; | |
269 | + int error; | |
270 | + | |
271 | + dprintk("--> nfs4_referral_get_sb()\n"); | |
272 | + | |
273 | + export_path = data->mnt_path; | |
274 | + data->mnt_path = "/"; | |
275 | + | |
276 | + root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type, | |
277 | + flags, data, data->hostname); | |
278 | + data->mnt_path = export_path; | |
279 | + | |
280 | + error = PTR_ERR(root_mnt); | |
281 | + if (IS_ERR(root_mnt)) | |
282 | + goto out; | |
283 | + | |
284 | + error = nfs_follow_remote_path(root_mnt, export_path, mnt); | |
285 | +out: | |
286 | + dprintk("<-- nfs4_referral_get_sb() = %d%s\n", error, | |
287 | + error != 0 ? " [error]" : ""); | |
288 | + return error; | |
289 | +} | |
290 | + | |
291 | #endif /* CONFIG_NFS_V4 */ |