xref: /openbmc/linux/fs/nfs/nfs4super.c (revision f8bade6c9a6213c2c5ba6e5bf32415ecab6e41e5)
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>
7f2aedb71SDavid Howells #include <linux/mount.h>
8fbdefd64SBryan Schumaker #include <linux/nfs4_mount.h>
9466bfe7fSBryan Schumaker #include <linux/nfs_fs.h>
100cfcd405SDai Ngo #include <linux/nfs_ssc.h>
1119d87ca3SBryan Schumaker #include "delegation.h"
12fbdefd64SBryan Schumaker #include "internal.h"
13466bfe7fSBryan Schumaker #include "nfs4_fs.h"
1440c64c26SAnna Schumaker #include "nfs4idmap.h"
15c8d74d9bSTrond Myklebust #include "dns_resolve.h"
1619d87ca3SBryan Schumaker #include "pnfs.h"
17ab7017a3SBryan Schumaker #include "nfs.h"
18129d1977SBryan Schumaker 
19fbdefd64SBryan Schumaker #define NFSDBG_FACILITY		NFSDBG_VFS
20fbdefd64SBryan Schumaker 
2119d87ca3SBryan Schumaker static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
2219d87ca3SBryan Schumaker static void nfs4_evict_inode(struct inode *inode);
23fbdefd64SBryan Schumaker 
24fbdefd64SBryan Schumaker static const struct super_operations nfs4_sops = {
25fbdefd64SBryan Schumaker 	.alloc_inode	= nfs_alloc_inode,
26ca1a199eSAl Viro 	.free_inode	= nfs_free_inode,
27fbdefd64SBryan Schumaker 	.write_inode	= nfs4_write_inode,
28eed99357STrond Myklebust 	.drop_inode	= nfs_drop_inode,
29fbdefd64SBryan Schumaker 	.statfs		= nfs_statfs,
30fbdefd64SBryan Schumaker 	.evict_inode	= nfs4_evict_inode,
31fbdefd64SBryan Schumaker 	.umount_begin	= nfs_umount_begin,
32fbdefd64SBryan Schumaker 	.show_options	= nfs_show_options,
33fbdefd64SBryan Schumaker 	.show_devname	= nfs_show_devname,
34fbdefd64SBryan Schumaker 	.show_path	= nfs_show_path,
35fbdefd64SBryan Schumaker 	.show_stats	= nfs_show_stats,
36fbdefd64SBryan Schumaker };
37fbdefd64SBryan Schumaker 
38ab7017a3SBryan Schumaker struct nfs_subversion nfs_v4 = {
39ab7017a3SBryan Schumaker 	.owner		= THIS_MODULE,
40ab7017a3SBryan Schumaker 	.nfs_fs		= &nfs4_fs_type,
41ab7017a3SBryan Schumaker 	.rpc_vers	= &nfs_version4,
42ab7017a3SBryan Schumaker 	.rpc_ops	= &nfs_v4_clientops,
436a74490dSBryan Schumaker 	.sops		= &nfs4_sops,
446a74490dSBryan Schumaker 	.xattr		= nfs4_xattr_handlers,
45ab7017a3SBryan Schumaker };
46ab7017a3SBryan Schumaker 
nfs4_write_inode(struct inode * inode,struct writeback_control * wbc)4719d87ca3SBryan Schumaker static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
4819d87ca3SBryan Schumaker {
4919d87ca3SBryan Schumaker 	int ret = nfs_write_inode(inode, wbc);
5019d87ca3SBryan Schumaker 
5171244d9bSTrond Myklebust 	if (ret == 0)
5271244d9bSTrond Myklebust 		ret = pnfs_layoutcommit_inode(inode,
5371244d9bSTrond Myklebust 				wbc->sync_mode == WB_SYNC_ALL);
5419d87ca3SBryan Schumaker 	return ret;
5519d87ca3SBryan Schumaker }
5619d87ca3SBryan Schumaker 
5719d87ca3SBryan Schumaker /*
5819d87ca3SBryan Schumaker  * Clean out any remaining NFSv4 state that might be left over due
5919d87ca3SBryan Schumaker  * to open() calls that passed nfs_atomic_lookup, but failed to call
6019d87ca3SBryan Schumaker  * nfs_open().
6119d87ca3SBryan Schumaker  */
nfs4_evict_inode(struct inode * inode)6219d87ca3SBryan Schumaker static void nfs4_evict_inode(struct inode *inode)
6319d87ca3SBryan Schumaker {
6491b0abe3SJohannes Weiner 	truncate_inode_pages_final(&inode->i_data);
6519d87ca3SBryan Schumaker 	clear_inode(inode);
66b47e0e47STrond Myklebust 	/* If we are holding a delegation, return and free it */
67b47e0e47STrond Myklebust 	nfs_inode_evict_delegation(inode);
68415320fcSTrond Myklebust 	/* Note that above delegreturn would trigger pnfs return-on-close */
69415320fcSTrond Myklebust 	pnfs_return_layout(inode);
70b6d49ecdSTrond Myklebust 	pnfs_destroy_layout_final(NFS_I(inode));
7119d87ca3SBryan Schumaker 	/* First call standard NFS clear_inode() code */
7219d87ca3SBryan Schumaker 	nfs_clear_inode(inode);
7395ad37f9SFrank van der Linden 	nfs4_xattr_cache_zap(inode);
7419d87ca3SBryan Schumaker }
7519d87ca3SBryan Schumaker 
76fbdefd64SBryan Schumaker struct nfs_referral_count {
77fbdefd64SBryan Schumaker 	struct list_head list;
78fbdefd64SBryan Schumaker 	const struct task_struct *task;
79fbdefd64SBryan Schumaker 	unsigned int referral_count;
80fbdefd64SBryan Schumaker };
81fbdefd64SBryan Schumaker 
82fbdefd64SBryan Schumaker static LIST_HEAD(nfs_referral_count_list);
83fbdefd64SBryan Schumaker static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
84fbdefd64SBryan Schumaker 
nfs_find_referral_count(void)85fbdefd64SBryan Schumaker static struct nfs_referral_count *nfs_find_referral_count(void)
86fbdefd64SBryan Schumaker {
87fbdefd64SBryan Schumaker 	struct nfs_referral_count *p;
88fbdefd64SBryan Schumaker 
89fbdefd64SBryan Schumaker 	list_for_each_entry(p, &nfs_referral_count_list, list) {
90fbdefd64SBryan Schumaker 		if (p->task == current)
91fbdefd64SBryan Schumaker 			return p;
92fbdefd64SBryan Schumaker 	}
93fbdefd64SBryan Schumaker 	return NULL;
94fbdefd64SBryan Schumaker }
95fbdefd64SBryan Schumaker 
96fbdefd64SBryan Schumaker #define NFS_MAX_NESTED_REFERRALS 2
97fbdefd64SBryan Schumaker 
nfs_referral_loop_protect(void)98fbdefd64SBryan Schumaker static int nfs_referral_loop_protect(void)
99fbdefd64SBryan Schumaker {
100fbdefd64SBryan Schumaker 	struct nfs_referral_count *p, *new;
101fbdefd64SBryan Schumaker 	int ret = -ENOMEM;
102fbdefd64SBryan Schumaker 
103fbdefd64SBryan Schumaker 	new = kmalloc(sizeof(*new), GFP_KERNEL);
104fbdefd64SBryan Schumaker 	if (!new)
105fbdefd64SBryan Schumaker 		goto out;
106fbdefd64SBryan Schumaker 	new->task = current;
107fbdefd64SBryan Schumaker 	new->referral_count = 1;
108fbdefd64SBryan Schumaker 
109fbdefd64SBryan Schumaker 	ret = 0;
110fbdefd64SBryan Schumaker 	spin_lock(&nfs_referral_count_list_lock);
111fbdefd64SBryan Schumaker 	p = nfs_find_referral_count();
112fbdefd64SBryan Schumaker 	if (p != NULL) {
113fbdefd64SBryan Schumaker 		if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
114fbdefd64SBryan Schumaker 			ret = -ELOOP;
115fbdefd64SBryan Schumaker 		else
116fbdefd64SBryan Schumaker 			p->referral_count++;
117fbdefd64SBryan Schumaker 	} else {
118fbdefd64SBryan Schumaker 		list_add(&new->list, &nfs_referral_count_list);
119fbdefd64SBryan Schumaker 		new = NULL;
120fbdefd64SBryan Schumaker 	}
121fbdefd64SBryan Schumaker 	spin_unlock(&nfs_referral_count_list_lock);
122fbdefd64SBryan Schumaker 	kfree(new);
123fbdefd64SBryan Schumaker out:
124fbdefd64SBryan Schumaker 	return ret;
125fbdefd64SBryan Schumaker }
126fbdefd64SBryan Schumaker 
nfs_referral_loop_unprotect(void)127fbdefd64SBryan Schumaker static void nfs_referral_loop_unprotect(void)
128fbdefd64SBryan Schumaker {
129fbdefd64SBryan Schumaker 	struct nfs_referral_count *p;
130fbdefd64SBryan Schumaker 
131fbdefd64SBryan Schumaker 	spin_lock(&nfs_referral_count_list_lock);
132fbdefd64SBryan Schumaker 	p = nfs_find_referral_count();
133fbdefd64SBryan Schumaker 	p->referral_count--;
134fbdefd64SBryan Schumaker 	if (p->referral_count == 0)
135fbdefd64SBryan Schumaker 		list_del(&p->list);
136fbdefd64SBryan Schumaker 	else
137fbdefd64SBryan Schumaker 		p = NULL;
138fbdefd64SBryan Schumaker 	spin_unlock(&nfs_referral_count_list_lock);
139fbdefd64SBryan Schumaker 	kfree(p);
140fbdefd64SBryan Schumaker }
141fbdefd64SBryan Schumaker 
do_nfs4_mount(struct nfs_server * server,struct fs_context * fc,const char * hostname,const char * export_path)142f2aedb71SDavid Howells static int do_nfs4_mount(struct nfs_server *server,
143f2aedb71SDavid Howells 			 struct fs_context *fc,
1444e357761SAl Viro 			 const char *hostname,
145fbdefd64SBryan Schumaker 			 const char *export_path)
146fbdefd64SBryan Schumaker {
147f2aedb71SDavid Howells 	struct nfs_fs_context *root_ctx;
148f2aedb71SDavid Howells 	struct fs_context *root_fc;
1494e357761SAl Viro 	struct vfsmount *root_mnt;
150fbdefd64SBryan Schumaker 	struct dentry *dentry;
1514e357761SAl Viro 	size_t len;
152f2aedb71SDavid Howells 	int ret;
153f2aedb71SDavid Howells 
154f2aedb71SDavid Howells 	struct fs_parameter param = {
155f2aedb71SDavid Howells 		.key	= "source",
156f2aedb71SDavid Howells 		.type	= fs_value_is_string,
157f2aedb71SDavid Howells 		.dirfd	= -1,
158f2aedb71SDavid Howells 	};
1594e357761SAl Viro 
1604e357761SAl Viro 	if (IS_ERR(server))
161f2aedb71SDavid Howells 		return PTR_ERR(server);
162f2aedb71SDavid Howells 
163f2aedb71SDavid Howells 	root_fc = vfs_dup_fs_context(fc);
164f2aedb71SDavid Howells 	if (IS_ERR(root_fc)) {
165f2aedb71SDavid Howells 		nfs_free_server(server);
166f2aedb71SDavid Howells 		return PTR_ERR(root_fc);
167f2aedb71SDavid Howells 	}
168f2aedb71SDavid Howells 	kfree(root_fc->source);
169f2aedb71SDavid Howells 	root_fc->source = NULL;
170f2aedb71SDavid Howells 
171f2aedb71SDavid Howells 	root_ctx = nfs_fc2context(root_fc);
172f2aedb71SDavid Howells 	root_ctx->internal = true;
17362a55d08SScott Mayhew 	root_ctx->server = server;
174f2aedb71SDavid Howells 	/* We leave export_path unset as it's not used to find the root. */
1754e357761SAl Viro 
1764e357761SAl Viro 	len = strlen(hostname) + 5;
177f2aedb71SDavid Howells 	param.string = kmalloc(len, GFP_KERNEL);
178f2aedb71SDavid Howells 	if (param.string == NULL) {
179f2aedb71SDavid Howells 		put_fs_context(root_fc);
180f2aedb71SDavid Howells 		return -ENOMEM;
1814e357761SAl Viro 	}
1824e357761SAl Viro 
1834e357761SAl Viro 	/* Does hostname needs to be enclosed in brackets? */
1844e357761SAl Viro 	if (strchr(hostname, ':'))
185f2aedb71SDavid Howells 		param.size = snprintf(param.string, len, "[%s]:/", hostname);
1864e357761SAl Viro 	else
187f2aedb71SDavid Howells 		param.size = snprintf(param.string, len, "%s:/", hostname);
188f2aedb71SDavid Howells 	ret = vfs_parse_fs_param(root_fc, &param);
189f2aedb71SDavid Howells 	kfree(param.string);
190f2aedb71SDavid Howells 	if (ret < 0) {
191f2aedb71SDavid Howells 		put_fs_context(root_fc);
192f2aedb71SDavid Howells 		return ret;
193f2aedb71SDavid Howells 	}
194f2aedb71SDavid Howells 	root_mnt = fc_mount(root_fc);
195f2aedb71SDavid Howells 	put_fs_context(root_fc);
196fbdefd64SBryan Schumaker 
197fbdefd64SBryan Schumaker 	if (IS_ERR(root_mnt))
198f2aedb71SDavid Howells 		return PTR_ERR(root_mnt);
199fbdefd64SBryan Schumaker 
200f2aedb71SDavid Howells 	ret = nfs_referral_loop_protect();
201f2aedb71SDavid Howells 	if (ret) {
202fbdefd64SBryan Schumaker 		mntput(root_mnt);
203f2aedb71SDavid Howells 		return ret;
204fbdefd64SBryan Schumaker 	}
205fbdefd64SBryan Schumaker 
206fbdefd64SBryan Schumaker 	dentry = mount_subtree(root_mnt, export_path);
207fbdefd64SBryan Schumaker 	nfs_referral_loop_unprotect();
208fbdefd64SBryan Schumaker 
209f2aedb71SDavid Howells 	if (IS_ERR(dentry))
210f2aedb71SDavid Howells 		return PTR_ERR(dentry);
211f2aedb71SDavid Howells 
212f2aedb71SDavid Howells 	fc->root = dentry;
213f2aedb71SDavid Howells 	return 0;
214fbdefd64SBryan Schumaker }
215fbdefd64SBryan Schumaker 
nfs4_try_get_tree(struct fs_context * fc)216f2aedb71SDavid Howells int nfs4_try_get_tree(struct fs_context *fc)
217fbdefd64SBryan Schumaker {
218f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
219f2aedb71SDavid Howells 	int err;
220fbdefd64SBryan Schumaker 
221f2aedb71SDavid Howells 	dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
222fbdefd64SBryan Schumaker 
223f2aedb71SDavid Howells 	/* We create a mount for the server's root, walk to the requested
224f2aedb71SDavid Howells 	 * location and then create another mount for that.
225f2aedb71SDavid Howells 	 */
22662a55d08SScott Mayhew 	err= do_nfs4_mount(nfs4_create_server(fc),
227f2aedb71SDavid Howells 			   fc, ctx->nfs_server.hostname,
2285eb005caSDavid Howells 			   ctx->nfs_server.export_path);
229f2aedb71SDavid Howells 	if (err) {
230*c98e9daaSScott Mayhew 		nfs_ferrorf(fc, MOUNT, "NFS4: Couldn't follow remote path");
231f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
232f2aedb71SDavid Howells 	} else {
233f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
234f2aedb71SDavid Howells 	}
235f2aedb71SDavid Howells 	return err;
236fbdefd64SBryan Schumaker }
237fbdefd64SBryan Schumaker 
238fbdefd64SBryan Schumaker /*
239fbdefd64SBryan Schumaker  * Create an NFS4 server record on referral traversal
240fbdefd64SBryan Schumaker  */
nfs4_get_referral_tree(struct fs_context * fc)241f2aedb71SDavid Howells int nfs4_get_referral_tree(struct fs_context *fc)
242fbdefd64SBryan Schumaker {
243f2aedb71SDavid Howells 	struct nfs_fs_context *ctx = nfs_fc2context(fc);
244f2aedb71SDavid Howells 	int err;
245fbdefd64SBryan Schumaker 
246fbdefd64SBryan Schumaker 	dprintk("--> nfs4_referral_mount()\n");
247fbdefd64SBryan Schumaker 
248f2aedb71SDavid Howells 	/* create a new volume representation */
24962a55d08SScott Mayhew 	err = do_nfs4_mount(nfs4_create_referral_server(fc),
250f2aedb71SDavid Howells 			    fc, ctx->nfs_server.hostname,
251f2aedb71SDavid Howells 			    ctx->nfs_server.export_path);
252f2aedb71SDavid Howells 	if (err) {
253*c98e9daaSScott Mayhew 		nfs_ferrorf(fc, MOUNT, "NFS4: Couldn't follow remote path");
254f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
255f2aedb71SDavid Howells 	} else {
256f2aedb71SDavid Howells 		dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
257f2aedb71SDavid Howells 	}
258f2aedb71SDavid Howells 	return err;
259fbdefd64SBryan Schumaker }
260fbdefd64SBryan Schumaker 
init_nfs_v4(void)26189d77c8fSBryan Schumaker static int __init init_nfs_v4(void)
262129d1977SBryan Schumaker {
263129d1977SBryan Schumaker 	int err;
264129d1977SBryan Schumaker 
265c8d74d9bSTrond Myklebust 	err = nfs_dns_resolver_init();
266129d1977SBryan Schumaker 	if (err)
267129d1977SBryan Schumaker 		goto out;
268129d1977SBryan Schumaker 
269c8d74d9bSTrond Myklebust 	err = nfs_idmap_init();
270466bfe7fSBryan Schumaker 	if (err)
271466bfe7fSBryan Schumaker 		goto out1;
272466bfe7fSBryan Schumaker 
27395ad37f9SFrank van der Linden #ifdef CONFIG_NFS_V4_2
27495ad37f9SFrank van der Linden 	err = nfs4_xattr_cache_init();
27595ad37f9SFrank van der Linden 	if (err)
27695ad37f9SFrank van der Linden 		goto out2;
27795ad37f9SFrank van der Linden #endif
27895ad37f9SFrank van der Linden 
279c8d74d9bSTrond Myklebust 	err = nfs4_register_sysctl();
280c8d74d9bSTrond Myklebust 	if (err)
281c8d74d9bSTrond Myklebust 		goto out2;
282c8d74d9bSTrond Myklebust 
2830cfcd405SDai Ngo #ifdef CONFIG_NFS_V4_2
2840cfcd405SDai Ngo 	nfs42_ssc_register_ops();
2850cfcd405SDai Ngo #endif
286ab7017a3SBryan Schumaker 	register_nfs_version(&nfs_v4);
287129d1977SBryan Schumaker 	return 0;
288c8d74d9bSTrond Myklebust out2:
289466bfe7fSBryan Schumaker 	nfs_idmap_quit();
290c8d74d9bSTrond Myklebust out1:
291c8d74d9bSTrond Myklebust 	nfs_dns_resolver_destroy();
292129d1977SBryan Schumaker out:
293129d1977SBryan Schumaker 	return err;
294129d1977SBryan Schumaker }
295129d1977SBryan Schumaker 
exit_nfs_v4(void)29689d77c8fSBryan Schumaker static void __exit exit_nfs_v4(void)
297129d1977SBryan Schumaker {
2985f01d953SPeng Tao 	/* Not called in the _init(), conditionally loaded */
2995f01d953SPeng Tao 	nfs4_pnfs_v3_ds_connect_unload();
3005f01d953SPeng Tao 
301ab7017a3SBryan Schumaker 	unregister_nfs_version(&nfs_v4);
30295ad37f9SFrank van der Linden #ifdef CONFIG_NFS_V4_2
30395ad37f9SFrank van der Linden 	nfs4_xattr_cache_exit();
3040cfcd405SDai Ngo 	nfs42_ssc_unregister_ops();
30595ad37f9SFrank van der Linden #endif
306466bfe7fSBryan Schumaker 	nfs4_unregister_sysctl();
307129d1977SBryan Schumaker 	nfs_idmap_quit();
308c8d74d9bSTrond Myklebust 	nfs_dns_resolver_destroy();
309129d1977SBryan Schumaker }
31089d77c8fSBryan Schumaker 
31189d77c8fSBryan Schumaker MODULE_LICENSE("GPL");
31289d77c8fSBryan Schumaker 
31389d77c8fSBryan Schumaker module_init(init_nfs_v4);
31489d77c8fSBryan Schumaker module_exit(exit_nfs_v4);
315