]> asedeno.scripts.mit.edu Git - linux.git/blob - fs/nfs/nfs4super.c
nfs: stash nfs_subversion reference into nfs_mount_info
[linux.git] / fs / nfs / nfs4super.c
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, &nfs_v4);
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                               struct nfs_subversion *nfs_mod)
221 {
222         struct nfs_parsed_mount_data *data = mount_info->parsed;
223         struct dentry *res;
224
225         mount_info->set_security = nfs_set_sb_security;
226
227         dfprintk(MOUNT, "--> nfs4_try_mount()\n");
228
229         res = do_nfs4_mount(nfs4_create_server(mount_info, &nfs_v4),
230                             flags, mount_info,
231                             data->nfs_server.hostname,
232                             data->nfs_server.export_path);
233
234         dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
235                  PTR_ERR_OR_ZERO(res),
236                  IS_ERR(res) ? " [error]" : "");
237         return res;
238 }
239
240 /*
241  * Create an NFS4 server record on referral traversal
242  */
243 static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
244                 int flags, const char *dev_name, void *raw_data)
245 {
246         struct nfs_clone_mount *data = raw_data;
247         struct nfs_mount_info mount_info = {
248                 .fill_super = nfs_fill_super,
249                 .set_security = nfs_clone_sb_security,
250                 .cloned = data,
251                 .nfs_mod = &nfs_v4,
252         };
253         struct dentry *res;
254
255         dprintk("--> nfs4_referral_mount()\n");
256
257         mount_info.mntfh = nfs_alloc_fhandle();
258         if (!mount_info.mntfh)
259                 return ERR_PTR(-ENOMEM);
260
261         res = do_nfs4_mount(nfs4_create_referral_server(mount_info.cloned,
262                                                         mount_info.mntfh),
263                             flags, &mount_info, data->hostname, data->mnt_path);
264
265         dprintk("<-- nfs4_referral_mount() = %d%s\n",
266                 PTR_ERR_OR_ZERO(res),
267                 IS_ERR(res) ? " [error]" : "");
268
269         nfs_free_fhandle(mount_info.mntfh);
270         return res;
271 }
272
273
274 static int __init init_nfs_v4(void)
275 {
276         int err;
277
278         err = nfs_dns_resolver_init();
279         if (err)
280                 goto out;
281
282         err = nfs_idmap_init();
283         if (err)
284                 goto out1;
285
286         err = nfs4_register_sysctl();
287         if (err)
288                 goto out2;
289
290         register_nfs_version(&nfs_v4);
291         return 0;
292 out2:
293         nfs_idmap_quit();
294 out1:
295         nfs_dns_resolver_destroy();
296 out:
297         return err;
298 }
299
300 static void __exit exit_nfs_v4(void)
301 {
302         /* Not called in the _init(), conditionally loaded */
303         nfs4_pnfs_v3_ds_connect_unload();
304
305         unregister_nfs_version(&nfs_v4);
306         nfs4_unregister_sysctl();
307         nfs_idmap_quit();
308         nfs_dns_resolver_destroy();
309 }
310
311 MODULE_LICENSE("GPL");
312
313 module_init(init_nfs_v4);
314 module_exit(exit_nfs_v4);