blob: c70e1730755cd030914ad67d9e514178b41655ec [file] [log] [blame]
Bryan Schumaker129d1972012-07-16 16:39:13 -04001/*
2 * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com>
3 */
4#include <linux/init.h>
Bryan Schumakerfbdefd62012-07-16 16:39:20 -04005#include <linux/module.h>
Bryan Schumaker129d1972012-07-16 16:39:13 -04006#include <linux/nfs_idmap.h>
Bryan Schumakerfbdefd62012-07-16 16:39:20 -04007#include <linux/nfs4_mount.h>
Bryan Schumaker466bfe72012-07-16 16:39:14 -04008#include <linux/nfs_fs.h>
Bryan Schumakerfbdefd62012-07-16 16:39:20 -04009#include "internal.h"
Bryan Schumaker466bfe72012-07-16 16:39:14 -040010#include "nfs4_fs.h"
Bryan Schumakerab7017a2012-07-30 16:05:16 -040011#include "nfs.h"
Bryan Schumaker129d1972012-07-16 16:39:13 -040012
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040013#define NFSDBG_FACILITY NFSDBG_VFS
14
15static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
16 int flags, const char *dev_name, void *raw_data);
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040017static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
18 int flags, const char *dev_name, void *raw_data);
19static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
20 int flags, const char *dev_name, void *raw_data);
21
22static struct file_system_type nfs4_fs_type = {
23 .owner = THIS_MODULE,
24 .name = "nfs4",
25 .mount = nfs_fs_mount,
26 .kill_sb = nfs_kill_super,
27 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
28};
29
30static struct file_system_type nfs4_remote_fs_type = {
31 .owner = THIS_MODULE,
32 .name = "nfs4",
33 .mount = nfs4_remote_mount,
34 .kill_sb = nfs_kill_super,
35 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
36};
37
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040038static struct file_system_type nfs4_remote_referral_fs_type = {
39 .owner = THIS_MODULE,
40 .name = "nfs4",
41 .mount = nfs4_remote_referral_mount,
42 .kill_sb = nfs_kill_super,
43 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
44};
45
46struct file_system_type nfs4_referral_fs_type = {
47 .owner = THIS_MODULE,
48 .name = "nfs4",
49 .mount = nfs4_referral_mount,
50 .kill_sb = nfs_kill_super,
51 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
52};
53
54static const struct super_operations nfs4_sops = {
55 .alloc_inode = nfs_alloc_inode,
56 .destroy_inode = nfs_destroy_inode,
57 .write_inode = nfs4_write_inode,
58 .put_super = nfs_put_super,
59 .statfs = nfs_statfs,
60 .evict_inode = nfs4_evict_inode,
61 .umount_begin = nfs_umount_begin,
62 .show_options = nfs_show_options,
63 .show_devname = nfs_show_devname,
64 .show_path = nfs_show_path,
65 .show_stats = nfs_show_stats,
66 .remount_fs = nfs_remount,
67};
68
Bryan Schumakerab7017a2012-07-30 16:05:16 -040069struct nfs_subversion nfs_v4 = {
70 .owner = THIS_MODULE,
71 .nfs_fs = &nfs4_fs_type,
72 .rpc_vers = &nfs_version4,
73 .rpc_ops = &nfs_v4_clientops,
Bryan Schumaker6a744902012-07-30 16:05:20 -040074 .sops = &nfs4_sops,
75 .xattr = nfs4_xattr_handlers,
Bryan Schumakerab7017a2012-07-30 16:05:16 -040076};
77
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040078/*
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040079 * Get the superblock for the NFS4 root partition
80 */
81static struct dentry *
82nfs4_remote_mount(struct file_system_type *fs_type, int flags,
83 const char *dev_name, void *info)
84{
85 struct nfs_mount_info *mount_info = info;
86 struct nfs_server *server;
87 struct dentry *mntroot = ERR_PTR(-ENOMEM);
88
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040089 mount_info->set_security = nfs_set_sb_security;
90
91 /* Get a volume representation */
Bryan Schumaker1179acc2012-07-30 16:05:19 -040092 server = nfs4_create_server(mount_info, &nfs_v4);
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040093 if (IS_ERR(server)) {
94 mntroot = ERR_CAST(server);
95 goto out;
96 }
97
Bryan Schumakerab7017a2012-07-30 16:05:16 -040098 mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4);
Bryan Schumakerfbdefd62012-07-16 16:39:20 -040099
100out:
101 return mntroot;
102}
103
104static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
105 int flags, void *data, const char *hostname)
106{
107 struct vfsmount *root_mnt;
108 char *root_devname;
109 size_t len;
110
111 len = strlen(hostname) + 5;
112 root_devname = kmalloc(len, GFP_KERNEL);
113 if (root_devname == NULL)
114 return ERR_PTR(-ENOMEM);
115 /* Does hostname needs to be enclosed in brackets? */
116 if (strchr(hostname, ':'))
117 snprintf(root_devname, len, "[%s]:/", hostname);
118 else
119 snprintf(root_devname, len, "%s:/", hostname);
120 root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
121 kfree(root_devname);
122 return root_mnt;
123}
124
125struct nfs_referral_count {
126 struct list_head list;
127 const struct task_struct *task;
128 unsigned int referral_count;
129};
130
131static LIST_HEAD(nfs_referral_count_list);
132static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
133
134static struct nfs_referral_count *nfs_find_referral_count(void)
135{
136 struct nfs_referral_count *p;
137
138 list_for_each_entry(p, &nfs_referral_count_list, list) {
139 if (p->task == current)
140 return p;
141 }
142 return NULL;
143}
144
145#define NFS_MAX_NESTED_REFERRALS 2
146
147static int nfs_referral_loop_protect(void)
148{
149 struct nfs_referral_count *p, *new;
150 int ret = -ENOMEM;
151
152 new = kmalloc(sizeof(*new), GFP_KERNEL);
153 if (!new)
154 goto out;
155 new->task = current;
156 new->referral_count = 1;
157
158 ret = 0;
159 spin_lock(&nfs_referral_count_list_lock);
160 p = nfs_find_referral_count();
161 if (p != NULL) {
162 if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
163 ret = -ELOOP;
164 else
165 p->referral_count++;
166 } else {
167 list_add(&new->list, &nfs_referral_count_list);
168 new = NULL;
169 }
170 spin_unlock(&nfs_referral_count_list_lock);
171 kfree(new);
172out:
173 return ret;
174}
175
176static void nfs_referral_loop_unprotect(void)
177{
178 struct nfs_referral_count *p;
179
180 spin_lock(&nfs_referral_count_list_lock);
181 p = nfs_find_referral_count();
182 p->referral_count--;
183 if (p->referral_count == 0)
184 list_del(&p->list);
185 else
186 p = NULL;
187 spin_unlock(&nfs_referral_count_list_lock);
188 kfree(p);
189}
190
191static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
192 const char *export_path)
193{
194 struct dentry *dentry;
195 int err;
196
197 if (IS_ERR(root_mnt))
198 return ERR_CAST(root_mnt);
199
200 err = nfs_referral_loop_protect();
201 if (err) {
202 mntput(root_mnt);
203 return ERR_PTR(err);
204 }
205
206 dentry = mount_subtree(root_mnt, export_path);
207 nfs_referral_loop_unprotect();
208
209 return dentry;
210}
211
212struct dentry *nfs4_try_mount(int flags, const char *dev_name,
Bryan Schumakerff9099f22012-07-30 16:05:18 -0400213 struct nfs_mount_info *mount_info,
214 struct nfs_subversion *nfs_mod)
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400215{
216 char *export_path;
217 struct vfsmount *root_mnt;
218 struct dentry *res;
219 struct nfs_parsed_mount_data *data = mount_info->parsed;
220
221 dfprintk(MOUNT, "--> nfs4_try_mount()\n");
222
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400223 export_path = data->nfs_server.export_path;
224 data->nfs_server.export_path = "/";
225 root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
226 data->nfs_server.hostname);
227 data->nfs_server.export_path = export_path;
228
229 res = nfs_follow_remote_path(root_mnt, export_path);
230
231 dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
232 IS_ERR(res) ? PTR_ERR(res) : 0,
233 IS_ERR(res) ? " [error]" : "");
234 return res;
235}
236
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400237static struct dentry *
238nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
239 const char *dev_name, void *raw_data)
240{
241 struct nfs_mount_info mount_info = {
Bryan Schumaker6a744902012-07-30 16:05:20 -0400242 .fill_super = nfs_fill_super,
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400243 .set_security = nfs_clone_sb_security,
244 .cloned = raw_data,
245 };
246 struct nfs_server *server;
247 struct dentry *mntroot = ERR_PTR(-ENOMEM);
248
249 dprintk("--> nfs4_referral_get_sb()\n");
250
251 mount_info.mntfh = nfs_alloc_fhandle();
252 if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
253 goto out;
254
255 /* create a new volume representation */
256 server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
257 if (IS_ERR(server)) {
258 mntroot = ERR_CAST(server);
259 goto out;
260 }
261
Bryan Schumakerab7017a2012-07-30 16:05:16 -0400262 mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4);
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400263out:
264 nfs_free_fhandle(mount_info.mntfh);
265 return mntroot;
266}
267
268/*
269 * Create an NFS4 server record on referral traversal
270 */
271static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
272 int flags, const char *dev_name, void *raw_data)
273{
274 struct nfs_clone_mount *data = raw_data;
275 char *export_path;
276 struct vfsmount *root_mnt;
277 struct dentry *res;
278
279 dprintk("--> nfs4_referral_mount()\n");
280
281 export_path = data->mnt_path;
282 data->mnt_path = "/";
283
284 root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
285 flags, data, data->hostname);
286 data->mnt_path = export_path;
287
288 res = nfs_follow_remote_path(root_mnt, export_path);
289 dprintk("<-- nfs4_referral_mount() = %ld%s\n",
290 IS_ERR(res) ? PTR_ERR(res) : 0,
291 IS_ERR(res) ? " [error]" : "");
292 return res;
293}
294
295
Bryan Schumaker129d1972012-07-16 16:39:13 -0400296int __init init_nfs_v4(void)
297{
298 int err;
299
300 err = nfs_idmap_init();
301 if (err)
302 goto out;
303
Bryan Schumaker466bfe72012-07-16 16:39:14 -0400304 err = nfs4_register_sysctl();
305 if (err)
306 goto out1;
307
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400308 err = register_filesystem(&nfs4_fs_type);
309 if (err < 0)
310 goto out2;
311
Bryan Schumakerab7017a2012-07-30 16:05:16 -0400312 register_nfs_version(&nfs_v4);
Bryan Schumaker129d1972012-07-16 16:39:13 -0400313 return 0;
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400314out2:
315 nfs4_unregister_sysctl();
Bryan Schumaker466bfe72012-07-16 16:39:14 -0400316out1:
317 nfs_idmap_quit();
Bryan Schumaker129d1972012-07-16 16:39:13 -0400318out:
319 return err;
320}
321
Bryan Schumakerbb6e0712012-07-17 15:18:30 -0400322void exit_nfs_v4(void)
Bryan Schumaker129d1972012-07-16 16:39:13 -0400323{
Bryan Schumakerab7017a2012-07-30 16:05:16 -0400324 unregister_nfs_version(&nfs_v4);
Bryan Schumakerfbdefd62012-07-16 16:39:20 -0400325 unregister_filesystem(&nfs4_fs_type);
Bryan Schumaker466bfe72012-07-16 16:39:14 -0400326 nfs4_unregister_sysctl();
Bryan Schumaker129d1972012-07-16 16:39:13 -0400327 nfs_idmap_quit();
328}