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/mount.h>
8 : : #include <linux/nfs4_mount.h>
9 : : #include <linux/nfs_fs.h>
10 : : #include "delegation.h"
11 : : #include "internal.h"
12 : : #include "nfs4_fs.h"
13 : : #include "nfs4idmap.h"
14 : : #include "dns_resolve.h"
15 : : #include "pnfs.h"
16 : : #include "nfs.h"
17 : :
18 : : #define NFSDBG_FACILITY NFSDBG_VFS
19 : :
20 : : static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc);
21 : : static void nfs4_evict_inode(struct inode *inode);
22 : :
23 : : static const struct super_operations nfs4_sops = {
24 : : .alloc_inode = nfs_alloc_inode,
25 : : .free_inode = nfs_free_inode,
26 : : .write_inode = nfs4_write_inode,
27 : : .drop_inode = nfs_drop_inode,
28 : : .statfs = nfs_statfs,
29 : : .evict_inode = nfs4_evict_inode,
30 : : .umount_begin = nfs_umount_begin,
31 : : .show_options = nfs_show_options,
32 : : .show_devname = nfs_show_devname,
33 : : .show_path = nfs_show_path,
34 : : .show_stats = nfs_show_stats,
35 : : };
36 : :
37 : : struct nfs_subversion nfs_v4 = {
38 : : .owner = THIS_MODULE,
39 : : .nfs_fs = &nfs4_fs_type,
40 : : .rpc_vers = &nfs_version4,
41 : : .rpc_ops = &nfs_v4_clientops,
42 : : .sops = &nfs4_sops,
43 : : .xattr = nfs4_xattr_handlers,
44 : : };
45 : :
46 : 0 : static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
47 : : {
48 : 0 : int ret = nfs_write_inode(inode, wbc);
49 : :
50 [ # # ]: 0 : if (ret == 0)
51 : 0 : ret = pnfs_layoutcommit_inode(inode,
52 : : wbc->sync_mode == WB_SYNC_ALL);
53 : 0 : return ret;
54 : : }
55 : :
56 : : /*
57 : : * Clean out any remaining NFSv4 state that might be left over due
58 : : * to open() calls that passed nfs_atomic_lookup, but failed to call
59 : : * nfs_open().
60 : : */
61 : 0 : static void nfs4_evict_inode(struct inode *inode)
62 : : {
63 : 0 : truncate_inode_pages_final(&inode->i_data);
64 : 0 : clear_inode(inode);
65 : : /* If we are holding a delegation, return and free it */
66 : 0 : nfs_inode_evict_delegation(inode);
67 : : /* Note that above delegreturn would trigger pnfs return-on-close */
68 : 0 : pnfs_return_layout(inode);
69 : 0 : pnfs_destroy_layout(NFS_I(inode));
70 : : /* First call standard NFS clear_inode() code */
71 : 0 : nfs_clear_inode(inode);
72 : 0 : }
73 : :
74 : : struct nfs_referral_count {
75 : : struct list_head list;
76 : : const struct task_struct *task;
77 : : unsigned int referral_count;
78 : : };
79 : :
80 : : static LIST_HEAD(nfs_referral_count_list);
81 : : static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
82 : :
83 : 0 : static struct nfs_referral_count *nfs_find_referral_count(void)
84 : : {
85 : 0 : struct nfs_referral_count *p;
86 : :
87 [ # # # # ]: 0 : list_for_each_entry(p, &nfs_referral_count_list, list) {
88 [ # # # # ]: 0 : if (p->task == current)
89 : : return p;
90 : : }
91 : : return NULL;
92 : : }
93 : :
94 : : #define NFS_MAX_NESTED_REFERRALS 2
95 : :
96 : 0 : static int nfs_referral_loop_protect(void)
97 : : {
98 : 0 : struct nfs_referral_count *p, *new;
99 : 0 : int ret = -ENOMEM;
100 : :
101 : 0 : new = kmalloc(sizeof(*new), GFP_KERNEL);
102 [ # # ]: 0 : if (!new)
103 : 0 : goto out;
104 : 0 : new->task = current;
105 : 0 : new->referral_count = 1;
106 : :
107 : 0 : ret = 0;
108 : 0 : spin_lock(&nfs_referral_count_list_lock);
109 : 0 : p = nfs_find_referral_count();
110 [ # # ]: 0 : if (p != NULL) {
111 [ # # ]: 0 : if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
112 : : ret = -ELOOP;
113 : : else
114 : 0 : p->referral_count++;
115 : : } else {
116 : 0 : list_add(&new->list, &nfs_referral_count_list);
117 : 0 : new = NULL;
118 : : }
119 : 0 : spin_unlock(&nfs_referral_count_list_lock);
120 : 0 : kfree(new);
121 : 0 : out:
122 : 0 : return ret;
123 : : }
124 : :
125 : 0 : static void nfs_referral_loop_unprotect(void)
126 : : {
127 : 0 : struct nfs_referral_count *p;
128 : :
129 : 0 : spin_lock(&nfs_referral_count_list_lock);
130 : 0 : p = nfs_find_referral_count();
131 : 0 : p->referral_count--;
132 [ # # ]: 0 : if (p->referral_count == 0)
133 : 0 : list_del(&p->list);
134 : : else
135 : : p = NULL;
136 : 0 : spin_unlock(&nfs_referral_count_list_lock);
137 : 0 : kfree(p);
138 : 0 : }
139 : :
140 : 0 : static int do_nfs4_mount(struct nfs_server *server,
141 : : struct fs_context *fc,
142 : : const char *hostname,
143 : : const char *export_path)
144 : : {
145 : 0 : struct nfs_fs_context *root_ctx;
146 : 0 : struct fs_context *root_fc;
147 : 0 : struct vfsmount *root_mnt;
148 : 0 : struct dentry *dentry;
149 : 0 : size_t len;
150 : 0 : int ret;
151 : :
152 : 0 : struct fs_parameter param = {
153 : : .key = "source",
154 : : .type = fs_value_is_string,
155 : : .dirfd = -1,
156 : : };
157 : :
158 [ # # ]: 0 : if (IS_ERR(server))
159 : 0 : return PTR_ERR(server);
160 : :
161 : 0 : root_fc = vfs_dup_fs_context(fc);
162 [ # # ]: 0 : if (IS_ERR(root_fc)) {
163 : 0 : nfs_free_server(server);
164 : 0 : return PTR_ERR(root_fc);
165 : : }
166 : 0 : kfree(root_fc->source);
167 : 0 : root_fc->source = NULL;
168 : :
169 [ # # ]: 0 : root_ctx = nfs_fc2context(root_fc);
170 : 0 : root_ctx->internal = true;
171 : 0 : root_ctx->server = server;
172 : : /* We leave export_path unset as it's not used to find the root. */
173 : :
174 : 0 : len = strlen(hostname) + 5;
175 [ # # ]: 0 : param.string = kmalloc(len, GFP_KERNEL);
176 [ # # ]: 0 : if (param.string == NULL) {
177 : 0 : put_fs_context(root_fc);
178 : 0 : return -ENOMEM;
179 : : }
180 : :
181 : : /* Does hostname needs to be enclosed in brackets? */
182 [ # # ]: 0 : if (strchr(hostname, ':'))
183 : 0 : param.size = snprintf(param.string, len, "[%s]:/", hostname);
184 : : else
185 : 0 : param.size = snprintf(param.string, len, "%s:/", hostname);
186 : 0 : ret = vfs_parse_fs_param(root_fc, ¶m);
187 : 0 : kfree(param.string);
188 [ # # ]: 0 : if (ret < 0) {
189 : 0 : put_fs_context(root_fc);
190 : 0 : return ret;
191 : : }
192 : 0 : root_mnt = fc_mount(root_fc);
193 : 0 : put_fs_context(root_fc);
194 : :
195 [ # # ]: 0 : if (IS_ERR(root_mnt))
196 : 0 : return PTR_ERR(root_mnt);
197 : :
198 : 0 : ret = nfs_referral_loop_protect();
199 [ # # ]: 0 : if (ret) {
200 : 0 : mntput(root_mnt);
201 : 0 : return ret;
202 : : }
203 : :
204 : 0 : dentry = mount_subtree(root_mnt, export_path);
205 : 0 : nfs_referral_loop_unprotect();
206 : :
207 [ # # ]: 0 : if (IS_ERR(dentry))
208 : 0 : return PTR_ERR(dentry);
209 : :
210 : 0 : fc->root = dentry;
211 : 0 : return 0;
212 : : }
213 : :
214 : 0 : int nfs4_try_get_tree(struct fs_context *fc)
215 : : {
216 : 0 : struct nfs_fs_context *ctx = nfs_fc2context(fc);
217 : 0 : int err;
218 : :
219 : 0 : dfprintk(MOUNT, "--> nfs4_try_get_tree()\n");
220 : :
221 : : /* We create a mount for the server's root, walk to the requested
222 : : * location and then create another mount for that.
223 : : */
224 : 0 : err= do_nfs4_mount(nfs4_create_server(fc),
225 : 0 : fc, ctx->nfs_server.hostname,
226 : 0 : ctx->nfs_server.export_path);
227 [ # # ]: 0 : if (err) {
228 : 0 : nfs_errorf(fc, "NFS4: Couldn't follow remote path");
229 : 0 : dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
230 : : } else {
231 : 0 : dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
232 : : }
233 : 0 : return err;
234 : : }
235 : :
236 : : /*
237 : : * Create an NFS4 server record on referral traversal
238 : : */
239 : 0 : int nfs4_get_referral_tree(struct fs_context *fc)
240 : : {
241 : 0 : struct nfs_fs_context *ctx = nfs_fc2context(fc);
242 : 0 : int err;
243 : :
244 : 0 : dprintk("--> nfs4_referral_mount()\n");
245 : :
246 : : /* create a new volume representation */
247 : 0 : err = do_nfs4_mount(nfs4_create_referral_server(fc),
248 : 0 : fc, ctx->nfs_server.hostname,
249 : 0 : ctx->nfs_server.export_path);
250 [ # # ]: 0 : if (err) {
251 : 0 : nfs_errorf(fc, "NFS4: Couldn't follow remote path");
252 : 0 : dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
253 : : } else {
254 : 0 : dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
255 : : }
256 : 0 : return err;
257 : : }
258 : :
259 : 28 : static int __init init_nfs_v4(void)
260 : : {
261 : 28 : int err;
262 : :
263 : 28 : err = nfs_dns_resolver_init();
264 : 28 : if (err)
265 : : goto out;
266 : :
267 : 28 : err = nfs_idmap_init();
268 [ - + ]: 28 : if (err)
269 : 0 : goto out1;
270 : :
271 : 28 : err = nfs4_register_sysctl();
272 [ - + ]: 28 : if (err)
273 : 0 : goto out2;
274 : :
275 : 28 : register_nfs_version(&nfs_v4);
276 : 28 : return 0;
277 : : out2:
278 : 0 : nfs_idmap_quit();
279 : : out1:
280 : : nfs_dns_resolver_destroy();
281 : : out:
282 : : return err;
283 : : }
284 : :
285 : 0 : static void __exit exit_nfs_v4(void)
286 : : {
287 : : /* Not called in the _init(), conditionally loaded */
288 : 0 : nfs4_pnfs_v3_ds_connect_unload();
289 : :
290 : 0 : unregister_nfs_version(&nfs_v4);
291 : 0 : nfs4_unregister_sysctl();
292 : 0 : nfs_idmap_quit();
293 : 0 : nfs_dns_resolver_destroy();
294 : 0 : }
295 : :
296 : : MODULE_LICENSE("GPL");
297 : :
298 : : module_init(init_nfs_v4);
299 : : module_exit(exit_nfs_v4);
|