1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Optimized MPEG FS - inode and super operations. |
4 | * Copyright (C) 2006 Bob Copeland <me@bobcopeland.com> |
5 | */ |
6 | #include <linux/module.h> |
7 | #include <linux/sched.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/fs.h> |
10 | #include <linux/vfs.h> |
11 | #include <linux/cred.h> |
12 | #include <linux/parser.h> |
13 | #include <linux/buffer_head.h> |
14 | #include <linux/vmalloc.h> |
15 | #include <linux/writeback.h> |
16 | #include <linux/seq_file.h> |
17 | #include <linux/crc-itu-t.h> |
18 | #include "omfs.h" |
19 | |
20 | MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>" ); |
21 | MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux" ); |
22 | MODULE_LICENSE("GPL" ); |
23 | |
24 | struct buffer_head *omfs_bread(struct super_block *sb, sector_t block) |
25 | { |
26 | struct omfs_sb_info *sbi = OMFS_SB(sb); |
27 | if (block >= sbi->s_num_blocks) |
28 | return NULL; |
29 | |
30 | return sb_bread(sb, block: clus_to_blk(sbi, block)); |
31 | } |
32 | |
33 | struct inode *omfs_new_inode(struct inode *dir, umode_t mode) |
34 | { |
35 | struct inode *inode; |
36 | u64 new_block; |
37 | int err; |
38 | int len; |
39 | struct omfs_sb_info *sbi = OMFS_SB(sb: dir->i_sb); |
40 | |
41 | inode = new_inode(sb: dir->i_sb); |
42 | if (!inode) |
43 | return ERR_PTR(error: -ENOMEM); |
44 | |
45 | err = omfs_allocate_range(sb: dir->i_sb, min_request: sbi->s_mirrors, max_request: sbi->s_mirrors, |
46 | return_block: &new_block, return_size: &len); |
47 | if (err) |
48 | goto fail; |
49 | |
50 | inode->i_ino = new_block; |
51 | inode_init_owner(idmap: &nop_mnt_idmap, inode, NULL, mode); |
52 | inode->i_mapping->a_ops = &omfs_aops; |
53 | |
54 | simple_inode_init_ts(inode); |
55 | switch (mode & S_IFMT) { |
56 | case S_IFDIR: |
57 | inode->i_op = &omfs_dir_inops; |
58 | inode->i_fop = &omfs_dir_operations; |
59 | inode->i_size = sbi->s_sys_blocksize; |
60 | inc_nlink(inode); |
61 | break; |
62 | case S_IFREG: |
63 | inode->i_op = &omfs_file_inops; |
64 | inode->i_fop = &omfs_file_operations; |
65 | inode->i_size = 0; |
66 | break; |
67 | } |
68 | |
69 | insert_inode_hash(inode); |
70 | mark_inode_dirty(inode); |
71 | return inode; |
72 | fail: |
73 | make_bad_inode(inode); |
74 | iput(inode); |
75 | return ERR_PTR(error: err); |
76 | } |
77 | |
78 | /* |
79 | * Update the header checksums for a dirty inode based on its contents. |
80 | * Caller is expected to hold the buffer head underlying oi and mark it |
81 | * dirty. |
82 | */ |
83 | static void omfs_update_checksums(struct omfs_inode *oi) |
84 | { |
85 | int xor, i, ofs = 0, count; |
86 | u16 crc = 0; |
87 | unsigned char *ptr = (unsigned char *) oi; |
88 | |
89 | count = be32_to_cpu(oi->i_head.h_body_size); |
90 | ofs = sizeof(struct omfs_header); |
91 | |
92 | crc = crc_itu_t(crc, buffer: ptr + ofs, len: count); |
93 | oi->i_head.h_crc = cpu_to_be16(crc); |
94 | |
95 | xor = ptr[0]; |
96 | for (i = 1; i < OMFS_XOR_COUNT; i++) |
97 | xor ^= ptr[i]; |
98 | |
99 | oi->i_head.h_check_xor = xor; |
100 | } |
101 | |
102 | static int __omfs_write_inode(struct inode *inode, int wait) |
103 | { |
104 | struct omfs_inode *oi; |
105 | struct omfs_sb_info *sbi = OMFS_SB(sb: inode->i_sb); |
106 | struct buffer_head *bh, *bh2; |
107 | u64 ctime; |
108 | int i; |
109 | int ret = -EIO; |
110 | int sync_failed = 0; |
111 | |
112 | /* get current inode since we may have written sibling ptrs etc. */ |
113 | bh = omfs_bread(sb: inode->i_sb, block: inode->i_ino); |
114 | if (!bh) |
115 | goto out; |
116 | |
117 | oi = (struct omfs_inode *) bh->b_data; |
118 | |
119 | oi->i_head.h_self = cpu_to_be64(inode->i_ino); |
120 | if (S_ISDIR(inode->i_mode)) |
121 | oi->i_type = OMFS_DIR; |
122 | else if (S_ISREG(inode->i_mode)) |
123 | oi->i_type = OMFS_FILE; |
124 | else { |
125 | printk(KERN_WARNING "omfs: unknown file type: %d\n" , |
126 | inode->i_mode); |
127 | goto out_brelse; |
128 | } |
129 | |
130 | oi->i_head.h_body_size = cpu_to_be32(sbi->s_sys_blocksize - |
131 | sizeof(struct omfs_header)); |
132 | oi->i_head.h_version = 1; |
133 | oi->i_head.h_type = OMFS_INODE_NORMAL; |
134 | oi->i_head.h_magic = OMFS_IMAGIC; |
135 | oi->i_size = cpu_to_be64(inode->i_size); |
136 | |
137 | ctime = inode_get_ctime_sec(inode) * 1000LL + |
138 | ((inode_get_ctime_nsec(inode) + 999)/1000); |
139 | oi->i_ctime = cpu_to_be64(ctime); |
140 | |
141 | omfs_update_checksums(oi); |
142 | |
143 | mark_buffer_dirty(bh); |
144 | if (wait) { |
145 | sync_dirty_buffer(bh); |
146 | if (buffer_req(bh) && !buffer_uptodate(bh)) |
147 | sync_failed = 1; |
148 | } |
149 | |
150 | /* if mirroring writes, copy to next fsblock */ |
151 | for (i = 1; i < sbi->s_mirrors; i++) { |
152 | bh2 = omfs_bread(sb: inode->i_sb, block: inode->i_ino + i); |
153 | if (!bh2) |
154 | goto out_brelse; |
155 | |
156 | memcpy(bh2->b_data, bh->b_data, bh->b_size); |
157 | mark_buffer_dirty(bh: bh2); |
158 | if (wait) { |
159 | sync_dirty_buffer(bh: bh2); |
160 | if (buffer_req(bh: bh2) && !buffer_uptodate(bh: bh2)) |
161 | sync_failed = 1; |
162 | } |
163 | brelse(bh: bh2); |
164 | } |
165 | ret = (sync_failed) ? -EIO : 0; |
166 | out_brelse: |
167 | brelse(bh); |
168 | out: |
169 | return ret; |
170 | } |
171 | |
172 | static int omfs_write_inode(struct inode *inode, struct writeback_control *wbc) |
173 | { |
174 | return __omfs_write_inode(inode, wait: wbc->sync_mode == WB_SYNC_ALL); |
175 | } |
176 | |
177 | int omfs_sync_inode(struct inode *inode) |
178 | { |
179 | return __omfs_write_inode(inode, wait: 1); |
180 | } |
181 | |
182 | /* |
183 | * called when an entry is deleted, need to clear the bits in the |
184 | * bitmaps. |
185 | */ |
186 | static void omfs_evict_inode(struct inode *inode) |
187 | { |
188 | truncate_inode_pages_final(&inode->i_data); |
189 | clear_inode(inode); |
190 | |
191 | if (inode->i_nlink) |
192 | return; |
193 | |
194 | if (S_ISREG(inode->i_mode)) { |
195 | inode->i_size = 0; |
196 | omfs_shrink_inode(inode); |
197 | } |
198 | |
199 | omfs_clear_range(sb: inode->i_sb, block: inode->i_ino, count: 2); |
200 | } |
201 | |
202 | struct inode *omfs_iget(struct super_block *sb, ino_t ino) |
203 | { |
204 | struct omfs_sb_info *sbi = OMFS_SB(sb); |
205 | struct omfs_inode *oi; |
206 | struct buffer_head *bh; |
207 | u64 ctime; |
208 | unsigned long nsecs; |
209 | struct inode *inode; |
210 | |
211 | inode = iget_locked(sb, ino); |
212 | if (!inode) |
213 | return ERR_PTR(error: -ENOMEM); |
214 | if (!(inode->i_state & I_NEW)) |
215 | return inode; |
216 | |
217 | bh = omfs_bread(sb: inode->i_sb, block: ino); |
218 | if (!bh) |
219 | goto iget_failed; |
220 | |
221 | oi = (struct omfs_inode *)bh->b_data; |
222 | |
223 | /* check self */ |
224 | if (ino != be64_to_cpu(oi->i_head.h_self)) |
225 | goto fail_bh; |
226 | |
227 | inode->i_uid = sbi->s_uid; |
228 | inode->i_gid = sbi->s_gid; |
229 | |
230 | ctime = be64_to_cpu(oi->i_ctime); |
231 | nsecs = do_div(ctime, 1000) * 1000L; |
232 | |
233 | inode_set_atime(inode, sec: ctime, nsec: nsecs); |
234 | inode_set_mtime(inode, sec: ctime, nsec: nsecs); |
235 | inode_set_ctime(inode, sec: ctime, nsec: nsecs); |
236 | |
237 | inode->i_mapping->a_ops = &omfs_aops; |
238 | |
239 | switch (oi->i_type) { |
240 | case OMFS_DIR: |
241 | inode->i_mode = S_IFDIR | (S_IRWXUGO & ~sbi->s_dmask); |
242 | inode->i_op = &omfs_dir_inops; |
243 | inode->i_fop = &omfs_dir_operations; |
244 | inode->i_size = sbi->s_sys_blocksize; |
245 | inc_nlink(inode); |
246 | break; |
247 | case OMFS_FILE: |
248 | inode->i_mode = S_IFREG | (S_IRWXUGO & ~sbi->s_fmask); |
249 | inode->i_fop = &omfs_file_operations; |
250 | inode->i_size = be64_to_cpu(oi->i_size); |
251 | break; |
252 | } |
253 | brelse(bh); |
254 | unlock_new_inode(inode); |
255 | return inode; |
256 | fail_bh: |
257 | brelse(bh); |
258 | iget_failed: |
259 | iget_failed(inode); |
260 | return ERR_PTR(error: -EIO); |
261 | } |
262 | |
263 | static void omfs_put_super(struct super_block *sb) |
264 | { |
265 | struct omfs_sb_info *sbi = OMFS_SB(sb); |
266 | kfree(objp: sbi->s_imap); |
267 | kfree(objp: sbi); |
268 | sb->s_fs_info = NULL; |
269 | } |
270 | |
271 | static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf) |
272 | { |
273 | struct super_block *s = dentry->d_sb; |
274 | struct omfs_sb_info *sbi = OMFS_SB(sb: s); |
275 | u64 id = huge_encode_dev(dev: s->s_bdev->bd_dev); |
276 | |
277 | buf->f_type = OMFS_MAGIC; |
278 | buf->f_bsize = sbi->s_blocksize; |
279 | buf->f_blocks = sbi->s_num_blocks; |
280 | buf->f_files = sbi->s_num_blocks; |
281 | buf->f_namelen = OMFS_NAMELEN; |
282 | buf->f_fsid = u64_to_fsid(v: id); |
283 | |
284 | buf->f_bfree = buf->f_bavail = buf->f_ffree = |
285 | omfs_count_free(sb: s); |
286 | |
287 | return 0; |
288 | } |
289 | |
290 | /* |
291 | * Display the mount options in /proc/mounts. |
292 | */ |
293 | static int omfs_show_options(struct seq_file *m, struct dentry *root) |
294 | { |
295 | struct omfs_sb_info *sbi = OMFS_SB(sb: root->d_sb); |
296 | umode_t cur_umask = current_umask(); |
297 | |
298 | if (!uid_eq(left: sbi->s_uid, current_uid())) |
299 | seq_printf(m, fmt: ",uid=%u" , |
300 | from_kuid_munged(to: &init_user_ns, uid: sbi->s_uid)); |
301 | if (!gid_eq(left: sbi->s_gid, current_gid())) |
302 | seq_printf(m, fmt: ",gid=%u" , |
303 | from_kgid_munged(to: &init_user_ns, gid: sbi->s_gid)); |
304 | |
305 | if (sbi->s_dmask == sbi->s_fmask) { |
306 | if (sbi->s_fmask != cur_umask) |
307 | seq_printf(m, fmt: ",umask=%o" , sbi->s_fmask); |
308 | } else { |
309 | if (sbi->s_dmask != cur_umask) |
310 | seq_printf(m, fmt: ",dmask=%o" , sbi->s_dmask); |
311 | if (sbi->s_fmask != cur_umask) |
312 | seq_printf(m, fmt: ",fmask=%o" , sbi->s_fmask); |
313 | } |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static const struct super_operations omfs_sops = { |
319 | .write_inode = omfs_write_inode, |
320 | .evict_inode = omfs_evict_inode, |
321 | .put_super = omfs_put_super, |
322 | .statfs = omfs_statfs, |
323 | .show_options = omfs_show_options, |
324 | }; |
325 | |
326 | /* |
327 | * For Rio Karma, there is an on-disk free bitmap whose location is |
328 | * stored in the root block. For ReplayTV, there is no such free bitmap |
329 | * so we have to walk the tree. Both inodes and file data are allocated |
330 | * from the same map. This array can be big (300k) so we allocate |
331 | * in units of the blocksize. |
332 | */ |
333 | static int omfs_get_imap(struct super_block *sb) |
334 | { |
335 | unsigned int bitmap_size, array_size; |
336 | int count; |
337 | struct omfs_sb_info *sbi = OMFS_SB(sb); |
338 | struct buffer_head *bh; |
339 | unsigned long **ptr; |
340 | sector_t block; |
341 | |
342 | bitmap_size = DIV_ROUND_UP(sbi->s_num_blocks, 8); |
343 | array_size = DIV_ROUND_UP(bitmap_size, sb->s_blocksize); |
344 | |
345 | if (sbi->s_bitmap_ino == ~0ULL) |
346 | goto out; |
347 | |
348 | sbi->s_imap_size = array_size; |
349 | sbi->s_imap = kcalloc(n: array_size, size: sizeof(unsigned long *), GFP_KERNEL); |
350 | if (!sbi->s_imap) |
351 | goto nomem; |
352 | |
353 | block = clus_to_blk(sbi, block: sbi->s_bitmap_ino); |
354 | if (block >= sbi->s_num_blocks) |
355 | goto nomem; |
356 | |
357 | ptr = sbi->s_imap; |
358 | for (count = bitmap_size; count > 0; count -= sb->s_blocksize) { |
359 | bh = sb_bread(sb, block: block++); |
360 | if (!bh) |
361 | goto nomem_free; |
362 | *ptr = kmemdup(p: bh->b_data, size: sb->s_blocksize, GFP_KERNEL); |
363 | if (!*ptr) { |
364 | brelse(bh); |
365 | goto nomem_free; |
366 | } |
367 | if (count < sb->s_blocksize) |
368 | memset((void *)*ptr + count, 0xff, |
369 | sb->s_blocksize - count); |
370 | brelse(bh); |
371 | ptr++; |
372 | } |
373 | out: |
374 | return 0; |
375 | |
376 | nomem_free: |
377 | for (count = 0; count < array_size; count++) |
378 | kfree(objp: sbi->s_imap[count]); |
379 | |
380 | kfree(objp: sbi->s_imap); |
381 | nomem: |
382 | sbi->s_imap = NULL; |
383 | sbi->s_imap_size = 0; |
384 | return -ENOMEM; |
385 | } |
386 | |
387 | enum { |
388 | Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask, Opt_err |
389 | }; |
390 | |
391 | static const match_table_t tokens = { |
392 | {Opt_uid, "uid=%u" }, |
393 | {Opt_gid, "gid=%u" }, |
394 | {Opt_umask, "umask=%o" }, |
395 | {Opt_dmask, "dmask=%o" }, |
396 | {Opt_fmask, "fmask=%o" }, |
397 | {Opt_err, NULL}, |
398 | }; |
399 | |
400 | static int parse_options(char *options, struct omfs_sb_info *sbi) |
401 | { |
402 | char *p; |
403 | substring_t args[MAX_OPT_ARGS]; |
404 | int option; |
405 | |
406 | if (!options) |
407 | return 1; |
408 | |
409 | while ((p = strsep(&options, "," )) != NULL) { |
410 | int token; |
411 | if (!*p) |
412 | continue; |
413 | |
414 | token = match_token(p, table: tokens, args); |
415 | switch (token) { |
416 | case Opt_uid: |
417 | if (match_int(&args[0], result: &option)) |
418 | return 0; |
419 | sbi->s_uid = make_kuid(current_user_ns(), uid: option); |
420 | if (!uid_valid(uid: sbi->s_uid)) |
421 | return 0; |
422 | break; |
423 | case Opt_gid: |
424 | if (match_int(&args[0], result: &option)) |
425 | return 0; |
426 | sbi->s_gid = make_kgid(current_user_ns(), gid: option); |
427 | if (!gid_valid(gid: sbi->s_gid)) |
428 | return 0; |
429 | break; |
430 | case Opt_umask: |
431 | if (match_octal(&args[0], result: &option)) |
432 | return 0; |
433 | sbi->s_fmask = sbi->s_dmask = option; |
434 | break; |
435 | case Opt_dmask: |
436 | if (match_octal(&args[0], result: &option)) |
437 | return 0; |
438 | sbi->s_dmask = option; |
439 | break; |
440 | case Opt_fmask: |
441 | if (match_octal(&args[0], result: &option)) |
442 | return 0; |
443 | sbi->s_fmask = option; |
444 | break; |
445 | default: |
446 | return 0; |
447 | } |
448 | } |
449 | return 1; |
450 | } |
451 | |
452 | static int omfs_fill_super(struct super_block *sb, void *data, int silent) |
453 | { |
454 | struct buffer_head *bh, *bh2; |
455 | struct omfs_super_block *omfs_sb; |
456 | struct omfs_root_block *omfs_rb; |
457 | struct omfs_sb_info *sbi; |
458 | struct inode *root; |
459 | int ret = -EINVAL; |
460 | |
461 | sbi = kzalloc(size: sizeof(struct omfs_sb_info), GFP_KERNEL); |
462 | if (!sbi) |
463 | return -ENOMEM; |
464 | |
465 | sb->s_fs_info = sbi; |
466 | |
467 | sbi->s_uid = current_uid(); |
468 | sbi->s_gid = current_gid(); |
469 | sbi->s_dmask = sbi->s_fmask = current_umask(); |
470 | |
471 | if (!parse_options(options: (char *) data, sbi)) |
472 | goto end; |
473 | |
474 | sb->s_maxbytes = 0xffffffff; |
475 | |
476 | sb->s_time_gran = NSEC_PER_MSEC; |
477 | sb->s_time_min = 0; |
478 | sb->s_time_max = U64_MAX / MSEC_PER_SEC; |
479 | |
480 | sb_set_blocksize(sb, 0x200); |
481 | |
482 | bh = sb_bread(sb, block: 0); |
483 | if (!bh) |
484 | goto end; |
485 | |
486 | omfs_sb = (struct omfs_super_block *)bh->b_data; |
487 | |
488 | if (omfs_sb->s_magic != cpu_to_be32(OMFS_MAGIC)) { |
489 | if (!silent) |
490 | printk(KERN_ERR "omfs: Invalid superblock (%x)\n" , |
491 | omfs_sb->s_magic); |
492 | goto out_brelse_bh; |
493 | } |
494 | sb->s_magic = OMFS_MAGIC; |
495 | |
496 | sbi->s_num_blocks = be64_to_cpu(omfs_sb->s_num_blocks); |
497 | sbi->s_blocksize = be32_to_cpu(omfs_sb->s_blocksize); |
498 | sbi->s_mirrors = be32_to_cpu(omfs_sb->s_mirrors); |
499 | sbi->s_root_ino = be64_to_cpu(omfs_sb->s_root_block); |
500 | sbi->s_sys_blocksize = be32_to_cpu(omfs_sb->s_sys_blocksize); |
501 | mutex_init(&sbi->s_bitmap_lock); |
502 | |
503 | if (sbi->s_num_blocks > OMFS_MAX_BLOCKS) { |
504 | printk(KERN_ERR "omfs: sysblock number (%llx) is out of range\n" , |
505 | (unsigned long long)sbi->s_num_blocks); |
506 | goto out_brelse_bh; |
507 | } |
508 | |
509 | if (sbi->s_sys_blocksize > PAGE_SIZE) { |
510 | printk(KERN_ERR "omfs: sysblock size (%d) is out of range\n" , |
511 | sbi->s_sys_blocksize); |
512 | goto out_brelse_bh; |
513 | } |
514 | |
515 | if (sbi->s_blocksize < sbi->s_sys_blocksize || |
516 | sbi->s_blocksize > OMFS_MAX_BLOCK_SIZE) { |
517 | printk(KERN_ERR "omfs: block size (%d) is out of range\n" , |
518 | sbi->s_blocksize); |
519 | goto out_brelse_bh; |
520 | } |
521 | |
522 | /* |
523 | * Use sys_blocksize as the fs block since it is smaller than a |
524 | * page while the fs blocksize can be larger. |
525 | */ |
526 | sb_set_blocksize(sb, sbi->s_sys_blocksize); |
527 | |
528 | /* |
529 | * ...and the difference goes into a shift. sys_blocksize is always |
530 | * a power of two factor of blocksize. |
531 | */ |
532 | sbi->s_block_shift = get_bitmask_order(count: sbi->s_blocksize) - |
533 | get_bitmask_order(count: sbi->s_sys_blocksize); |
534 | |
535 | bh2 = omfs_bread(sb, be64_to_cpu(omfs_sb->s_root_block)); |
536 | if (!bh2) |
537 | goto out_brelse_bh; |
538 | |
539 | omfs_rb = (struct omfs_root_block *)bh2->b_data; |
540 | |
541 | sbi->s_bitmap_ino = be64_to_cpu(omfs_rb->r_bitmap); |
542 | sbi->s_clustersize = be32_to_cpu(omfs_rb->r_clustersize); |
543 | |
544 | if (sbi->s_num_blocks != be64_to_cpu(omfs_rb->r_num_blocks)) { |
545 | printk(KERN_ERR "omfs: block count discrepancy between " |
546 | "super and root blocks (%llx, %llx)\n" , |
547 | (unsigned long long)sbi->s_num_blocks, |
548 | (unsigned long long)be64_to_cpu(omfs_rb->r_num_blocks)); |
549 | goto out_brelse_bh2; |
550 | } |
551 | |
552 | if (sbi->s_bitmap_ino != ~0ULL && |
553 | sbi->s_bitmap_ino > sbi->s_num_blocks) { |
554 | printk(KERN_ERR "omfs: free space bitmap location is corrupt " |
555 | "(%llx, total blocks %llx)\n" , |
556 | (unsigned long long) sbi->s_bitmap_ino, |
557 | (unsigned long long) sbi->s_num_blocks); |
558 | goto out_brelse_bh2; |
559 | } |
560 | if (sbi->s_clustersize < 1 || |
561 | sbi->s_clustersize > OMFS_MAX_CLUSTER_SIZE) { |
562 | printk(KERN_ERR "omfs: cluster size out of range (%d)" , |
563 | sbi->s_clustersize); |
564 | goto out_brelse_bh2; |
565 | } |
566 | |
567 | ret = omfs_get_imap(sb); |
568 | if (ret) |
569 | goto out_brelse_bh2; |
570 | |
571 | sb->s_op = &omfs_sops; |
572 | |
573 | root = omfs_iget(sb, be64_to_cpu(omfs_rb->r_root_dir)); |
574 | if (IS_ERR(ptr: root)) { |
575 | ret = PTR_ERR(ptr: root); |
576 | goto out_brelse_bh2; |
577 | } |
578 | |
579 | sb->s_root = d_make_root(root); |
580 | if (!sb->s_root) { |
581 | ret = -ENOMEM; |
582 | goto out_brelse_bh2; |
583 | } |
584 | printk(KERN_DEBUG "omfs: Mounted volume %s\n" , omfs_rb->r_name); |
585 | |
586 | ret = 0; |
587 | out_brelse_bh2: |
588 | brelse(bh: bh2); |
589 | out_brelse_bh: |
590 | brelse(bh); |
591 | end: |
592 | if (ret) |
593 | kfree(objp: sbi); |
594 | return ret; |
595 | } |
596 | |
597 | static struct dentry *omfs_mount(struct file_system_type *fs_type, |
598 | int flags, const char *dev_name, void *data) |
599 | { |
600 | return mount_bdev(fs_type, flags, dev_name, data, fill_super: omfs_fill_super); |
601 | } |
602 | |
603 | static struct file_system_type omfs_fs_type = { |
604 | .owner = THIS_MODULE, |
605 | .name = "omfs" , |
606 | .mount = omfs_mount, |
607 | .kill_sb = kill_block_super, |
608 | .fs_flags = FS_REQUIRES_DEV, |
609 | }; |
610 | MODULE_ALIAS_FS("omfs" ); |
611 | |
612 | static int __init init_omfs_fs(void) |
613 | { |
614 | return register_filesystem(&omfs_fs_type); |
615 | } |
616 | |
617 | static void __exit exit_omfs_fs(void) |
618 | { |
619 | unregister_filesystem(&omfs_fs_type); |
620 | } |
621 | |
622 | module_init(init_omfs_fs); |
623 | module_exit(exit_omfs_fs); |
624 | |