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 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 */ 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 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 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 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 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 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 */ 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 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 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