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