]> asedeno.scripts.mit.edu Git - linux.git/blob - fs/nfs/nfs4super.c
nfs: fold nfs4_remote_fs_type and nfs4_remote_referral_fs_type
[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 static struct vfsmount *nfs_do_root_mount(struct nfs_server *server, int flags,
105                                           struct nfs_mount_info *info,
106                                           const char *hostname)
107 {
108         struct vfsmount *root_mnt;
109         char *root_devname;
110         size_t len;
111
112         if (IS_ERR(server))
113                 return ERR_CAST(server);
114
115         len = strlen(hostname) + 5;
116         root_devname = kmalloc(len, GFP_KERNEL);
117         if (root_devname == NULL) {
118                 nfs_free_server(server);
119                 return ERR_PTR(-ENOMEM);
120         }
121         /* Does hostname needs to be enclosed in brackets? */
122         if (strchr(hostname, ':'))
123                 snprintf(root_devname, len, "[%s]:/", hostname);
124         else
125                 snprintf(root_devname, len, "%s:/", hostname);
126         info->server = server;
127         root_mnt = vfs_kern_mount(&nfs4_remote_fs_type, flags, root_devname, info);
128         if (info->server)
129                 nfs_free_server(info->server);
130         info->server = NULL;
131         kfree(root_devname);
132         return root_mnt;
133 }
134
135 struct nfs_referral_count {
136         struct list_head list;
137         const struct task_struct *task;
138         unsigned int referral_count;
139 };
140
141 static LIST_HEAD(nfs_referral_count_list);
142 static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
143
144 static struct nfs_referral_count *nfs_find_referral_count(void)
145 {
146         struct nfs_referral_count *p;
147
148         list_for_each_entry(p, &nfs_referral_count_list, list) {
149                 if (p->task == current)
150                         return p;
151         }
152         return NULL;
153 }
154
155 #define NFS_MAX_NESTED_REFERRALS 2
156
157 static int nfs_referral_loop_protect(void)
158 {
159         struct nfs_referral_count *p, *new;
160         int ret = -ENOMEM;
161
162         new = kmalloc(sizeof(*new), GFP_KERNEL);
163         if (!new)
164                 goto out;
165         new->task = current;
166         new->referral_count = 1;
167
168         ret = 0;
169         spin_lock(&nfs_referral_count_list_lock);
170         p = nfs_find_referral_count();
171         if (p != NULL) {
172                 if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
173                         ret = -ELOOP;
174                 else
175                         p->referral_count++;
176         } else {
177                 list_add(&new->list, &nfs_referral_count_list);
178                 new = NULL;
179         }
180         spin_unlock(&nfs_referral_count_list_lock);
181         kfree(new);
182 out:
183         return ret;
184 }
185
186 static void nfs_referral_loop_unprotect(void)
187 {
188         struct nfs_referral_count *p;
189
190         spin_lock(&nfs_referral_count_list_lock);
191         p = nfs_find_referral_count();
192         p->referral_count--;
193         if (p->referral_count == 0)
194                 list_del(&p->list);
195         else
196                 p = NULL;
197         spin_unlock(&nfs_referral_count_list_lock);
198         kfree(p);
199 }
200
201 static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
202                 const char *export_path)
203 {
204         struct dentry *dentry;
205         int err;
206
207         if (IS_ERR(root_mnt))
208                 return ERR_CAST(root_mnt);
209
210         err = nfs_referral_loop_protect();
211         if (err) {
212                 mntput(root_mnt);
213                 return ERR_PTR(err);
214         }
215
216         dentry = mount_subtree(root_mnt, export_path);
217         nfs_referral_loop_unprotect();
218
219         return dentry;
220 }
221
222 struct dentry *nfs4_try_mount(int flags, const char *dev_name,
223                               struct nfs_mount_info *mount_info,
224                               struct nfs_subversion *nfs_mod)
225 {
226         char *export_path;
227         struct vfsmount *root_mnt;
228         struct dentry *res;
229         struct nfs_parsed_mount_data *data = mount_info->parsed;
230
231         mount_info->set_security = nfs_set_sb_security;
232
233         dfprintk(MOUNT, "--> nfs4_try_mount()\n");
234
235         export_path = data->nfs_server.export_path;
236         data->nfs_server.export_path = "/";
237         root_mnt = nfs_do_root_mount(
238                         nfs4_create_server(mount_info, &nfs_v4),
239                         flags, mount_info,
240                         data->nfs_server.hostname);
241         data->nfs_server.export_path = export_path;
242
243         res = nfs_follow_remote_path(root_mnt, export_path);
244
245         dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
246                  PTR_ERR_OR_ZERO(res),
247                  IS_ERR(res) ? " [error]" : "");
248         return res;
249 }
250
251 /*
252  * Create an NFS4 server record on referral traversal
253  */
254 static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
255                 int flags, const char *dev_name, void *raw_data)
256 {
257         struct nfs_clone_mount *data = raw_data;
258         struct nfs_mount_info mount_info = {
259                 .fill_super = nfs_fill_super,
260                 .set_security = nfs_clone_sb_security,
261                 .cloned = data,
262         };
263         char *export_path;
264         struct vfsmount *root_mnt;
265         struct dentry *res;
266
267         dprintk("--> nfs4_referral_mount()\n");
268
269         mount_info.mntfh = nfs_alloc_fhandle();
270         if (!mount_info.mntfh)
271                 return ERR_PTR(-ENOMEM);
272
273         export_path = data->mnt_path;
274         data->mnt_path = "/";
275         root_mnt = nfs_do_root_mount(
276                         nfs4_create_referral_server(mount_info.cloned,
277                                                     mount_info.mntfh),
278                         flags, &mount_info, data->hostname);
279         data->mnt_path = export_path;
280
281         res = nfs_follow_remote_path(root_mnt, export_path);
282         dprintk("<-- nfs4_referral_mount() = %d%s\n",
283                 PTR_ERR_OR_ZERO(res),
284                 IS_ERR(res) ? " [error]" : "");
285
286         nfs_free_fhandle(mount_info.mntfh);
287         return res;
288 }
289
290
291 static int __init init_nfs_v4(void)
292 {
293         int err;
294
295         err = nfs_dns_resolver_init();
296         if (err)
297                 goto out;
298
299         err = nfs_idmap_init();
300         if (err)
301                 goto out1;
302
303         err = nfs4_register_sysctl();
304         if (err)
305                 goto out2;
306
307         register_nfs_version(&nfs_v4);
308         return 0;
309 out2:
310         nfs_idmap_quit();
311 out1:
312         nfs_dns_resolver_destroy();
313 out:
314         return err;
315 }
316
317 static void __exit exit_nfs_v4(void)
318 {
319         /* Not called in the _init(), conditionally loaded */
320         nfs4_pnfs_v3_ds_connect_unload();
321
322         unregister_nfs_version(&nfs_v4);
323         nfs4_unregister_sysctl();
324         nfs_idmap_quit();
325         nfs_dns_resolver_destroy();
326 }
327
328 MODULE_LICENSE("GPL");
329
330 module_init(init_nfs_v4);
331 module_exit(exit_nfs_v4);