]>
Commit | Line | Data |
---|---|---|
ec26815a DH |
1 | /* AFS superblock handling |
2 | * | |
08e0e7c8 | 3 | * Copyright (c) 2002, 2007 Red Hat, Inc. All rights reserved. |
1da177e4 LT |
4 | * |
5 | * This software may be freely redistributed under the terms of the | |
6 | * GNU General Public License. | |
7 | * | |
8 | * You should have received a copy of the GNU General Public License | |
9 | * along with this program; if not, write to the Free Software | |
10 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
11 | * | |
12 | * Authors: David Howells <[email protected]> | |
ec26815a | 13 | * David Woodhouse <[email protected]> |
1da177e4 LT |
14 | * |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/module.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/fs.h> | |
22 | #include <linux/pagemap.h> | |
1da177e4 LT |
23 | #include "internal.h" |
24 | ||
25 | #define AFS_FS_MAGIC 0x6B414653 /* 'kAFS' */ | |
26 | ||
27 | struct afs_mount_params { | |
28 | int rwpath; | |
29 | struct afs_cell *default_cell; | |
30 | struct afs_volume *volume; | |
31 | }; | |
32 | ||
e18b890b | 33 | static void afs_i_init_once(void *foo, struct kmem_cache *cachep, |
1da177e4 LT |
34 | unsigned long flags); |
35 | ||
454e2398 DH |
36 | static int afs_get_sb(struct file_system_type *fs_type, |
37 | int flags, const char *dev_name, | |
38 | void *data, struct vfsmount *mnt); | |
1da177e4 LT |
39 | |
40 | static struct inode *afs_alloc_inode(struct super_block *sb); | |
41 | ||
42 | static void afs_put_super(struct super_block *sb); | |
43 | ||
44 | static void afs_destroy_inode(struct inode *inode); | |
45 | ||
1f5ce9e9 | 46 | struct file_system_type afs_fs_type = { |
1da177e4 LT |
47 | .owner = THIS_MODULE, |
48 | .name = "afs", | |
49 | .get_sb = afs_get_sb, | |
50 | .kill_sb = kill_anon_super, | |
51 | .fs_flags = FS_BINARY_MOUNTDATA, | |
52 | }; | |
53 | ||
ee9b6d61 | 54 | static const struct super_operations afs_super_ops = { |
1da177e4 LT |
55 | .statfs = simple_statfs, |
56 | .alloc_inode = afs_alloc_inode, | |
57 | .drop_inode = generic_delete_inode, | |
58 | .destroy_inode = afs_destroy_inode, | |
59 | .clear_inode = afs_clear_inode, | |
08e0e7c8 | 60 | .umount_begin = afs_umount_begin, |
1da177e4 LT |
61 | .put_super = afs_put_super, |
62 | }; | |
63 | ||
e18b890b | 64 | static struct kmem_cache *afs_inode_cachep; |
1da177e4 LT |
65 | static atomic_t afs_count_active_inodes; |
66 | ||
1da177e4 LT |
67 | /* |
68 | * initialise the filesystem | |
69 | */ | |
70 | int __init afs_fs_init(void) | |
71 | { | |
72 | int ret; | |
73 | ||
74 | _enter(""); | |
75 | ||
1da177e4 LT |
76 | /* create ourselves an inode cache */ |
77 | atomic_set(&afs_count_active_inodes, 0); | |
78 | ||
79 | ret = -ENOMEM; | |
80 | afs_inode_cachep = kmem_cache_create("afs_inode_cache", | |
81 | sizeof(struct afs_vnode), | |
82 | 0, | |
83 | SLAB_HWCACHE_ALIGN, | |
84 | afs_i_init_once, | |
85 | NULL); | |
86 | if (!afs_inode_cachep) { | |
87 | printk(KERN_NOTICE "kAFS: Failed to allocate inode cache\n"); | |
88 | return ret; | |
89 | } | |
90 | ||
91 | /* now export our filesystem to lesser mortals */ | |
92 | ret = register_filesystem(&afs_fs_type); | |
93 | if (ret < 0) { | |
94 | kmem_cache_destroy(afs_inode_cachep); | |
08e0e7c8 | 95 | _leave(" = %d", ret); |
1da177e4 LT |
96 | return ret; |
97 | } | |
98 | ||
08e0e7c8 | 99 | _leave(" = 0"); |
1da177e4 | 100 | return 0; |
ec26815a | 101 | } |
1da177e4 | 102 | |
1da177e4 LT |
103 | /* |
104 | * clean up the filesystem | |
105 | */ | |
106 | void __exit afs_fs_exit(void) | |
107 | { | |
08e0e7c8 DH |
108 | _enter(""); |
109 | ||
110 | afs_mntpt_kill_timer(); | |
1da177e4 LT |
111 | unregister_filesystem(&afs_fs_type); |
112 | ||
113 | if (atomic_read(&afs_count_active_inodes) != 0) { | |
114 | printk("kAFS: %d active inode objects still present\n", | |
115 | atomic_read(&afs_count_active_inodes)); | |
116 | BUG(); | |
117 | } | |
118 | ||
119 | kmem_cache_destroy(afs_inode_cachep); | |
08e0e7c8 | 120 | _leave(""); |
ec26815a | 121 | } |
1da177e4 | 122 | |
1da177e4 LT |
123 | /* |
124 | * check that an argument has a value | |
125 | */ | |
126 | static int want_arg(char **_value, const char *option) | |
127 | { | |
128 | if (!_value || !*_value || !**_value) { | |
129 | printk(KERN_NOTICE "kAFS: %s: argument missing\n", option); | |
130 | return 0; | |
131 | } | |
132 | return 1; | |
ec26815a | 133 | } |
1da177e4 | 134 | |
1da177e4 LT |
135 | /* |
136 | * check that there's no subsequent value | |
137 | */ | |
138 | static int want_no_value(char *const *_value, const char *option) | |
139 | { | |
140 | if (*_value && **_value) { | |
141 | printk(KERN_NOTICE "kAFS: %s: Invalid argument: %s\n", | |
142 | option, *_value); | |
143 | return 0; | |
144 | } | |
145 | return 1; | |
ec26815a | 146 | } |
1da177e4 | 147 | |
1da177e4 LT |
148 | /* |
149 | * parse the mount options | |
150 | * - this function has been shamelessly adapted from the ext3 fs which | |
151 | * shamelessly adapted it from the msdos fs | |
152 | */ | |
153 | static int afs_super_parse_options(struct afs_mount_params *params, | |
08e0e7c8 | 154 | char *options, const char **devname) |
1da177e4 | 155 | { |
08e0e7c8 | 156 | struct afs_cell *cell; |
1da177e4 LT |
157 | char *key, *value; |
158 | int ret; | |
159 | ||
160 | _enter("%s", options); | |
161 | ||
162 | options[PAGE_SIZE - 1] = 0; | |
163 | ||
164 | ret = 0; | |
08e0e7c8 | 165 | while ((key = strsep(&options, ","))) { |
1da177e4 LT |
166 | value = strchr(key, '='); |
167 | if (value) | |
168 | *value++ = 0; | |
169 | ||
08e0e7c8 | 170 | _debug("kAFS: KEY: %s, VAL:%s", key, value ?: "-"); |
1da177e4 LT |
171 | |
172 | if (strcmp(key, "rwpath") == 0) { | |
173 | if (!want_no_value(&value, "rwpath")) | |
174 | return -EINVAL; | |
175 | params->rwpath = 1; | |
ec26815a | 176 | } else if (strcmp(key, "vol") == 0) { |
1da177e4 LT |
177 | if (!want_arg(&value, "vol")) |
178 | return -EINVAL; | |
179 | *devname = value; | |
ec26815a | 180 | } else if (strcmp(key, "cell") == 0) { |
1da177e4 LT |
181 | if (!want_arg(&value, "cell")) |
182 | return -EINVAL; | |
08e0e7c8 DH |
183 | cell = afs_cell_lookup(value, strlen(value)); |
184 | if (IS_ERR(cell)) | |
185 | return PTR_ERR(cell); | |
1da177e4 | 186 | afs_put_cell(params->default_cell); |
08e0e7c8 DH |
187 | params->default_cell = cell; |
188 | } else { | |
189 | printk("kAFS: Unknown mount option: '%s'\n", key); | |
190 | ret = -EINVAL; | |
191 | goto error; | |
1da177e4 | 192 | } |
1da177e4 LT |
193 | } |
194 | ||
195 | ret = 0; | |
ec26815a | 196 | error: |
1da177e4 LT |
197 | _leave(" = %d", ret); |
198 | return ret; | |
ec26815a | 199 | } |
1da177e4 | 200 | |
1da177e4 LT |
201 | /* |
202 | * check a superblock to see if it's the one we're looking for | |
203 | */ | |
204 | static int afs_test_super(struct super_block *sb, void *data) | |
205 | { | |
206 | struct afs_mount_params *params = data; | |
207 | struct afs_super_info *as = sb->s_fs_info; | |
208 | ||
209 | return as->volume == params->volume; | |
ec26815a | 210 | } |
1da177e4 | 211 | |
1da177e4 LT |
212 | /* |
213 | * fill in the superblock | |
214 | */ | |
215 | static int afs_fill_super(struct super_block *sb, void *data, int silent) | |
216 | { | |
217 | struct afs_mount_params *params = data; | |
218 | struct afs_super_info *as = NULL; | |
219 | struct afs_fid fid; | |
220 | struct dentry *root = NULL; | |
221 | struct inode *inode = NULL; | |
222 | int ret; | |
223 | ||
08e0e7c8 | 224 | _enter(""); |
1da177e4 LT |
225 | |
226 | /* allocate a superblock info record */ | |
b593e48d | 227 | as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); |
1da177e4 LT |
228 | if (!as) { |
229 | _leave(" = -ENOMEM"); | |
230 | return -ENOMEM; | |
231 | } | |
232 | ||
1da177e4 LT |
233 | afs_get_volume(params->volume); |
234 | as->volume = params->volume; | |
235 | ||
236 | /* fill in the superblock */ | |
237 | sb->s_blocksize = PAGE_CACHE_SIZE; | |
238 | sb->s_blocksize_bits = PAGE_CACHE_SHIFT; | |
239 | sb->s_magic = AFS_FS_MAGIC; | |
240 | sb->s_op = &afs_super_ops; | |
241 | sb->s_fs_info = as; | |
242 | ||
243 | /* allocate the root inode and dentry */ | |
244 | fid.vid = as->volume->vid; | |
245 | fid.vnode = 1; | |
246 | fid.unique = 1; | |
08e0e7c8 DH |
247 | inode = afs_iget(sb, &fid); |
248 | if (IS_ERR(inode)) | |
249 | goto error_inode; | |
1da177e4 LT |
250 | |
251 | ret = -ENOMEM; | |
252 | root = d_alloc_root(inode); | |
253 | if (!root) | |
254 | goto error; | |
255 | ||
256 | sb->s_root = root; | |
257 | ||
08e0e7c8 | 258 | _leave(" = 0"); |
1da177e4 LT |
259 | return 0; |
260 | ||
08e0e7c8 DH |
261 | error_inode: |
262 | ret = PTR_ERR(inode); | |
263 | inode = NULL; | |
ec26815a | 264 | error: |
1da177e4 LT |
265 | iput(inode); |
266 | afs_put_volume(as->volume); | |
267 | kfree(as); | |
268 | ||
269 | sb->s_fs_info = NULL; | |
270 | ||
08e0e7c8 | 271 | _leave(" = %d", ret); |
1da177e4 | 272 | return ret; |
ec26815a | 273 | } |
1da177e4 | 274 | |
1da177e4 LT |
275 | /* |
276 | * get an AFS superblock | |
277 | * - TODO: don't use get_sb_nodev(), but rather call sget() directly | |
278 | */ | |
454e2398 DH |
279 | static int afs_get_sb(struct file_system_type *fs_type, |
280 | int flags, | |
281 | const char *dev_name, | |
282 | void *options, | |
283 | struct vfsmount *mnt) | |
1da177e4 LT |
284 | { |
285 | struct afs_mount_params params; | |
286 | struct super_block *sb; | |
08e0e7c8 | 287 | struct afs_volume *vol; |
1da177e4 LT |
288 | int ret; |
289 | ||
290 | _enter(",,%s,%p", dev_name, options); | |
291 | ||
292 | memset(¶ms, 0, sizeof(params)); | |
293 | ||
1da177e4 LT |
294 | /* parse the options */ |
295 | if (options) { | |
296 | ret = afs_super_parse_options(¶ms, options, &dev_name); | |
297 | if (ret < 0) | |
298 | goto error; | |
299 | if (!dev_name) { | |
300 | printk("kAFS: no volume name specified\n"); | |
301 | ret = -EINVAL; | |
302 | goto error; | |
303 | } | |
304 | } | |
305 | ||
306 | /* parse the device name */ | |
08e0e7c8 DH |
307 | vol = afs_volume_lookup(dev_name, params.default_cell, params.rwpath); |
308 | if (IS_ERR(vol)) { | |
309 | ret = PTR_ERR(vol); | |
1da177e4 | 310 | goto error; |
08e0e7c8 DH |
311 | } |
312 | ||
313 | params.volume = vol; | |
1da177e4 LT |
314 | |
315 | /* allocate a deviceless superblock */ | |
316 | sb = sget(fs_type, afs_test_super, set_anon_super, ¶ms); | |
08e0e7c8 DH |
317 | if (IS_ERR(sb)) { |
318 | ret = PTR_ERR(sb); | |
1da177e4 | 319 | goto error; |
08e0e7c8 | 320 | } |
1da177e4 LT |
321 | |
322 | sb->s_flags = flags; | |
323 | ||
9b04c997 | 324 | ret = afs_fill_super(sb, ¶ms, flags & MS_SILENT ? 1 : 0); |
1da177e4 LT |
325 | if (ret < 0) { |
326 | up_write(&sb->s_umount); | |
327 | deactivate_super(sb); | |
328 | goto error; | |
329 | } | |
330 | sb->s_flags |= MS_ACTIVE; | |
454e2398 | 331 | simple_set_mnt(mnt, sb); |
1da177e4 LT |
332 | |
333 | afs_put_volume(params.volume); | |
334 | afs_put_cell(params.default_cell); | |
08e0e7c8 | 335 | _leave(" = 0 [%p]", sb); |
454e2398 | 336 | return 0; |
1da177e4 | 337 | |
ec26815a | 338 | error: |
1da177e4 LT |
339 | afs_put_volume(params.volume); |
340 | afs_put_cell(params.default_cell); | |
1da177e4 | 341 | _leave(" = %d", ret); |
454e2398 | 342 | return ret; |
ec26815a | 343 | } |
1da177e4 | 344 | |
1da177e4 LT |
345 | /* |
346 | * finish the unmounting process on the superblock | |
347 | */ | |
348 | static void afs_put_super(struct super_block *sb) | |
349 | { | |
350 | struct afs_super_info *as = sb->s_fs_info; | |
351 | ||
352 | _enter(""); | |
353 | ||
354 | afs_put_volume(as->volume); | |
1da177e4 LT |
355 | |
356 | _leave(""); | |
ec26815a | 357 | } |
1da177e4 | 358 | |
1da177e4 LT |
359 | /* |
360 | * initialise an inode cache slab element prior to any use | |
361 | */ | |
e18b890b | 362 | static void afs_i_init_once(void *_vnode, struct kmem_cache *cachep, |
1da177e4 LT |
363 | unsigned long flags) |
364 | { | |
ec26815a | 365 | struct afs_vnode *vnode = _vnode; |
1da177e4 LT |
366 | |
367 | if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == | |
368 | SLAB_CTOR_CONSTRUCTOR) { | |
369 | memset(vnode, 0, sizeof(*vnode)); | |
370 | inode_init_once(&vnode->vfs_inode); | |
371 | init_waitqueue_head(&vnode->update_waitq); | |
372 | spin_lock_init(&vnode->lock); | |
08e0e7c8 DH |
373 | INIT_WORK(&vnode->cb_broken_work, afs_broken_callback_work); |
374 | mutex_init(&vnode->cb_broken_lock); | |
1da177e4 | 375 | } |
ec26815a | 376 | } |
1da177e4 | 377 | |
1da177e4 LT |
378 | /* |
379 | * allocate an AFS inode struct from our slab cache | |
380 | */ | |
381 | static struct inode *afs_alloc_inode(struct super_block *sb) | |
382 | { | |
383 | struct afs_vnode *vnode; | |
384 | ||
ec26815a | 385 | vnode = kmem_cache_alloc(afs_inode_cachep, GFP_KERNEL); |
1da177e4 LT |
386 | if (!vnode) |
387 | return NULL; | |
388 | ||
389 | atomic_inc(&afs_count_active_inodes); | |
390 | ||
391 | memset(&vnode->fid, 0, sizeof(vnode->fid)); | |
392 | memset(&vnode->status, 0, sizeof(vnode->status)); | |
393 | ||
394 | vnode->volume = NULL; | |
395 | vnode->update_cnt = 0; | |
396 | vnode->flags = 0; | |
08e0e7c8 | 397 | vnode->cb_promised = false; |
1da177e4 LT |
398 | |
399 | return &vnode->vfs_inode; | |
ec26815a | 400 | } |
1da177e4 | 401 | |
1da177e4 LT |
402 | /* |
403 | * destroy an AFS inode struct | |
404 | */ | |
405 | static void afs_destroy_inode(struct inode *inode) | |
406 | { | |
08e0e7c8 DH |
407 | struct afs_vnode *vnode = AFS_FS_I(inode); |
408 | ||
1da177e4 LT |
409 | _enter("{%lu}", inode->i_ino); |
410 | ||
08e0e7c8 DH |
411 | _debug("DESTROY INODE %p", inode); |
412 | ||
413 | ASSERTCMP(vnode->server, ==, NULL); | |
414 | ||
415 | kmem_cache_free(afs_inode_cachep, vnode); | |
1da177e4 | 416 | atomic_dec(&afs_count_active_inodes); |
ec26815a | 417 | } |