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