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 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) {
81 : : int status;
82 : : bool sync = true;
83 : :
84 : : if (wbc->sync_mode == WB_SYNC_NONE)
85 : : sync = false;
86 : :
87 : : status = pnfs_layoutcommit_inode(inode, sync);
88 : : if (status < 0)
89 : : return status;
90 : : }
91 : : return ret;
92 : : }
93 : :
94 : : /*
95 : : * Clean out any remaining NFSv4 state that might be left over due
96 : : * to open() calls that passed nfs_atomic_lookup, but failed to call
97 : : * nfs_open().
98 : : */
99 : 0 : static void nfs4_evict_inode(struct inode *inode)
100 : : {
101 : 0 : truncate_inode_pages(&inode->i_data, 0);
102 : 0 : clear_inode(inode);
103 : : pnfs_return_layout(inode);
104 : : pnfs_destroy_layout(NFS_I(inode));
105 : : /* If we are holding a delegation, return it! */
106 : 0 : nfs_inode_return_delegation_noreclaim(inode);
107 : : /* First call standard NFS clear_inode() code */
108 : 0 : nfs_clear_inode(inode);
109 : 0 : }
110 : :
111 : : /*
112 : : * Get the superblock for the NFS4 root partition
113 : : */
114 : : static struct dentry *
115 : 0 : nfs4_remote_mount(struct file_system_type *fs_type, int flags,
116 : : const char *dev_name, void *info)
117 : : {
118 : : struct nfs_mount_info *mount_info = info;
119 : : struct nfs_server *server;
120 : : struct dentry *mntroot = ERR_PTR(-ENOMEM);
121 : :
122 : 0 : mount_info->set_security = nfs_set_sb_security;
123 : :
124 : : /* Get a volume representation */
125 : 0 : server = nfs4_create_server(mount_info, &nfs_v4);
126 [ # # ]: 0 : if (IS_ERR(server)) {
127 : : mntroot = ERR_CAST(server);
128 : : goto out;
129 : : }
130 : :
131 : 0 : mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4);
132 : :
133 : : out:
134 : 0 : return mntroot;
135 : : }
136 : :
137 : 0 : static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
138 : : int flags, void *data, const char *hostname)
139 : : {
140 : : struct vfsmount *root_mnt;
141 : : char *root_devname;
142 : : size_t len;
143 : :
144 : 0 : len = strlen(hostname) + 5;
145 : : root_devname = kmalloc(len, GFP_KERNEL);
146 [ # # ]: 0 : if (root_devname == NULL)
147 : : return ERR_PTR(-ENOMEM);
148 : : /* Does hostname needs to be enclosed in brackets? */
149 [ # # ]: 0 : if (strchr(hostname, ':'))
150 : 0 : snprintf(root_devname, len, "[%s]:/", hostname);
151 : : else
152 : 0 : snprintf(root_devname, len, "%s:/", hostname);
153 : 0 : root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
154 : 0 : kfree(root_devname);
155 : 0 : return root_mnt;
156 : : }
157 : :
158 : : struct nfs_referral_count {
159 : : struct list_head list;
160 : : const struct task_struct *task;
161 : : unsigned int referral_count;
162 : : };
163 : :
164 : : static LIST_HEAD(nfs_referral_count_list);
165 : : static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
166 : :
167 : : static struct nfs_referral_count *nfs_find_referral_count(void)
168 : : {
169 : : struct nfs_referral_count *p;
170 : :
171 [ # # ][ # # ]: 0 : list_for_each_entry(p, &nfs_referral_count_list, list) {
172 [ # # ][ # # ]: 0 : if (p->task == current)
173 : : return p;
174 : : }
175 : : return NULL;
176 : : }
177 : :
178 : : #define NFS_MAX_NESTED_REFERRALS 2
179 : :
180 : 0 : static int nfs_referral_loop_protect(void)
181 : : {
182 : : struct nfs_referral_count *p, *new;
183 : : int ret = -ENOMEM;
184 : :
185 : : new = kmalloc(sizeof(*new), GFP_KERNEL);
186 [ # # ]: 0 : if (!new)
187 : : goto out;
188 : 0 : new->task = current;
189 : 0 : new->referral_count = 1;
190 : :
191 : : ret = 0;
192 : : spin_lock(&nfs_referral_count_list_lock);
193 : : p = nfs_find_referral_count();
194 [ # # ]: 0 : if (p != NULL) {
195 [ # # ]: 0 : if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
196 : : ret = -ELOOP;
197 : : else
198 : 0 : p->referral_count++;
199 : : } else {
200 : 0 : list_add(&new->list, &nfs_referral_count_list);
201 : : new = NULL;
202 : : }
203 : : spin_unlock(&nfs_referral_count_list_lock);
204 : 0 : kfree(new);
205 : : out:
206 : 0 : return ret;
207 : : }
208 : :
209 : 0 : static void nfs_referral_loop_unprotect(void)
210 : : {
211 : : struct nfs_referral_count *p;
212 : :
213 : : spin_lock(&nfs_referral_count_list_lock);
214 : : p = nfs_find_referral_count();
215 : 0 : p->referral_count--;
216 [ # # ]: 0 : if (p->referral_count == 0)
217 : : list_del(&p->list);
218 : : else
219 : : p = NULL;
220 : : spin_unlock(&nfs_referral_count_list_lock);
221 : 0 : kfree(p);
222 : 0 : }
223 : :
224 : 0 : static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
225 : : const char *export_path)
226 : : {
227 : : struct dentry *dentry;
228 : : int err;
229 : :
230 [ # # ]: 0 : if (IS_ERR(root_mnt))
231 : : return ERR_CAST(root_mnt);
232 : :
233 : 0 : err = nfs_referral_loop_protect();
234 [ # # ]: 0 : if (err) {
235 : 0 : mntput(root_mnt);
236 : 0 : return ERR_PTR(err);
237 : : }
238 : :
239 : 0 : dentry = mount_subtree(root_mnt, export_path);
240 : 0 : nfs_referral_loop_unprotect();
241 : :
242 : 0 : return dentry;
243 : : }
244 : :
245 : 0 : struct dentry *nfs4_try_mount(int flags, const char *dev_name,
246 : : struct nfs_mount_info *mount_info,
247 : : struct nfs_subversion *nfs_mod)
248 : : {
249 : : char *export_path;
250 : : struct vfsmount *root_mnt;
251 : : struct dentry *res;
252 : 0 : struct nfs_parsed_mount_data *data = mount_info->parsed;
253 : :
254 : : dfprintk(MOUNT, "--> nfs4_try_mount()\n");
255 : :
256 : 0 : export_path = data->nfs_server.export_path;
257 : 0 : data->nfs_server.export_path = "/";
258 : 0 : root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
259 : 0 : data->nfs_server.hostname);
260 : 0 : data->nfs_server.export_path = export_path;
261 : :
262 : 0 : res = nfs_follow_remote_path(root_mnt, export_path);
263 : :
264 : : dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
265 : : PTR_ERR_OR_ZERO(res),
266 : : IS_ERR(res) ? " [error]" : "");
267 : 0 : return res;
268 : : }
269 : :
270 : : static struct dentry *
271 : 0 : nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
272 : : const char *dev_name, void *raw_data)
273 : : {
274 : 0 : struct nfs_mount_info mount_info = {
275 : : .fill_super = nfs_fill_super,
276 : : .set_security = nfs_clone_sb_security,
277 : : .cloned = raw_data,
278 : : };
279 : : struct nfs_server *server;
280 : : struct dentry *mntroot = ERR_PTR(-ENOMEM);
281 : :
282 : : dprintk("--> nfs4_referral_get_sb()\n");
283 : :
284 : 0 : mount_info.mntfh = nfs_alloc_fhandle();
285 [ # # ][ # # ]: 0 : if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
286 : : goto out;
287 : :
288 : : /* create a new volume representation */
289 : 0 : server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
290 [ # # ]: 0 : if (IS_ERR(server)) {
291 : : mntroot = ERR_CAST(server);
292 : : goto out;
293 : : }
294 : :
295 : 0 : mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4);
296 : : out:
297 : 0 : nfs_free_fhandle(mount_info.mntfh);
298 : 0 : return mntroot;
299 : : }
300 : :
301 : : /*
302 : : * Create an NFS4 server record on referral traversal
303 : : */
304 : 0 : static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
305 : : int flags, const char *dev_name, void *raw_data)
306 : : {
307 : : struct nfs_clone_mount *data = raw_data;
308 : : char *export_path;
309 : : struct vfsmount *root_mnt;
310 : : struct dentry *res;
311 : :
312 : : dprintk("--> nfs4_referral_mount()\n");
313 : :
314 : 0 : export_path = data->mnt_path;
315 : 0 : data->mnt_path = "/";
316 : :
317 : 0 : root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
318 : 0 : flags, data, data->hostname);
319 : 0 : data->mnt_path = export_path;
320 : :
321 : 0 : res = nfs_follow_remote_path(root_mnt, export_path);
322 : : dprintk("<-- nfs4_referral_mount() = %d%s\n",
323 : : PTR_ERR_OR_ZERO(res),
324 : : IS_ERR(res) ? " [error]" : "");
325 : 0 : return res;
326 : : }
327 : :
328 : :
329 : 0 : static int __init init_nfs_v4(void)
330 : : {
331 : : int err;
332 : :
333 : : err = nfs_dns_resolver_init();
334 : : if (err)
335 : : goto out;
336 : :
337 : 0 : err = nfs_idmap_init();
338 [ # # ]: 0 : if (err)
339 : : goto out1;
340 : :
341 : 0 : err = nfs4_register_sysctl();
342 [ # # ]: 0 : if (err)
343 : : goto out2;
344 : :
345 : 0 : register_nfs_version(&nfs_v4);
346 : 0 : return 0;
347 : : out2:
348 : 0 : nfs_idmap_quit();
349 : : out1:
350 : : nfs_dns_resolver_destroy();
351 : : out:
352 : 0 : return err;
353 : : }
354 : :
355 : 0 : static void __exit exit_nfs_v4(void)
356 : : {
357 : 0 : unregister_nfs_version(&nfs_v4);
358 : 0 : nfs4_unregister_sysctl();
359 : 0 : nfs_idmap_quit();
360 : : nfs_dns_resolver_destroy();
361 : 0 : }
362 : :
363 : : MODULE_LICENSE("GPL");
364 : :
365 : : module_init(init_nfs_v4);
366 : : module_exit(exit_nfs_v4);
|