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