xref: /openbmc/linux/fs/nfs/nfs4super.c (revision f2aedb713c284429987dc66c7aaf38decfc8da2a)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2129d1977SBryan Schumaker /*
3129d1977SBryan Schumaker  * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com>
4129d1977SBryan Schumaker  */
5129d1977SBryan Schumaker #include <linux/init.h>
6fbdefd64SBryan Schumaker #include <linux/module.h>
7*f2aedb71SDavid Howells #include <linux/mount.h>
8fbdefd64SBryan Schumaker #include <linux/nfs4_mount.h>
9466bfe7fSBryan Schumaker #include <linux/nfs_fs.h>
1019d87ca3SBryan Schumaker #include "delegation.h"
11fbdefd64SBryan Schumaker #include "internal.h"
12466bfe7fSBryan Schumaker #include "nfs4_fs.h"
1340c64c26SAnna Schumaker #include "nfs4idmap.h"
14c8d74d9bSTrond Myklebust #include "dns_resolve.h"
1519d87ca3SBryan Schumaker #include "pnfs.h"
16ab7017a3SBryan Schumaker #include "nfs.h"
17129d1977SBryan Schumaker 
18fbdefd64SBryan Schumaker #define NFSDBG_FACILITY		NFSDBG_VFS
19fbdefd64SBryan Schumaker 
2019d87ca3SBryan Schumaker static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
2119d87ca3SBryan Schumaker static void nfs4_evict_inode(struct inode *inode);
22fbdefd64SBryan Schumaker 
23fbdefd64SBryan Schumaker static const struct super_operations nfs4_sops = {
24fbdefd64SBryan Schumaker 	.alloc_inode	= nfs_alloc_inode,
25ca1a199eSAl Viro 	.free_inode	= nfs_free_inode,
26fbdefd64SBryan Schumaker 	.write_inode	= nfs4_write_inode,
27eed99357STrond Myklebust 	.drop_inode	= nfs_drop_inode,
28fbdefd64SBryan Schumaker 	.statfs		= nfs_statfs,
29fbdefd64SBryan Schumaker 	.evict_inode	= nfs4_evict_inode,
30fbdefd64SBryan Schumaker 	.umount_begin	= nfs_umount_begin,
31fbdefd64SBryan Schumaker 	.show_options	= nfs_show_options,
32fbdefd64SBryan Schumaker 	.show_devname	= nfs_show_devname,
33fbdefd64SBryan Schumaker 	.show_path	= nfs_show_path,
34fbdefd64SBryan Schumaker 	.show_stats	= nfs_show_stats,
35fbdefd64SBryan Schumaker };
36fbdefd64SBryan Schumaker 
37ab7017a3SBryan Schumaker struct nfs_subversion nfs_v4 = {
38ab7017a3SBryan Schumaker 	.owner = THIS_MODULE,
39ab7017a3SBryan Schumaker 	.nfs_fs   = &nfs4_fs_type,
40ab7017a3SBryan Schumaker 	.rpc_vers = &nfs_version4,
41ab7017a3SBryan Schumaker 	.rpc_ops  = &nfs_v4_clientops,
426a74490dSBryan Schumaker 	.sops     = &nfs4_sops,
436a74490dSBryan Schumaker 	.xattr    = nfs4_xattr_handlers,
44ab7017a3SBryan Schumaker };
45ab7017a3SBryan Schumaker 
4619d87ca3SBryan Schumaker static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
4719d87ca3SBryan Schumaker {
4819d87ca3SBryan Schumaker 	int ret = nfs_write_inode(inode, wbc);
4919d87ca3SBryan Schumaker 
5071244d9bSTrond Myklebust 	if (ret == 0)
5171244d9bSTrond Myklebust 		ret = pnfs_layoutcommit_inode(inode,
5271244d9bSTrond Myklebust 				wbc->sync_mode == WB_SYNC_ALL);
5319d87ca3SBryan Schumaker 	return ret;
5419d87ca3SBryan Schumaker }
5519d87ca3SBryan Schumaker 
5619d87ca3SBryan Schumaker /*
5719d87ca3SBryan Schumaker  * Clean out any remaining NFSv4 state that might be left over due
5819d87ca3SBryan Schumaker  * to open() calls that passed nfs_atomic_lookup, but failed to call
5919d87ca3SBryan Schumaker  * nfs_open().
6019d87ca3SBryan Schumaker  */
6119d87ca3SBryan Schumaker static void nfs4_evict_inode(struct inode *inode)
6219d87ca3SBryan Schumaker {
6391b0abe3SJohannes Weiner 	truncate_inode_pages_final(&inode->i_data);
6419d87ca3SBryan Schumaker 	clear_inode(inode);
65b47e0e47STrond Myklebust 	/* If we are holding a delegation, return and free it */
66b47e0e47STrond Myklebust 	nfs_inode_evict_delegation(inode);
67415320fcSTrond Myklebust 	/* Note that above delegreturn would trigger pnfs return-on-close */
68415320fcSTrond Myklebust 	pnfs_return_layout(inode);
69415320fcSTrond Myklebust 	pnfs_destroy_layout(NFS_I(inode));
7019d87ca3SBryan Schumaker 	/* First call standard NFS clear_inode() code */
7119d87ca3SBryan Schumaker 	nfs_clear_inode(inode);
7219d87ca3SBryan Schumaker }
7319d87ca3SBryan Schumaker 
74fbdefd64SBryan Schumaker struct nfs_referral_count {
75fbdefd64SBryan Schumaker 	struct list_head list;
76fbdefd64SBryan Schumaker 	const struct task_struct *task;
77fbdefd64SBryan Schumaker 	unsigned int referral_count;
78fbdefd64SBryan Schumaker };
79fbdefd64SBryan Schumaker 
80fbdefd64SBryan Schumaker static LIST_HEAD(nfs_referral_count_list);
81fbdefd64SBryan Schumaker static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
82fbdefd64SBryan Schumaker 
83fbdefd64SBryan Schumaker static struct nfs_referral_count *nfs_find_referral_count(void)
84fbdefd64SBryan Schumaker {
85fbdefd64SBryan Schumaker 	struct nfs_referral_count *p;
86fbdefd64SBryan Schumaker 
87fbdefd64SBryan Schumaker 	list_for_each_entry(p, &nfs_referral_count_list, list) {
88fbdefd64SBryan Schumaker 		if (p->task == current)
89fbdefd64SBryan Schumaker 			return p;
90fbdefd64SBryan Schumaker 	}
91fbdefd64SBryan Schumaker 	return NULL;
92fbdefd64SBryan Schumaker }
93fbdefd64SBryan Schumaker 
94fbdefd64SBryan Schumaker #define NFS_MAX_NESTED_REFERRALS 2
95fbdefd64SBryan Schumaker 
96fbdefd64SBryan Schumaker static int nfs_referral_loop_protect(void)
97fbdefd64SBryan Schumaker {
98fbdefd64SBryan Schumaker 	struct nfs_referral_count *p, *new;
99fbdefd64SBryan Schumaker 	int ret = -ENOMEM;
100fbdefd64SBryan Schumaker 
101fbdefd64SBryan Schumaker 	new = kmalloc(sizeof(*new), GFP_KERNEL);
102fbdefd64SBryan Schumaker 	if (!new)
103fbdefd64SBryan Schumaker 		goto out;
104fbdefd64SBryan Schumaker 	new->task = current;
105fbdefd64SBryan Schumaker 	new->referral_count = 1;
106fbdefd64SBryan Schumaker 
107fbdefd64SBryan Schumaker 	ret = 0;
108fbdefd64SBryan Schumaker 	spin_lock(&nfs_referral_count_list_lock);
109fbdefd64SBryan Schumaker 	p = nfs_find_referral_count();
110fbdefd64SBryan Schumaker 	if (p != NULL) {
111fbdefd64SBryan Schumaker 		if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
112fbdefd64SBryan Schumaker 			ret = -ELOOP;
113fbdefd64SBryan Schumaker 		else
114fbdefd64SBryan Schumaker 			p->referral_count++;
115fbdefd64SBryan Schumaker 	} else {
116fbdefd64SBryan Schumaker 		list_add(&new->list, &nfs_referral_count_list);
117fbdefd64SBryan Schumaker 		new = NULL;
118fbdefd64SBryan Schumaker 	}
119fbdefd64SBryan Schumaker 	spin_unlock(&nfs_referral_count_list_lock);
120fbdefd64SBryan Schumaker 	kfree(new);
121fbdefd64SBryan Schumaker out:
122fbdefd64SBryan Schumaker 	return ret;
123fbdefd64SBryan Schumaker }
124fbdefd64SBryan Schumaker 
125fbdefd64SBryan Schumaker static void nfs_referral_loop_unprotect(void)
126fbdefd64SBryan Schumaker {
127fbdefd64SBryan Schumaker 	struct nfs_referral_count *p;
128fbdefd64SBryan Schumaker 
129fbdefd64SBryan Schumaker 	spin_lock(&nfs_referral_count_list_lock);
130fbdefd64SBryan Schumaker 	p = nfs_find_referral_count();
131fbdefd64SBryan Schumaker 	p->referral_count--;
132fbdefd64SBryan Schumaker 	if (p->referral_count == 0)
133fbdefd64SBryan Schumaker 		list_del(&p->list);
134fbdefd64SBryan Schumaker 	else
135fbdefd64SBryan Schumaker 		p = NULL;
136fbdefd64SBryan Schumaker 	spin_unlock(&nfs_referral_count_list_lock);
137fbdefd64SBryan Schumaker 	kfree(p);
138fbdefd64SBryan Schumaker }
139fbdefd64SBryan Schumaker 
140*f2aedb71SDavid Howells static int do_nfs4_mount(struct nfs_server *server,
141*f2aedb71SDavid Howells 			 struct fs_context *fc,
1424e357761SAl Viro 			 const char *hostname,
143fbdefd64SBryan Schumaker 			 const char *export_path)
144fbdefd64SBryan Schumaker {
145*f2aedb71SDavid Howells 	struct nfs_fs_context *root_ctx;
146*f2aedb71SDavid Howells 	struct fs_context *root_fc;
1474e357761SAl Viro 	struct vfsmount *root_mnt;
148fbdefd64SBryan Schumaker 	struct dentry *dentry;
1494e357761SAl Viro 	size_t len;
150*f2aedb71SDavid Howells 	int ret;
151*f2aedb71SDavid Howells 
152*f2aedb71SDavid Howells 	struct fs_parameter param = {
153*f2aedb71SDavid Howells 		.key	= "source",
154*f2aedb71SDavid Howells 		.type	= fs_value_is_string,
155*f2aedb71SDavid Howells 		.dirfd	= -1,
156*f2aedb71SDavid Howells 	};
1574e357761SAl Viro 
1584e357761SAl Viro 	if (IS_ERR(server))
159*f2aedb71SDavid Howells 		return PTR_ERR(server);
160*f2aedb71SDavid Howells 
161*f2aedb71SDavid Howells 	root_fc = vfs_dup_fs_context(fc);
162*f2aedb71SDavid Howells 	if (IS_ERR(root_fc)) {
163*f2aedb71SDavid Howells 		nfs_free_server(server);
164*f2aedb71SDavid Howells 		return PTR_ERR(root_fc);
165*f2aedb71SDavid Howells 	}
166*f2aedb71SDavid Howells 	kfree(root_fc->source);
167*f2aedb71SDavid Howells 	root_fc->source = NULL;
168*f2aedb71SDavid Howells 
169*f2aedb71SDavid Howells 	root_ctx = nfs_fc2context(root_fc);
170*f2aedb71SDavid Howells 	root_ctx->internal = true;
171*f2aedb71SDavid Howells 	root_ctx->mount_info.server = server;
172*f2aedb71SDavid Howells 	/* We leave export_path unset as it's not used to find the root. */
1734e357761SAl Viro 
1744e357761SAl Viro 	len = strlen(hostname) + 5;
175*f2aedb71SDavid Howells 	param.string = kmalloc(len, GFP_KERNEL);
176*f2aedb71SDavid Howells 	if (param.string == NULL) {
177*f2aedb71SDavid Howells 		put_fs_context(root_fc);
178*f2aedb71SDavid Howells 		return -ENOMEM;
1794e357761SAl Viro 	}
1804e357761SAl Viro 
1814e357761SAl Viro 	/* Does hostname needs to be enclosed in brackets? */
1824e357761SAl Viro 	if (strchr(hostname, ':'))
183*f2aedb71SDavid Howells 		param.size = snprintf(param.string, len, "[%s]:/", hostname);
1844e357761SAl Viro 	else
185*f2aedb71SDavid Howells 		param.size = snprintf(param.string, len, "%s:/", hostname);
186*f2aedb71SDavid Howells 	ret = vfs_parse_fs_param(root_fc, &param);
187*f2aedb71SDavid Howells 	kfree(param.string);
188*f2aedb71SDavid Howells 	if (ret < 0) {
189*f2aedb71SDavid Howells 		put_fs_context(root_fc);
190*f2aedb71SDavid Howells 		return ret;
191*f2aedb71SDavid Howells 	}
192*f2aedb71SDavid Howells 	root_mnt = fc_mount(root_fc);
193*f2aedb71SDavid Howells 	put_fs_context(root_fc);
194fbdefd64SBryan Schumaker 
195fbdefd64SBryan Schumaker 	if (IS_ERR(root_mnt))
196*f2aedb71SDavid Howells 		return PTR_ERR(root_mnt);
197fbdefd64SBryan Schumaker 
198*f2aedb71SDavid Howells 	ret = nfs_referral_loop_protect();
199*f2aedb71SDavid Howells 	if (ret) {
200fbdefd64SBryan Schumaker 		mntput(root_mnt);
201*f2aedb71SDavid Howells 		return ret;
202fbdefd64SBryan Schumaker 	}
203fbdefd64SBryan Schumaker 
204fbdefd64SBryan Schumaker 	dentry = mount_subtree(root_mnt, export_path);
205fbdefd64SBryan Schumaker 	nfs_referral_loop_unprotect();
206fbdefd64SBryan Schumaker 
207*f2aedb71SDavid Howells 	if (IS_ERR(dentry))
208*f2aedb71SDavid Howells 		return PTR_ERR(dentry);
209*f2aedb71SDavid Howells 
210*f2aedb71SDavid Howells 	fc->root = dentry;
211*f2aedb71SDavid Howells 	return 0;
212fbdefd64SBryan Schumaker }
213fbdefd64SBryan Schumaker 
214*f2aedb71SDavid Howells int nfs4_try_get_tree(struct fs_context *fc)
215fbdefd64SBryan Schumaker {
216*f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
217*f2aedb71SDavid Howells 	int err;
218fbdefd64SBryan Schumaker 
219*f2aedb71SDavid Howells 	dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
220fbdefd64SBryan Schumaker 
221*f2aedb71SDavid Howells 	/* We create a mount for the server's root, walk to the requested
222*f2aedb71SDavid Howells 	 * location and then create another mount for that.
223*f2aedb71SDavid Howells 	 */
224*f2aedb71SDavid Howells 	err= do_nfs4_mount(nfs4_create_server(&ctx->mount_info),
225*f2aedb71SDavid Howells 			   fc, ctx->nfs_server.hostname,
2265eb005caSDavid Howells 			   ctx->nfs_server.export_path);
227*f2aedb71SDavid Howells 	if (err) {
228*f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
229*f2aedb71SDavid Howells 	} else {
230*f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
231*f2aedb71SDavid Howells 	}
232*f2aedb71SDavid Howells 	return err;
233fbdefd64SBryan Schumaker }
234fbdefd64SBryan Schumaker 
235fbdefd64SBryan Schumaker /*
236fbdefd64SBryan Schumaker  * Create an NFS4 server record on referral traversal
237fbdefd64SBryan Schumaker  */
238*f2aedb71SDavid Howells int nfs4_get_referral_tree(struct fs_context *fc)
239fbdefd64SBryan Schumaker {
240*f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
241*f2aedb71SDavid Howells 	int err;
242fbdefd64SBryan Schumaker 
243fbdefd64SBryan Schumaker 	dprintk("--> nfs4_referral_mount()\n");
244fbdefd64SBryan Schumaker 
245*f2aedb71SDavid Howells 	/* create a new volume representation */
246*f2aedb71SDavid Howells 	err = do_nfs4_mount(nfs4_create_referral_server(&ctx->clone_data, ctx->mount_info.mntfh),
247*f2aedb71SDavid Howells 			    fc, ctx->nfs_server.hostname,
248*f2aedb71SDavid Howells 			    ctx->nfs_server.export_path);
249*f2aedb71SDavid Howells 	if (err) {
250*f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
251*f2aedb71SDavid Howells 	} else {
252*f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
253*f2aedb71SDavid Howells 	}
254*f2aedb71SDavid Howells 	return err;
255fbdefd64SBryan Schumaker }
256fbdefd64SBryan Schumaker 
257fbdefd64SBryan Schumaker 
25889d77c8fSBryan Schumaker static int __init init_nfs_v4(void)
259129d1977SBryan Schumaker {
260129d1977SBryan Schumaker 	int err;
261129d1977SBryan Schumaker 
262c8d74d9bSTrond Myklebust 	err = nfs_dns_resolver_init();
263129d1977SBryan Schumaker 	if (err)
264129d1977SBryan Schumaker 		goto out;
265129d1977SBryan Schumaker 
266c8d74d9bSTrond Myklebust 	err = nfs_idmap_init();
267466bfe7fSBryan Schumaker 	if (err)
268466bfe7fSBryan Schumaker 		goto out1;
269466bfe7fSBryan Schumaker 
270c8d74d9bSTrond Myklebust 	err = nfs4_register_sysctl();
271c8d74d9bSTrond Myklebust 	if (err)
272c8d74d9bSTrond Myklebust 		goto out2;
273c8d74d9bSTrond Myklebust 
274ab7017a3SBryan Schumaker 	register_nfs_version(&nfs_v4);
275129d1977SBryan Schumaker 	return 0;
276c8d74d9bSTrond Myklebust out2:
277466bfe7fSBryan Schumaker 	nfs_idmap_quit();
278c8d74d9bSTrond Myklebust out1:
279c8d74d9bSTrond Myklebust 	nfs_dns_resolver_destroy();
280129d1977SBryan Schumaker out:
281129d1977SBryan Schumaker 	return err;
282129d1977SBryan Schumaker }
283129d1977SBryan Schumaker 
28489d77c8fSBryan Schumaker static void __exit exit_nfs_v4(void)
285129d1977SBryan Schumaker {
2865f01d953SPeng Tao 	/* Not called in the _init(), conditionally loaded */
2875f01d953SPeng Tao 	nfs4_pnfs_v3_ds_connect_unload();
2885f01d953SPeng Tao 
289ab7017a3SBryan Schumaker 	unregister_nfs_version(&nfs_v4);
290466bfe7fSBryan Schumaker 	nfs4_unregister_sysctl();
291129d1977SBryan Schumaker 	nfs_idmap_quit();
292c8d74d9bSTrond Myklebust 	nfs_dns_resolver_destroy();
293129d1977SBryan Schumaker }
29489d77c8fSBryan Schumaker 
29589d77c8fSBryan Schumaker MODULE_LICENSE("GPL");
29689d77c8fSBryan Schumaker 
29789d77c8fSBryan Schumaker module_init(init_nfs_v4);
29889d77c8fSBryan Schumaker module_exit(exit_nfs_v4);
299