diff options
author | Bryan Schumaker <bjschuma@netapp.com> | 2012-07-16 16:39:20 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-07-17 13:33:55 -0400 |
commit | fbdefd6442811392e857721573b63a51d1149cc8 (patch) | |
tree | d3b36fa0af3a91a22c3799db13c4cb07d0ec2fff /fs/nfs/nfs4super.c | |
parent | 3cadf4b864cab9d19b935289c004799d1065cd03 (diff) |
NFS: Split out the NFS v4 filesystem types
This allows me to move the v4 mounting and unmounting functions out of
the generic client and into a file that is only compiled when CONFIG_NFS_V4
is enabled.
Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4super.c')
-rw-r--r-- | fs/nfs/nfs4super.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c index 70c394e75ca1..2af26913884f 100644 --- a/fs/nfs/nfs4super.c +++ b/fs/nfs/nfs4super.c | |||
@@ -2,10 +2,331 @@ | |||
2 | * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com> | 2 | * Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com> |
3 | */ | 3 | */ |
4 | #include <linux/init.h> | 4 | #include <linux/init.h> |
5 | #include <linux/module.h> | ||
5 | #include <linux/nfs_idmap.h> | 6 | #include <linux/nfs_idmap.h> |
7 | #include <linux/nfs4_mount.h> | ||
6 | #include <linux/nfs_fs.h> | 8 | #include <linux/nfs_fs.h> |
9 | #include "internal.h" | ||
7 | #include "nfs4_fs.h" | 10 | #include "nfs4_fs.h" |
8 | 11 | ||
12 | #define NFSDBG_FACILITY NFSDBG_VFS | ||
13 | |||
14 | static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type, | ||
15 | int flags, const char *dev_name, void *raw_data); | ||
16 | static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type, | ||
17 | int flags, const char *dev_name, void *raw_data); | ||
18 | static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, | ||
19 | int flags, const char *dev_name, void *raw_data); | ||
20 | static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type, | ||
21 | int flags, const char *dev_name, void *raw_data); | ||
22 | |||
23 | static struct file_system_type nfs4_fs_type = { | ||
24 | .owner = THIS_MODULE, | ||
25 | .name = "nfs4", | ||
26 | .mount = nfs_fs_mount, | ||
27 | .kill_sb = nfs_kill_super, | ||
28 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | ||
29 | }; | ||
30 | |||
31 | static struct file_system_type nfs4_remote_fs_type = { | ||
32 | .owner = THIS_MODULE, | ||
33 | .name = "nfs4", | ||
34 | .mount = nfs4_remote_mount, | ||
35 | .kill_sb = nfs_kill_super, | ||
36 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | ||
37 | }; | ||
38 | |||
39 | struct file_system_type nfs4_xdev_fs_type = { | ||
40 | .owner = THIS_MODULE, | ||
41 | .name = "nfs4", | ||
42 | .mount = nfs4_xdev_mount, | ||
43 | .kill_sb = nfs_kill_super, | ||
44 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | ||
45 | }; | ||
46 | |||
47 | static struct file_system_type nfs4_remote_referral_fs_type = { | ||
48 | .owner = THIS_MODULE, | ||
49 | .name = "nfs4", | ||
50 | .mount = nfs4_remote_referral_mount, | ||
51 | .kill_sb = nfs_kill_super, | ||
52 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | ||
53 | }; | ||
54 | |||
55 | struct file_system_type nfs4_referral_fs_type = { | ||
56 | .owner = THIS_MODULE, | ||
57 | .name = "nfs4", | ||
58 | .mount = nfs4_referral_mount, | ||
59 | .kill_sb = nfs_kill_super, | ||
60 | .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, | ||
61 | }; | ||
62 | |||
63 | static const struct super_operations nfs4_sops = { | ||
64 | .alloc_inode = nfs_alloc_inode, | ||
65 | .destroy_inode = nfs_destroy_inode, | ||
66 | .write_inode = nfs4_write_inode, | ||
67 | .put_super = nfs_put_super, | ||
68 | .statfs = nfs_statfs, | ||
69 | .evict_inode = nfs4_evict_inode, | ||
70 | .umount_begin = nfs_umount_begin, | ||
71 | .show_options = nfs_show_options, | ||
72 | .show_devname = nfs_show_devname, | ||
73 | .show_path = nfs_show_path, | ||
74 | .show_stats = nfs_show_stats, | ||
75 | .remount_fs = nfs_remount, | ||
76 | }; | ||
77 | |||
78 | /* | ||
79 | * Set up an NFS4 superblock | ||
80 | */ | ||
81 | static void nfs4_fill_super(struct super_block *sb, | ||
82 | struct nfs_mount_info *mount_info) | ||
83 | { | ||
84 | sb->s_time_gran = 1; | ||
85 | sb->s_op = &nfs4_sops; | ||
86 | /* | ||
87 | * The VFS shouldn't apply the umask to mode bits. We will do | ||
88 | * so ourselves when necessary. | ||
89 | */ | ||
90 | sb->s_flags |= MS_POSIXACL; | ||
91 | sb->s_xattr = nfs4_xattr_handlers; | ||
92 | nfs_initialise_sb(sb); | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Get the superblock for the NFS4 root partition | ||
97 | */ | ||
98 | static struct dentry * | ||
99 | nfs4_remote_mount(struct file_system_type *fs_type, int flags, | ||
100 | const char *dev_name, void *info) | ||
101 | { | ||
102 | struct nfs_mount_info *mount_info = info; | ||
103 | struct nfs_server *server; | ||
104 | struct dentry *mntroot = ERR_PTR(-ENOMEM); | ||
105 | |||
106 | mount_info->fill_super = nfs4_fill_super; | ||
107 | mount_info->set_security = nfs_set_sb_security; | ||
108 | |||
109 | /* Get a volume representation */ | ||
110 | server = nfs4_create_server(mount_info->parsed, mount_info->mntfh); | ||
111 | if (IS_ERR(server)) { | ||
112 | mntroot = ERR_CAST(server); | ||
113 | goto out; | ||
114 | } | ||
115 | |||
116 | mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info); | ||
117 | |||
118 | out: | ||
119 | return mntroot; | ||
120 | } | ||
121 | |||
122 | static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type, | ||
123 | int flags, void *data, const char *hostname) | ||
124 | { | ||
125 | struct vfsmount *root_mnt; | ||
126 | char *root_devname; | ||
127 | size_t len; | ||
128 | |||
129 | len = strlen(hostname) + 5; | ||
130 | root_devname = kmalloc(len, GFP_KERNEL); | ||
131 | if (root_devname == NULL) | ||
132 | return ERR_PTR(-ENOMEM); | ||
133 | /* Does hostname needs to be enclosed in brackets? */ | ||
134 | if (strchr(hostname, ':')) | ||
135 | snprintf(root_devname, len, "[%s]:/", hostname); | ||
136 | else | ||
137 | snprintf(root_devname, len, "%s:/", hostname); | ||
138 | root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data); | ||
139 | kfree(root_devname); | ||
140 | return root_mnt; | ||
141 | } | ||
142 | |||
143 | struct nfs_referral_count { | ||
144 | struct list_head list; | ||
145 | const struct task_struct *task; | ||
146 | unsigned int referral_count; | ||
147 | }; | ||
148 | |||
149 | static LIST_HEAD(nfs_referral_count_list); | ||
150 | static DEFINE_SPINLOCK(nfs_referral_count_list_lock); | ||
151 | |||
152 | static struct nfs_referral_count *nfs_find_referral_count(void) | ||
153 | { | ||
154 | struct nfs_referral_count *p; | ||
155 | |||
156 | list_for_each_entry(p, &nfs_referral_count_list, list) { | ||
157 | if (p->task == current) | ||
158 | return p; | ||
159 | } | ||
160 | return NULL; | ||
161 | } | ||
162 | |||
163 | #define NFS_MAX_NESTED_REFERRALS 2 | ||
164 | |||
165 | static int nfs_referral_loop_protect(void) | ||
166 | { | ||
167 | struct nfs_referral_count *p, *new; | ||
168 | int ret = -ENOMEM; | ||
169 | |||
170 | new = kmalloc(sizeof(*new), GFP_KERNEL); | ||
171 | if (!new) | ||
172 | goto out; | ||
173 | new->task = current; | ||
174 | new->referral_count = 1; | ||
175 | |||
176 | ret = 0; | ||
177 | spin_lock(&nfs_referral_count_list_lock); | ||
178 | p = nfs_find_referral_count(); | ||
179 | if (p != NULL) { | ||
180 | if (p->referral_count >= NFS_MAX_NESTED_REFERRALS) | ||
181 | ret = -ELOOP; | ||
182 | else | ||
183 | p->referral_count++; | ||
184 | } else { | ||
185 | list_add(&new->list, &nfs_referral_count_list); | ||
186 | new = NULL; | ||
187 | } | ||
188 | spin_unlock(&nfs_referral_count_list_lock); | ||
189 | kfree(new); | ||
190 | out: | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | static void nfs_referral_loop_unprotect(void) | ||
195 | { | ||
196 | struct nfs_referral_count *p; | ||
197 | |||
198 | spin_lock(&nfs_referral_count_list_lock); | ||
199 | p = nfs_find_referral_count(); | ||
200 | p->referral_count--; | ||
201 | if (p->referral_count == 0) | ||
202 | list_del(&p->list); | ||
203 | else | ||
204 | p = NULL; | ||
205 | spin_unlock(&nfs_referral_count_list_lock); | ||
206 | kfree(p); | ||
207 | } | ||
208 | |||
209 | static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, | ||
210 | const char *export_path) | ||
211 | { | ||
212 | struct dentry *dentry; | ||
213 | int err; | ||
214 | |||
215 | if (IS_ERR(root_mnt)) | ||
216 | return ERR_CAST(root_mnt); | ||
217 | |||
218 | err = nfs_referral_loop_protect(); | ||
219 | if (err) { | ||
220 | mntput(root_mnt); | ||
221 | return ERR_PTR(err); | ||
222 | } | ||
223 | |||
224 | dentry = mount_subtree(root_mnt, export_path); | ||
225 | nfs_referral_loop_unprotect(); | ||
226 | |||
227 | return dentry; | ||
228 | } | ||
229 | |||
230 | struct dentry *nfs4_try_mount(int flags, const char *dev_name, | ||
231 | struct nfs_mount_info *mount_info) | ||
232 | { | ||
233 | char *export_path; | ||
234 | struct vfsmount *root_mnt; | ||
235 | struct dentry *res; | ||
236 | struct nfs_parsed_mount_data *data = mount_info->parsed; | ||
237 | |||
238 | dfprintk(MOUNT, "--> nfs4_try_mount()\n"); | ||
239 | |||
240 | mount_info->fill_super = nfs4_fill_super; | ||
241 | |||
242 | export_path = data->nfs_server.export_path; | ||
243 | data->nfs_server.export_path = "/"; | ||
244 | root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info, | ||
245 | data->nfs_server.hostname); | ||
246 | data->nfs_server.export_path = export_path; | ||
247 | |||
248 | res = nfs_follow_remote_path(root_mnt, export_path); | ||
249 | |||
250 | dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n", | ||
251 | IS_ERR(res) ? PTR_ERR(res) : 0, | ||
252 | IS_ERR(res) ? " [error]" : ""); | ||
253 | return res; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Clone an NFS4 server record on xdev traversal (FSID-change) | ||
258 | */ | ||
259 | static struct dentry * | ||
260 | nfs4_xdev_mount(struct file_system_type *fs_type, int flags, | ||
261 | const char *dev_name, void *raw_data) | ||
262 | { | ||
263 | struct nfs_mount_info mount_info = { | ||
264 | .fill_super = nfs_clone_super, | ||
265 | .set_security = nfs_clone_sb_security, | ||
266 | .cloned = raw_data, | ||
267 | }; | ||
268 | return nfs_xdev_mount_common(&nfs4_fs_type, flags, dev_name, &mount_info); | ||
269 | } | ||
270 | |||
271 | static struct dentry * | ||
272 | nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags, | ||
273 | const char *dev_name, void *raw_data) | ||
274 | { | ||
275 | struct nfs_mount_info mount_info = { | ||
276 | .fill_super = nfs4_fill_super, | ||
277 | .set_security = nfs_clone_sb_security, | ||
278 | .cloned = raw_data, | ||
279 | }; | ||
280 | struct nfs_server *server; | ||
281 | struct dentry *mntroot = ERR_PTR(-ENOMEM); | ||
282 | |||
283 | dprintk("--> nfs4_referral_get_sb()\n"); | ||
284 | |||
285 | mount_info.mntfh = nfs_alloc_fhandle(); | ||
286 | if (mount_info.cloned == NULL || mount_info.mntfh == NULL) | ||
287 | goto out; | ||
288 | |||
289 | /* create a new volume representation */ | ||
290 | server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh); | ||
291 | if (IS_ERR(server)) { | ||
292 | mntroot = ERR_CAST(server); | ||
293 | goto out; | ||
294 | } | ||
295 | |||
296 | mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info); | ||
297 | out: | ||
298 | nfs_free_fhandle(mount_info.mntfh); | ||
299 | return mntroot; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Create an NFS4 server record on referral traversal | ||
304 | */ | ||
305 | static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type, | ||
306 | int flags, const char *dev_name, void *raw_data) | ||
307 | { | ||
308 | struct nfs_clone_mount *data = raw_data; | ||
309 | char *export_path; | ||
310 | struct vfsmount *root_mnt; | ||
311 | struct dentry *res; | ||
312 | |||
313 | dprintk("--> nfs4_referral_mount()\n"); | ||
314 | |||
315 | export_path = data->mnt_path; | ||
316 | data->mnt_path = "/"; | ||
317 | |||
318 | root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type, | ||
319 | flags, data, data->hostname); | ||
320 | data->mnt_path = export_path; | ||
321 | |||
322 | res = nfs_follow_remote_path(root_mnt, export_path); | ||
323 | dprintk("<-- nfs4_referral_mount() = %ld%s\n", | ||
324 | IS_ERR(res) ? PTR_ERR(res) : 0, | ||
325 | IS_ERR(res) ? " [error]" : ""); | ||
326 | return res; | ||
327 | } | ||
328 | |||
329 | |||
9 | int __init init_nfs_v4(void) | 330 | int __init init_nfs_v4(void) |
10 | { | 331 | { |
11 | int err; | 332 | int err; |
@@ -18,7 +339,13 @@ int __init init_nfs_v4(void) | |||
18 | if (err) | 339 | if (err) |
19 | goto out1; | 340 | goto out1; |
20 | 341 | ||
342 | err = register_filesystem(&nfs4_fs_type); | ||
343 | if (err < 0) | ||
344 | goto out2; | ||
345 | |||
21 | return 0; | 346 | return 0; |
347 | out2: | ||
348 | nfs4_unregister_sysctl(); | ||
22 | out1: | 349 | out1: |
23 | nfs_idmap_quit(); | 350 | nfs_idmap_quit(); |
24 | out: | 351 | out: |
@@ -27,6 +354,7 @@ out: | |||
27 | 354 | ||
28 | void __exit exit_nfs_v4(void) | 355 | void __exit exit_nfs_v4(void) |
29 | { | 356 | { |
357 | unregister_filesystem(&nfs4_fs_type); | ||
30 | nfs4_unregister_sysctl(); | 358 | nfs4_unregister_sysctl(); |
31 | nfs_idmap_quit(); | 359 | nfs_idmap_quit(); |
32 | } | 360 | } |