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> 7fbdefd64SBryan Schumaker #include <linux/nfs4_mount.h> 8466bfe7fSBryan Schumaker #include <linux/nfs_fs.h> 919d87ca3SBryan Schumaker #include "delegation.h" 10fbdefd64SBryan Schumaker #include "internal.h" 11466bfe7fSBryan Schumaker #include "nfs4_fs.h" 1240c64c26SAnna Schumaker #include "nfs4idmap.h" 13c8d74d9bSTrond Myklebust #include "dns_resolve.h" 1419d87ca3SBryan Schumaker #include "pnfs.h" 15ab7017a3SBryan Schumaker #include "nfs.h" 16129d1977SBryan Schumaker 17fbdefd64SBryan Schumaker #define NFSDBG_FACILITY NFSDBG_VFS 18fbdefd64SBryan Schumaker 1919d87ca3SBryan Schumaker static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc); 2019d87ca3SBryan Schumaker static void nfs4_evict_inode(struct inode *inode); 21fbdefd64SBryan Schumaker static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type, 22fbdefd64SBryan Schumaker int flags, const char *dev_name, void *raw_data); 23fbdefd64SBryan Schumaker static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, 24fbdefd64SBryan Schumaker int flags, const char *dev_name, void *raw_data); 25fbdefd64SBryan Schumaker 26fbdefd64SBryan Schumaker static struct file_system_type nfs4_remote_fs_type = { 27fbdefd64SBryan Schumaker .owner = THIS_MODULE, 28fbdefd64SBryan Schumaker .name = "nfs4", 29fbdefd64SBryan Schumaker .mount = nfs4_remote_mount, 30fbdefd64SBryan Schumaker .kill_sb = nfs_kill_super, 31ecf3d1f1SJeff Layton .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, 32fbdefd64SBryan Schumaker }; 33fbdefd64SBryan Schumaker 34fbdefd64SBryan Schumaker struct file_system_type nfs4_referral_fs_type = { 35fbdefd64SBryan Schumaker .owner = THIS_MODULE, 36fbdefd64SBryan Schumaker .name = "nfs4", 37fbdefd64SBryan Schumaker .mount = nfs4_referral_mount, 38fbdefd64SBryan Schumaker .kill_sb = nfs_kill_super, 39ecf3d1f1SJeff Layton .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, 40fbdefd64SBryan Schumaker }; 41fbdefd64SBryan Schumaker 42fbdefd64SBryan Schumaker static const struct super_operations nfs4_sops = { 43fbdefd64SBryan Schumaker .alloc_inode = nfs_alloc_inode, 44ca1a199eSAl Viro .free_inode = nfs_free_inode, 45fbdefd64SBryan Schumaker .write_inode = nfs4_write_inode, 46eed99357STrond Myklebust .drop_inode = nfs_drop_inode, 47fbdefd64SBryan Schumaker .statfs = nfs_statfs, 48fbdefd64SBryan Schumaker .evict_inode = nfs4_evict_inode, 49fbdefd64SBryan Schumaker .umount_begin = nfs_umount_begin, 50fbdefd64SBryan Schumaker .show_options = nfs_show_options, 51fbdefd64SBryan Schumaker .show_devname = nfs_show_devname, 52fbdefd64SBryan Schumaker .show_path = nfs_show_path, 53fbdefd64SBryan Schumaker .show_stats = nfs_show_stats, 54fbdefd64SBryan Schumaker .remount_fs = nfs_remount, 55fbdefd64SBryan Schumaker }; 56fbdefd64SBryan Schumaker 57ab7017a3SBryan Schumaker struct nfs_subversion nfs_v4 = { 58ab7017a3SBryan Schumaker .owner = THIS_MODULE, 59ab7017a3SBryan Schumaker .nfs_fs = &nfs4_fs_type, 60ab7017a3SBryan Schumaker .rpc_vers = &nfs_version4, 61ab7017a3SBryan Schumaker .rpc_ops = &nfs_v4_clientops, 626a74490dSBryan Schumaker .sops = &nfs4_sops, 636a74490dSBryan Schumaker .xattr = nfs4_xattr_handlers, 64ab7017a3SBryan Schumaker }; 65ab7017a3SBryan Schumaker 6619d87ca3SBryan Schumaker static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc) 6719d87ca3SBryan Schumaker { 6819d87ca3SBryan Schumaker int ret = nfs_write_inode(inode, wbc); 6919d87ca3SBryan Schumaker 7071244d9bSTrond Myklebust if (ret == 0) 7171244d9bSTrond Myklebust ret = pnfs_layoutcommit_inode(inode, 7271244d9bSTrond Myklebust wbc->sync_mode == WB_SYNC_ALL); 7319d87ca3SBryan Schumaker return ret; 7419d87ca3SBryan Schumaker } 7519d87ca3SBryan Schumaker 7619d87ca3SBryan Schumaker /* 7719d87ca3SBryan Schumaker * Clean out any remaining NFSv4 state that might be left over due 7819d87ca3SBryan Schumaker * to open() calls that passed nfs_atomic_lookup, but failed to call 7919d87ca3SBryan Schumaker * nfs_open(). 8019d87ca3SBryan Schumaker */ 8119d87ca3SBryan Schumaker static void nfs4_evict_inode(struct inode *inode) 8219d87ca3SBryan Schumaker { 8391b0abe3SJohannes Weiner truncate_inode_pages_final(&inode->i_data); 8419d87ca3SBryan Schumaker clear_inode(inode); 85b47e0e47STrond Myklebust /* If we are holding a delegation, return and free it */ 86b47e0e47STrond Myklebust nfs_inode_evict_delegation(inode); 87415320fcSTrond Myklebust /* Note that above delegreturn would trigger pnfs return-on-close */ 88415320fcSTrond Myklebust pnfs_return_layout(inode); 89415320fcSTrond Myklebust pnfs_destroy_layout(NFS_I(inode)); 9019d87ca3SBryan Schumaker /* First call standard NFS clear_inode() code */ 9119d87ca3SBryan Schumaker nfs_clear_inode(inode); 9219d87ca3SBryan Schumaker } 9319d87ca3SBryan Schumaker 94fbdefd64SBryan Schumaker /* 95fbdefd64SBryan Schumaker * Get the superblock for the NFS4 root partition 96fbdefd64SBryan Schumaker */ 97fbdefd64SBryan Schumaker static struct dentry * 98fbdefd64SBryan Schumaker nfs4_remote_mount(struct file_system_type *fs_type, int flags, 99fbdefd64SBryan Schumaker const char *dev_name, void *info) 100fbdefd64SBryan Schumaker { 1017643c12eSAl Viro return nfs_fs_mount_common(flags, dev_name, info, &nfs_v4); 102fbdefd64SBryan Schumaker } 103fbdefd64SBryan Schumaker 104fbdefd64SBryan Schumaker struct nfs_referral_count { 105fbdefd64SBryan Schumaker struct list_head list; 106fbdefd64SBryan Schumaker const struct task_struct *task; 107fbdefd64SBryan Schumaker unsigned int referral_count; 108fbdefd64SBryan Schumaker }; 109fbdefd64SBryan Schumaker 110fbdefd64SBryan Schumaker static LIST_HEAD(nfs_referral_count_list); 111fbdefd64SBryan Schumaker static DEFINE_SPINLOCK(nfs_referral_count_list_lock); 112fbdefd64SBryan Schumaker 113fbdefd64SBryan Schumaker static struct nfs_referral_count *nfs_find_referral_count(void) 114fbdefd64SBryan Schumaker { 115fbdefd64SBryan Schumaker struct nfs_referral_count *p; 116fbdefd64SBryan Schumaker 117fbdefd64SBryan Schumaker list_for_each_entry(p, &nfs_referral_count_list, list) { 118fbdefd64SBryan Schumaker if (p->task == current) 119fbdefd64SBryan Schumaker return p; 120fbdefd64SBryan Schumaker } 121fbdefd64SBryan Schumaker return NULL; 122fbdefd64SBryan Schumaker } 123fbdefd64SBryan Schumaker 124fbdefd64SBryan Schumaker #define NFS_MAX_NESTED_REFERRALS 2 125fbdefd64SBryan Schumaker 126fbdefd64SBryan Schumaker static int nfs_referral_loop_protect(void) 127fbdefd64SBryan Schumaker { 128fbdefd64SBryan Schumaker struct nfs_referral_count *p, *new; 129fbdefd64SBryan Schumaker int ret = -ENOMEM; 130fbdefd64SBryan Schumaker 131fbdefd64SBryan Schumaker new = kmalloc(sizeof(*new), GFP_KERNEL); 132fbdefd64SBryan Schumaker if (!new) 133fbdefd64SBryan Schumaker goto out; 134fbdefd64SBryan Schumaker new->task = current; 135fbdefd64SBryan Schumaker new->referral_count = 1; 136fbdefd64SBryan Schumaker 137fbdefd64SBryan Schumaker ret = 0; 138fbdefd64SBryan Schumaker spin_lock(&nfs_referral_count_list_lock); 139fbdefd64SBryan Schumaker p = nfs_find_referral_count(); 140fbdefd64SBryan Schumaker if (p != NULL) { 141fbdefd64SBryan Schumaker if (p->referral_count >= NFS_MAX_NESTED_REFERRALS) 142fbdefd64SBryan Schumaker ret = -ELOOP; 143fbdefd64SBryan Schumaker else 144fbdefd64SBryan Schumaker p->referral_count++; 145fbdefd64SBryan Schumaker } else { 146fbdefd64SBryan Schumaker list_add(&new->list, &nfs_referral_count_list); 147fbdefd64SBryan Schumaker new = NULL; 148fbdefd64SBryan Schumaker } 149fbdefd64SBryan Schumaker spin_unlock(&nfs_referral_count_list_lock); 150fbdefd64SBryan Schumaker kfree(new); 151fbdefd64SBryan Schumaker out: 152fbdefd64SBryan Schumaker return ret; 153fbdefd64SBryan Schumaker } 154fbdefd64SBryan Schumaker 155fbdefd64SBryan Schumaker static void nfs_referral_loop_unprotect(void) 156fbdefd64SBryan Schumaker { 157fbdefd64SBryan Schumaker struct nfs_referral_count *p; 158fbdefd64SBryan Schumaker 159fbdefd64SBryan Schumaker spin_lock(&nfs_referral_count_list_lock); 160fbdefd64SBryan Schumaker p = nfs_find_referral_count(); 161fbdefd64SBryan Schumaker p->referral_count--; 162fbdefd64SBryan Schumaker if (p->referral_count == 0) 163fbdefd64SBryan Schumaker list_del(&p->list); 164fbdefd64SBryan Schumaker else 165fbdefd64SBryan Schumaker p = NULL; 166fbdefd64SBryan Schumaker spin_unlock(&nfs_referral_count_list_lock); 167fbdefd64SBryan Schumaker kfree(p); 168fbdefd64SBryan Schumaker } 169fbdefd64SBryan Schumaker 170*4e357761SAl Viro static struct dentry *do_nfs4_mount(struct nfs_server *server, int flags, 171*4e357761SAl Viro struct nfs_mount_info *info, 172*4e357761SAl Viro const char *hostname, 173fbdefd64SBryan Schumaker const char *export_path) 174fbdefd64SBryan Schumaker { 175*4e357761SAl Viro struct vfsmount *root_mnt; 176fbdefd64SBryan Schumaker struct dentry *dentry; 177*4e357761SAl Viro char *root_devname; 178fbdefd64SBryan Schumaker int err; 179*4e357761SAl Viro size_t len; 180*4e357761SAl Viro 181*4e357761SAl Viro if (IS_ERR(server)) 182*4e357761SAl Viro return ERR_CAST(server); 183*4e357761SAl Viro 184*4e357761SAl Viro len = strlen(hostname) + 5; 185*4e357761SAl Viro root_devname = kmalloc(len, GFP_KERNEL); 186*4e357761SAl Viro if (root_devname == NULL) { 187*4e357761SAl Viro nfs_free_server(server); 188*4e357761SAl Viro return ERR_PTR(-ENOMEM); 189*4e357761SAl Viro } 190*4e357761SAl Viro 191*4e357761SAl Viro /* Does hostname needs to be enclosed in brackets? */ 192*4e357761SAl Viro if (strchr(hostname, ':')) 193*4e357761SAl Viro snprintf(root_devname, len, "[%s]:/", hostname); 194*4e357761SAl Viro else 195*4e357761SAl Viro snprintf(root_devname, len, "%s:/", hostname); 196*4e357761SAl Viro info->server = server; 197*4e357761SAl Viro root_mnt = vfs_kern_mount(&nfs4_remote_fs_type, flags, root_devname, info); 198*4e357761SAl Viro if (info->server) 199*4e357761SAl Viro nfs_free_server(info->server); 200*4e357761SAl Viro info->server = NULL; 201*4e357761SAl Viro kfree(root_devname); 202fbdefd64SBryan Schumaker 203fbdefd64SBryan Schumaker if (IS_ERR(root_mnt)) 204fbdefd64SBryan Schumaker return ERR_CAST(root_mnt); 205fbdefd64SBryan Schumaker 206fbdefd64SBryan Schumaker err = nfs_referral_loop_protect(); 207fbdefd64SBryan Schumaker if (err) { 208fbdefd64SBryan Schumaker mntput(root_mnt); 209fbdefd64SBryan Schumaker return ERR_PTR(err); 210fbdefd64SBryan Schumaker } 211fbdefd64SBryan Schumaker 212fbdefd64SBryan Schumaker dentry = mount_subtree(root_mnt, export_path); 213fbdefd64SBryan Schumaker nfs_referral_loop_unprotect(); 214fbdefd64SBryan Schumaker 215fbdefd64SBryan Schumaker return dentry; 216fbdefd64SBryan Schumaker } 217fbdefd64SBryan Schumaker 218fbdefd64SBryan Schumaker struct dentry *nfs4_try_mount(int flags, const char *dev_name, 219ff9099f2SBryan Schumaker struct nfs_mount_info *mount_info, 220ff9099f2SBryan Schumaker struct nfs_subversion *nfs_mod) 221fbdefd64SBryan Schumaker { 222fbdefd64SBryan Schumaker struct nfs_parsed_mount_data *data = mount_info->parsed; 223*4e357761SAl Viro struct dentry *res; 224fbdefd64SBryan Schumaker 2257643c12eSAl Viro mount_info->set_security = nfs_set_sb_security; 2267643c12eSAl Viro 227fbdefd64SBryan Schumaker dfprintk(MOUNT, "--> nfs4_try_mount()\n"); 228fbdefd64SBryan Schumaker 229*4e357761SAl Viro res = do_nfs4_mount(nfs4_create_server(mount_info, &nfs_v4), 2307643c12eSAl Viro flags, mount_info, 231*4e357761SAl Viro data->nfs_server.hostname, 232*4e357761SAl Viro data->nfs_server.export_path); 233fbdefd64SBryan Schumaker 2346706246bSGeyslan G. Bem dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", 2356706246bSGeyslan G. Bem PTR_ERR_OR_ZERO(res), 236fbdefd64SBryan Schumaker IS_ERR(res) ? " [error]" : ""); 237fbdefd64SBryan Schumaker return res; 238fbdefd64SBryan Schumaker } 239fbdefd64SBryan Schumaker 240fbdefd64SBryan Schumaker /* 241fbdefd64SBryan Schumaker * Create an NFS4 server record on referral traversal 242fbdefd64SBryan Schumaker */ 243fbdefd64SBryan Schumaker static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, 244fbdefd64SBryan Schumaker int flags, const char *dev_name, void *raw_data) 245fbdefd64SBryan Schumaker { 246fbdefd64SBryan Schumaker struct nfs_clone_mount *data = raw_data; 2477643c12eSAl Viro struct nfs_mount_info mount_info = { 2487643c12eSAl Viro .fill_super = nfs_fill_super, 2497643c12eSAl Viro .set_security = nfs_clone_sb_security, 2507643c12eSAl Viro .cloned = data, 2517643c12eSAl Viro }; 252fbdefd64SBryan Schumaker struct dentry *res; 253fbdefd64SBryan Schumaker 254fbdefd64SBryan Schumaker dprintk("--> nfs4_referral_mount()\n"); 255fbdefd64SBryan Schumaker 2567643c12eSAl Viro mount_info.mntfh = nfs_alloc_fhandle(); 2577643c12eSAl Viro if (!mount_info.mntfh) 2587643c12eSAl Viro return ERR_PTR(-ENOMEM); 2597643c12eSAl Viro 260*4e357761SAl Viro res = do_nfs4_mount(nfs4_create_referral_server(mount_info.cloned, 2617643c12eSAl Viro mount_info.mntfh), 262*4e357761SAl Viro flags, &mount_info, data->hostname, data->mnt_path); 263fbdefd64SBryan Schumaker 2646706246bSGeyslan G. Bem dprintk("<-- nfs4_referral_mount() = %d%s\n", 2656706246bSGeyslan G. Bem PTR_ERR_OR_ZERO(res), 266fbdefd64SBryan Schumaker IS_ERR(res) ? " [error]" : ""); 2677643c12eSAl Viro 2687643c12eSAl Viro nfs_free_fhandle(mount_info.mntfh); 269fbdefd64SBryan Schumaker return res; 270fbdefd64SBryan Schumaker } 271fbdefd64SBryan Schumaker 272fbdefd64SBryan Schumaker 27389d77c8fSBryan Schumaker static int __init init_nfs_v4(void) 274129d1977SBryan Schumaker { 275129d1977SBryan Schumaker int err; 276129d1977SBryan Schumaker 277c8d74d9bSTrond Myklebust err = nfs_dns_resolver_init(); 278129d1977SBryan Schumaker if (err) 279129d1977SBryan Schumaker goto out; 280129d1977SBryan Schumaker 281c8d74d9bSTrond Myklebust err = nfs_idmap_init(); 282466bfe7fSBryan Schumaker if (err) 283466bfe7fSBryan Schumaker goto out1; 284466bfe7fSBryan Schumaker 285c8d74d9bSTrond Myklebust err = nfs4_register_sysctl(); 286c8d74d9bSTrond Myklebust if (err) 287c8d74d9bSTrond Myklebust goto out2; 288c8d74d9bSTrond Myklebust 289ab7017a3SBryan Schumaker register_nfs_version(&nfs_v4); 290129d1977SBryan Schumaker return 0; 291c8d74d9bSTrond Myklebust out2: 292466bfe7fSBryan Schumaker nfs_idmap_quit(); 293c8d74d9bSTrond Myklebust out1: 294c8d74d9bSTrond Myklebust nfs_dns_resolver_destroy(); 295129d1977SBryan Schumaker out: 296129d1977SBryan Schumaker return err; 297129d1977SBryan Schumaker } 298129d1977SBryan Schumaker 29989d77c8fSBryan Schumaker static void __exit exit_nfs_v4(void) 300129d1977SBryan Schumaker { 3015f01d953SPeng Tao /* Not called in the _init(), conditionally loaded */ 3025f01d953SPeng Tao nfs4_pnfs_v3_ds_connect_unload(); 3035f01d953SPeng Tao 304ab7017a3SBryan Schumaker unregister_nfs_version(&nfs_v4); 305466bfe7fSBryan Schumaker nfs4_unregister_sysctl(); 306129d1977SBryan Schumaker nfs_idmap_quit(); 307c8d74d9bSTrond Myklebust nfs_dns_resolver_destroy(); 308129d1977SBryan Schumaker } 30989d77c8fSBryan Schumaker 31089d77c8fSBryan Schumaker MODULE_LICENSE("GPL"); 31189d77c8fSBryan Schumaker 31289d77c8fSBryan Schumaker module_init(init_nfs_v4); 31389d77c8fSBryan Schumaker module_exit(exit_nfs_v4); 314