xref: /linux/fs/nfs/nfs4super.c (revision 564eb714f5f09ac733c26860d5f0831f213fbdf1)
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 static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
77 {
78 	int ret = nfs_write_inode(inode, wbc);
79 
80 	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 static void nfs4_evict_inode(struct inode *inode)
100 {
101 	truncate_inode_pages(&inode->i_data, 0);
102 	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 	nfs_inode_return_delegation_noreclaim(inode);
107 	/* First call standard NFS clear_inode() code */
108 	nfs_clear_inode(inode);
109 }
110 
111 /*
112  * Get the superblock for the NFS4 root partition
113  */
114 static struct dentry *
115 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 	mount_info->set_security = nfs_set_sb_security;
123 
124 	/* Get a volume representation */
125 	server = nfs4_create_server(mount_info, &nfs_v4);
126 	if (IS_ERR(server)) {
127 		mntroot = ERR_CAST(server);
128 		goto out;
129 	}
130 
131 	mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, &nfs_v4);
132 
133 out:
134 	return mntroot;
135 }
136 
137 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 	len = strlen(hostname) + 5;
145 	root_devname = kmalloc(len, GFP_KERNEL);
146 	if (root_devname == NULL)
147 		return ERR_PTR(-ENOMEM);
148 	/* Does hostname needs to be enclosed in brackets? */
149 	if (strchr(hostname, ':'))
150 		snprintf(root_devname, len, "[%s]:/", hostname);
151 	else
152 		snprintf(root_devname, len, "%s:/", hostname);
153 	root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
154 	kfree(root_devname);
155 	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 	list_for_each_entry(p, &nfs_referral_count_list, list) {
172 		if (p->task == current)
173 			return p;
174 	}
175 	return NULL;
176 }
177 
178 #define NFS_MAX_NESTED_REFERRALS 2
179 
180 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 	if (!new)
187 		goto out;
188 	new->task = current;
189 	new->referral_count = 1;
190 
191 	ret = 0;
192 	spin_lock(&nfs_referral_count_list_lock);
193 	p = nfs_find_referral_count();
194 	if (p != NULL) {
195 		if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
196 			ret = -ELOOP;
197 		else
198 			p->referral_count++;
199 	} else {
200 		list_add(&new->list, &nfs_referral_count_list);
201 		new = NULL;
202 	}
203 	spin_unlock(&nfs_referral_count_list_lock);
204 	kfree(new);
205 out:
206 	return ret;
207 }
208 
209 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 	p->referral_count--;
216 	if (p->referral_count == 0)
217 		list_del(&p->list);
218 	else
219 		p = NULL;
220 	spin_unlock(&nfs_referral_count_list_lock);
221 	kfree(p);
222 }
223 
224 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 	if (IS_ERR(root_mnt))
231 		return ERR_CAST(root_mnt);
232 
233 	err = nfs_referral_loop_protect();
234 	if (err) {
235 		mntput(root_mnt);
236 		return ERR_PTR(err);
237 	}
238 
239 	dentry = mount_subtree(root_mnt, export_path);
240 	nfs_referral_loop_unprotect();
241 
242 	return dentry;
243 }
244 
245 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 	struct nfs_parsed_mount_data *data = mount_info->parsed;
253 
254 	dfprintk(MOUNT, "--> nfs4_try_mount()\n");
255 
256 	export_path = data->nfs_server.export_path;
257 	data->nfs_server.export_path = "/";
258 	root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
259 			data->nfs_server.hostname);
260 	data->nfs_server.export_path = export_path;
261 
262 	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 	return res;
268 }
269 
270 static struct dentry *
271 nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
272 			   const char *dev_name, void *raw_data)
273 {
274 	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 	mount_info.mntfh = nfs_alloc_fhandle();
285 	if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
286 		goto out;
287 
288 	/* create a new volume representation */
289 	server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
290 	if (IS_ERR(server)) {
291 		mntroot = ERR_CAST(server);
292 		goto out;
293 	}
294 
295 	mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, &nfs_v4);
296 out:
297 	nfs_free_fhandle(mount_info.mntfh);
298 	return mntroot;
299 }
300 
301 /*
302  * Create an NFS4 server record on referral traversal
303  */
304 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 	export_path = data->mnt_path;
315 	data->mnt_path = "/";
316 
317 	root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
318 			flags, data, data->hostname);
319 	data->mnt_path = export_path;
320 
321 	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 	return res;
326 }
327 
328 
329 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 	err = nfs_idmap_init();
338 	if (err)
339 		goto out1;
340 
341 	err = nfs4_register_sysctl();
342 	if (err)
343 		goto out2;
344 
345 	register_nfs_version(&nfs_v4);
346 	return 0;
347 out2:
348 	nfs_idmap_quit();
349 out1:
350 	nfs_dns_resolver_destroy();
351 out:
352 	return err;
353 }
354 
355 static void __exit exit_nfs_v4(void)
356 {
357 	unregister_nfs_version(&nfs_v4);
358 	nfs4_unregister_sysctl();
359 	nfs_idmap_quit();
360 	nfs_dns_resolver_destroy();
361 }
362 
363 MODULE_LICENSE("GPL");
364 
365 module_init(init_nfs_v4);
366 module_exit(exit_nfs_v4);
367