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
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.
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.
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>
20 fs/nfs/super.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++-------
21 1 file changed, 156 insertions(+), 20 deletions(-)
23 --- linux-2.6.27-SLE11_BRANCH.orig/fs/nfs/super.c
24 +++ linux-2.6.27-SLE11_BRANCH/fs/nfs/super.c
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
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);
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,
53 +static struct file_system_type nfs4_remote_fs_type = {
54 + .owner = THIS_MODULE,
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,
61 struct file_system_type nfs4_xdev_fs_type = {
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,
68 +static struct file_system_type nfs4_remote_referral_fs_type = {
69 + .owner = THIS_MODULE,
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,
76 struct file_system_type nfs4_referral_fs_type = {
79 @@ -2294,12 +2316,12 @@ out_no_client_address:
83 - * Get the superblock for an NFS4 mountpoint
84 + * Get the superblock for the NFS4 root partition
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)
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;
95 @@ -2310,18 +2332,12 @@ static int nfs4_get_sb(struct file_syste
99 - data = kzalloc(sizeof(*data), GFP_KERNEL);
100 mntfh = kzalloc(sizeof(*mntfh), GFP_KERNEL);
101 if (data == NULL || mntfh == NULL)
104 security_init_mnt_opts(&data->lsm_opts);
106 - /* Validate the mount data */
107 - error = nfs4_validate_mount_data(raw_data, data, dev_name);
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;
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);
123 @@ -2370,13 +2386,9 @@ static int nfs4_get_sb(struct file_syste
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);
137 @@ -2391,6 +2403,101 @@ error_splat_super:
141 +static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
142 + int flags, void *data, const char *hostname)
144 + struct vfsmount *root_mnt;
145 + char *root_devname;
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);
158 +static int nfs_follow_remote_path(struct vfsmount *root_mnt,
159 + const char *export_path, struct vfsmount *mnt_target)
161 + struct mnt_namespace *ns_private;
162 + struct nameidata nd;
163 + struct super_block *s;
166 + ns_private = create_mnt_ns(root_mnt);
167 + ret = PTR_ERR(ns_private);
168 + if (IS_ERR(ns_private))
171 + ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt,
172 + export_path, LOOKUP_FOLLOW, &nd);
174 + put_mnt_ns(ns_private);
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);
184 + path_put(&nd.path);
185 + down_write(&s->s_umount);
194 + * Get the superblock for an NFS4 mountpoint
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)
199 + struct nfs_parsed_mount_data *data;
201 + struct vfsmount *root_mnt;
202 + int error = -ENOMEM;
204 + data = kzalloc(sizeof(*data), GFP_KERNEL);
206 + goto out_free_data;
208 + /* Validate the mount data */
209 + error = nfs4_validate_mount_data(raw_data, data, dev_name);
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;
219 + error = PTR_ERR(root_mnt);
220 + if (IS_ERR(root_mnt))
223 + error = nfs_follow_remote_path(root_mnt, export_path, mnt);
226 + kfree(data->client_address);
227 + kfree(data->nfs_server.export_path);
228 + kfree(data->nfs_server.hostname);
231 + dprintk("<-- nfs4_get_sb() = %d%s\n", error,
232 + error != 0 ? " [error]" : "");
236 static void nfs4_kill_super(struct super_block *sb)
238 struct nfs_server *server = NFS_SB(sb);
239 @@ -2486,12 +2593,9 @@ error_splat_super:
244 - * Create an NFS4 server record on referral traversal
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)
253 struct nfs_clone_mount *data = raw_data;
254 struct super_block *s;
255 @@ -2571,4 +2675,36 @@ error_splat_super:
260 + * Create an NFS4 server record on referral traversal
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)
266 + struct nfs_clone_mount *data = raw_data;
268 + struct vfsmount *root_mnt;
271 + dprintk("--> nfs4_referral_get_sb()\n");
273 + export_path = data->mnt_path;
274 + data->mnt_path = "/";
276 + root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
277 + flags, data, data->hostname);
278 + data->mnt_path = export_path;
280 + error = PTR_ERR(root_mnt);
281 + if (IS_ERR(root_mnt))
284 + error = nfs_follow_remote_path(root_mnt, export_path, mnt);
286 + dprintk("<-- nfs4_referral_get_sb() = %d%s\n", error,
287 + error != 0 ? " [error]" : "");
291 #endif /* CONFIG_NFS_V4 */