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, ¶m);
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