]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blob - src/patches/suse-2.6.27.31/patches.fixes/nfs-03-NFSv4-Replace-nfs4_path_walk-with-VFS-path-lookup-in-private-namespace
Merge branch 'master' of git://git.ipfire.org/ipfire-2.x
[people/teissler/ipfire-2.x.git] / src / patches / suse-2.6.27.31 / patches.fixes / nfs-03-NFSv4-Replace-nfs4_path_walk-with-VFS-path-lookup-in-private-namespace
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 */