1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com> 4 */ 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/nfs4_mount.h> 8 #include <linux/nfs_fs.h> 9 #include "delegation.h" 10 #include "internal.h" 11 #include "nfs4_fs.h" 12 #include "nfs4idmap.h" 13 #include "dns_resolve.h" 14 #include "pnfs.h" 15 #include "nfs.h" 16 17 #define NFSDBG_FACILITY NFSDBG_VFS 18 19 static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc); 20 static void nfs4_evict_inode(struct inode *inode); 21 static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type, 22 int flags, const char *dev_name, void *raw_data); 23 static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, 24 int flags, const char *dev_name, void *raw_data); 25 26 static struct file_system_type nfs4_remote_fs_type = { 27 .owner = THIS_MODULE, 28 .name = "nfs4", 29 .mount = nfs4_remote_mount, 30 .kill_sb = nfs_kill_super, 31 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, 32 }; 33 34 struct file_system_type nfs4_referral_fs_type = { 35 .owner = THIS_MODULE, 36 .name = "nfs4", 37 .mount = nfs4_referral_mount, 38 .kill_sb = nfs_kill_super, 39 .fs_flags = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA, 40 }; 41 42 static const struct super_operations nfs4_sops = { 43 .alloc_inode = nfs_alloc_inode, 44 .free_inode = nfs_free_inode, 45 .write_inode = nfs4_write_inode, 46 .drop_inode = nfs_drop_inode, 47 .statfs = nfs_statfs, 48 .evict_inode = nfs4_evict_inode, 49 .umount_begin = nfs_umount_begin, 50 .show_options = nfs_show_options, 51 .show_devname = nfs_show_devname, 52 .show_path = nfs_show_path, 53 .show_stats = nfs_show_stats, 54 .remount_fs = nfs_remount, 55 }; 56 57 struct nfs_subversion nfs_v4 = { 58 .owner = THIS_MODULE, 59 .nfs_fs = &nfs4_fs_type, 60 .rpc_vers = &nfs_version4, 61 .rpc_ops = &nfs_v4_clientops, 62 .sops = &nfs4_sops, 63 .xattr = nfs4_xattr_handlers, 64 }; 65 66 static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc) 67 { 68 int ret = nfs_write_inode(inode, wbc); 69 70 if (ret == 0) 71 ret = pnfs_layoutcommit_inode(inode, 72 wbc->sync_mode == WB_SYNC_ALL); 73 return ret; 74 } 75 76 /* 77 * Clean out any remaining NFSv4 state that might be left over due 78 * to open() calls that passed nfs_atomic_lookup, but failed to call 79 * nfs_open(). 80 */ 81 static void nfs4_evict_inode(struct inode *inode) 82 { 83 truncate_inode_pages_final(&inode->i_data); 84 clear_inode(inode); 85 /* If we are holding a delegation, return and free it */ 86 nfs_inode_evict_delegation(inode); 87 /* Note that above delegreturn would trigger pnfs return-on-close */ 88 pnfs_return_layout(inode); 89 pnfs_destroy_layout(NFS_I(inode)); 90 /* First call standard NFS clear_inode() code */ 91 nfs_clear_inode(inode); 92 } 93 94 /* 95 * Get the superblock for the NFS4 root partition 96 */ 97 static struct dentry * 98 nfs4_remote_mount(struct file_system_type *fs_type, int flags, 99 const char *dev_name, void *info) 100 { 101 return nfs_fs_mount_common(flags, dev_name, info); 102 } 103 104 struct nfs_referral_count { 105 struct list_head list; 106 const struct task_struct *task; 107 unsigned int referral_count; 108 }; 109 110 static LIST_HEAD(nfs_referral_count_list); 111 static DEFINE_SPINLOCK(nfs_referral_count_list_lock); 112 113 static struct nfs_referral_count *nfs_find_referral_count(void) 114 { 115 struct nfs_referral_count *p; 116 117 list_for_each_entry(p, &nfs_referral_count_list, list) { 118 if (p->task == current) 119 return p; 120 } 121 return NULL; 122 } 123 124 #define NFS_MAX_NESTED_REFERRALS 2 125 126 static int nfs_referral_loop_protect(void) 127 { 128 struct nfs_referral_count *p, *new; 129 int ret = -ENOMEM; 130 131 new = kmalloc(sizeof(*new), GFP_KERNEL); 132 if (!new) 133 goto out; 134 new->task = current; 135 new->referral_count = 1; 136 137 ret = 0; 138 spin_lock(&nfs_referral_count_list_lock); 139 p = nfs_find_referral_count(); 140 if (p != NULL) { 141 if (p->referral_count >= NFS_MAX_NESTED_REFERRALS) 142 ret = -ELOOP; 143 else 144 p->referral_count++; 145 } else { 146 list_add(&new->list, &nfs_referral_count_list); 147 new = NULL; 148 } 149 spin_unlock(&nfs_referral_count_list_lock); 150 kfree(new); 151 out: 152 return ret; 153 } 154 155 static void nfs_referral_loop_unprotect(void) 156 { 157 struct nfs_referral_count *p; 158 159 spin_lock(&nfs_referral_count_list_lock); 160 p = nfs_find_referral_count(); 161 p->referral_count--; 162 if (p->referral_count == 0) 163 list_del(&p->list); 164 else 165 p = NULL; 166 spin_unlock(&nfs_referral_count_list_lock); 167 kfree(p); 168 } 169 170 static struct dentry *do_nfs4_mount(struct nfs_server *server, int flags, 171 struct nfs_mount_info *info, 172 const char *hostname, 173 const char *export_path) 174 { 175 struct vfsmount *root_mnt; 176 struct dentry *dentry; 177 char *root_devname; 178 int err; 179 size_t len; 180 181 if (IS_ERR(server)) 182 return ERR_CAST(server); 183 184 len = strlen(hostname) + 5; 185 root_devname = kmalloc(len, GFP_KERNEL); 186 if (root_devname == NULL) { 187 nfs_free_server(server); 188 return ERR_PTR(-ENOMEM); 189 } 190 191 /* Does hostname needs to be enclosed in brackets? */ 192 if (strchr(hostname, ':')) 193 snprintf(root_devname, len, "[%s]:/", hostname); 194 else 195 snprintf(root_devname, len, "%s:/", hostname); 196 info->server = server; 197 root_mnt = vfs_kern_mount(&nfs4_remote_fs_type, flags, root_devname, info); 198 if (info->server) 199 nfs_free_server(info->server); 200 info->server = NULL; 201 kfree(root_devname); 202 203 if (IS_ERR(root_mnt)) 204 return ERR_CAST(root_mnt); 205 206 err = nfs_referral_loop_protect(); 207 if (err) { 208 mntput(root_mnt); 209 return ERR_PTR(err); 210 } 211 212 dentry = mount_subtree(root_mnt, export_path); 213 nfs_referral_loop_unprotect(); 214 215 return dentry; 216 } 217 218 struct dentry *nfs4_try_mount(int flags, const char *dev_name, 219 struct nfs_mount_info *mount_info) 220 { 221 struct nfs_parsed_mount_data *data = mount_info->parsed; 222 struct dentry *res; 223 224 mount_info->set_security = nfs_set_sb_security; 225 226 dfprintk(MOUNT, "--> nfs4_try_mount()\n"); 227 228 res = do_nfs4_mount(nfs4_create_server(mount_info, &nfs_v4), 229 flags, mount_info, 230 data->nfs_server.hostname, 231 data->nfs_server.export_path); 232 233 dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", 234 PTR_ERR_OR_ZERO(res), 235 IS_ERR(res) ? " [error]" : ""); 236 return res; 237 } 238 239 /* 240 * Create an NFS4 server record on referral traversal 241 */ 242 static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, 243 int flags, const char *dev_name, void *raw_data) 244 { 245 struct nfs_clone_mount *data = raw_data; 246 struct nfs_mount_info mount_info = { 247 .fill_super = nfs_fill_super, 248 .set_security = nfs_clone_sb_security, 249 .cloned = data, 250 .nfs_mod = &nfs_v4, 251 }; 252 struct dentry *res; 253 254 dprintk("--> nfs4_referral_mount()\n"); 255 256 mount_info.mntfh = nfs_alloc_fhandle(); 257 if (!mount_info.mntfh) 258 return ERR_PTR(-ENOMEM); 259 260 res = do_nfs4_mount(nfs4_create_referral_server(mount_info.cloned, 261 mount_info.mntfh), 262 flags, &mount_info, data->hostname, data->mnt_path); 263 264 dprintk("<-- nfs4_referral_mount() = %d%s\n", 265 PTR_ERR_OR_ZERO(res), 266 IS_ERR(res) ? " [error]" : ""); 267 268 nfs_free_fhandle(mount_info.mntfh); 269 return res; 270 } 271 272 273 static int __init init_nfs_v4(void) 274 { 275 int err; 276 277 err = nfs_dns_resolver_init(); 278 if (err) 279 goto out; 280 281 err = nfs_idmap_init(); 282 if (err) 283 goto out1; 284 285 err = nfs4_register_sysctl(); 286 if (err) 287 goto out2; 288 289 register_nfs_version(&nfs_v4); 290 return 0; 291 out2: 292 nfs_idmap_quit(); 293 out1: 294 nfs_dns_resolver_destroy(); 295 out: 296 return err; 297 } 298 299 static void __exit exit_nfs_v4(void) 300 { 301 /* Not called in the _init(), conditionally loaded */ 302 nfs4_pnfs_v3_ds_connect_unload(); 303 304 unregister_nfs_version(&nfs_v4); 305 nfs4_unregister_sysctl(); 306 nfs_idmap_quit(); 307 nfs_dns_resolver_destroy(); 308 } 309 310 MODULE_LICENSE("GPL"); 311 312 module_init(init_nfs_v4); 313 module_exit(exit_nfs_v4); 314