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