NFS: Fix a number of bugs in the idmapper
[deliverable/linux.git] / fs / nfs / nfs4super.c
CommitLineData
129d1977
BS
1/*
2 * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com>
3 */
4#include <linux/init.h>
fbdefd64 5#include <linux/module.h>
129d1977 6#include <linux/nfs_idmap.h>
fbdefd64 7#include <linux/nfs4_mount.h>
466bfe7f 8#include <linux/nfs_fs.h>
fbdefd64 9#include "internal.h"
466bfe7f 10#include "nfs4_fs.h"
129d1977 11
fbdefd64
BS
12#define NFSDBG_FACILITY NFSDBG_VFS
13
14static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
15 int flags, const char *dev_name, void *raw_data);
16static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type,
17 int flags, const char *dev_name, void *raw_data);
18static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
19 int flags, const char *dev_name, void *raw_data);
20static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
21 int flags, const char *dev_name, void *raw_data);
22
23static struct file_system_type nfs4_fs_type = {
24 .owner = THIS_MODULE,
25 .name = "nfs4",
26 .mount = nfs_fs_mount,
27 .kill_sb = nfs_kill_super,
28 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
29};
30
31static struct file_system_type nfs4_remote_fs_type = {
32 .owner = THIS_MODULE,
33 .name = "nfs4",
34 .mount = nfs4_remote_mount,
35 .kill_sb = nfs_kill_super,
36 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
37};
38
39struct file_system_type nfs4_xdev_fs_type = {
40 .owner = THIS_MODULE,
41 .name = "nfs4",
42 .mount = nfs4_xdev_mount,
43 .kill_sb = nfs_kill_super,
44 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
45};
46
47static struct file_system_type nfs4_remote_referral_fs_type = {
48 .owner = THIS_MODULE,
49 .name = "nfs4",
50 .mount = nfs4_remote_referral_mount,
51 .kill_sb = nfs_kill_super,
52 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
53};
54
55struct file_system_type nfs4_referral_fs_type = {
56 .owner = THIS_MODULE,
57 .name = "nfs4",
58 .mount = nfs4_referral_mount,
59 .kill_sb = nfs_kill_super,
60 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
61};
62
63static const struct super_operations nfs4_sops = {
64 .alloc_inode = nfs_alloc_inode,
65 .destroy_inode = nfs_destroy_inode,
66 .write_inode = nfs4_write_inode,
67 .put_super = nfs_put_super,
68 .statfs = nfs_statfs,
69 .evict_inode = nfs4_evict_inode,
70 .umount_begin = nfs_umount_begin,
71 .show_options = nfs_show_options,
72 .show_devname = nfs_show_devname,
73 .show_path = nfs_show_path,
74 .show_stats = nfs_show_stats,
75 .remount_fs = nfs_remount,
76};
77
78/*
79 * Set up an NFS4 superblock
80 */
81static void nfs4_fill_super(struct super_block *sb,
82 struct nfs_mount_info *mount_info)
83{
84 sb->s_time_gran = 1;
85 sb->s_op = &nfs4_sops;
86 /*
87 * The VFS shouldn't apply the umask to mode bits. We will do
88 * so ourselves when necessary.
89 */
90 sb->s_flags |= MS_POSIXACL;
91 sb->s_xattr = nfs4_xattr_handlers;
92 nfs_initialise_sb(sb);
93}
94
95/*
96 * Get the superblock for the NFS4 root partition
97 */
98static struct dentry *
99nfs4_remote_mount(struct file_system_type *fs_type, int flags,
100 const char *dev_name, void *info)
101{
102 struct nfs_mount_info *mount_info = info;
103 struct nfs_server *server;
104 struct dentry *mntroot = ERR_PTR(-ENOMEM);
105
106 mount_info->fill_super = nfs4_fill_super;
107 mount_info->set_security = nfs_set_sb_security;
108
109 /* Get a volume representation */
110 server = nfs4_create_server(mount_info->parsed, mount_info->mntfh);
111 if (IS_ERR(server)) {
112 mntroot = ERR_CAST(server);
113 goto out;
114 }
115
116 mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
117
118out:
119 return mntroot;
120}
121
122static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
123 int flags, void *data, const char *hostname)
124{
125 struct vfsmount *root_mnt;
126 char *root_devname;
127 size_t len;
128
129 len = strlen(hostname) + 5;
130 root_devname = kmalloc(len, GFP_KERNEL);
131 if (root_devname == NULL)
132 return ERR_PTR(-ENOMEM);
133 /* Does hostname needs to be enclosed in brackets? */
134 if (strchr(hostname, ':'))
135 snprintf(root_devname, len, "[%s]:/", hostname);
136 else
137 snprintf(root_devname, len, "%s:/", hostname);
138 root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
139 kfree(root_devname);
140 return root_mnt;
141}
142
143struct nfs_referral_count {
144 struct list_head list;
145 const struct task_struct *task;
146 unsigned int referral_count;
147};
148
149static LIST_HEAD(nfs_referral_count_list);
150static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
151
152static struct nfs_referral_count *nfs_find_referral_count(void)
153{
154 struct nfs_referral_count *p;
155
156 list_for_each_entry(p, &nfs_referral_count_list, list) {
157 if (p->task == current)
158 return p;
159 }
160 return NULL;
161}
162
163#define NFS_MAX_NESTED_REFERRALS 2
164
165static int nfs_referral_loop_protect(void)
166{
167 struct nfs_referral_count *p, *new;
168 int ret = -ENOMEM;
169
170 new = kmalloc(sizeof(*new), GFP_KERNEL);
171 if (!new)
172 goto out;
173 new->task = current;
174 new->referral_count = 1;
175
176 ret = 0;
177 spin_lock(&nfs_referral_count_list_lock);
178 p = nfs_find_referral_count();
179 if (p != NULL) {
180 if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
181 ret = -ELOOP;
182 else
183 p->referral_count++;
184 } else {
185 list_add(&new->list, &nfs_referral_count_list);
186 new = NULL;
187 }
188 spin_unlock(&nfs_referral_count_list_lock);
189 kfree(new);
190out:
191 return ret;
192}
193
194static void nfs_referral_loop_unprotect(void)
195{
196 struct nfs_referral_count *p;
197
198 spin_lock(&nfs_referral_count_list_lock);
199 p = nfs_find_referral_count();
200 p->referral_count--;
201 if (p->referral_count == 0)
202 list_del(&p->list);
203 else
204 p = NULL;
205 spin_unlock(&nfs_referral_count_list_lock);
206 kfree(p);
207}
208
209static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
210 const char *export_path)
211{
212 struct dentry *dentry;
213 int err;
214
215 if (IS_ERR(root_mnt))
216 return ERR_CAST(root_mnt);
217
218 err = nfs_referral_loop_protect();
219 if (err) {
220 mntput(root_mnt);
221 return ERR_PTR(err);
222 }
223
224 dentry = mount_subtree(root_mnt, export_path);
225 nfs_referral_loop_unprotect();
226
227 return dentry;
228}
229
230struct dentry *nfs4_try_mount(int flags, const char *dev_name,
231 struct nfs_mount_info *mount_info)
232{
233 char *export_path;
234 struct vfsmount *root_mnt;
235 struct dentry *res;
236 struct nfs_parsed_mount_data *data = mount_info->parsed;
237
238 dfprintk(MOUNT, "--> nfs4_try_mount()\n");
239
240 mount_info->fill_super = nfs4_fill_super;
241
242 export_path = data->nfs_server.export_path;
243 data->nfs_server.export_path = "/";
244 root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
245 data->nfs_server.hostname);
246 data->nfs_server.export_path = export_path;
247
248 res = nfs_follow_remote_path(root_mnt, export_path);
249
250 dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
251 IS_ERR(res) ? PTR_ERR(res) : 0,
252 IS_ERR(res) ? " [error]" : "");
253 return res;
254}
255
256/*
257 * Clone an NFS4 server record on xdev traversal (FSID-change)
258 */
259static struct dentry *
260nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
261 const char *dev_name, void *raw_data)
262{
263 struct nfs_mount_info mount_info = {
264 .fill_super = nfs_clone_super,
265 .set_security = nfs_clone_sb_security,
266 .cloned = raw_data,
267 };
268 return nfs_xdev_mount_common(&nfs4_fs_type, flags, dev_name, &mount_info);
269}
270
271static struct dentry *
272nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
273 const char *dev_name, void *raw_data)
274{
275 struct nfs_mount_info mount_info = {
276 .fill_super = nfs4_fill_super,
277 .set_security = nfs_clone_sb_security,
278 .cloned = raw_data,
279 };
280 struct nfs_server *server;
281 struct dentry *mntroot = ERR_PTR(-ENOMEM);
282
283 dprintk("--> nfs4_referral_get_sb()\n");
284
285 mount_info.mntfh = nfs_alloc_fhandle();
286 if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
287 goto out;
288
289 /* create a new volume representation */
290 server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
291 if (IS_ERR(server)) {
292 mntroot = ERR_CAST(server);
293 goto out;
294 }
295
296 mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info);
297out:
298 nfs_free_fhandle(mount_info.mntfh);
299 return mntroot;
300}
301
302/*
303 * Create an NFS4 server record on referral traversal
304 */
305static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
306 int flags, const char *dev_name, void *raw_data)
307{
308 struct nfs_clone_mount *data = raw_data;
309 char *export_path;
310 struct vfsmount *root_mnt;
311 struct dentry *res;
312
313 dprintk("--> nfs4_referral_mount()\n");
314
315 export_path = data->mnt_path;
316 data->mnt_path = "/";
317
318 root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
319 flags, data, data->hostname);
320 data->mnt_path = export_path;
321
322 res = nfs_follow_remote_path(root_mnt, export_path);
323 dprintk("<-- nfs4_referral_mount() = %ld%s\n",
324 IS_ERR(res) ? PTR_ERR(res) : 0,
325 IS_ERR(res) ? " [error]" : "");
326 return res;
327}
328
329
129d1977
BS
330int __init init_nfs_v4(void)
331{
332 int err;
333
334 err = nfs_idmap_init();
335 if (err)
336 goto out;
337
466bfe7f
BS
338 err = nfs4_register_sysctl();
339 if (err)
340 goto out1;
341
fbdefd64
BS
342 err = register_filesystem(&nfs4_fs_type);
343 if (err < 0)
344 goto out2;
345
129d1977 346 return 0;
fbdefd64
BS
347out2:
348 nfs4_unregister_sysctl();
466bfe7f
BS
349out1:
350 nfs_idmap_quit();
129d1977
BS
351out:
352 return err;
353}
354
bb6e071f 355void exit_nfs_v4(void)
129d1977 356{
fbdefd64 357 unregister_filesystem(&nfs4_fs_type);
466bfe7f 358 nfs4_unregister_sysctl();
129d1977
BS
359 nfs_idmap_quit();
360}
This page took 0.037946 seconds and 5 git commands to generate.