diff options
author | Kai Bankett <chaosman@ontika.net> | 2012-02-16 23:59:20 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-03-20 21:29:38 -0400 |
commit | 5d026c7242201e7c9d0e12fcb2bcaffead9d59fd (patch) | |
tree | 3a3663fd35e3077574e30cf705a614ede69925eb /fs/qnx6/super_mmi.c | |
parent | 516cdb68e5b44ca1bef31619f5da8d5e9e298f88 (diff) |
fs: initial qnx6fs addition
Adds support for qnx6fs readonly support to the linux kernel.
* Mount option
The option mmi_fs can be used to mount Harman Becker/Audi MMI 3G
HDD qnx6fs filesystems.
* Documentation
A high level filesystem stucture description can be found in the
Documentation/filesystems directory. (qnx6.txt)
* Additional features
- Active (stable) superblock selection
- Superblock checksum check (enforced)
- Supports mount of qnx6 filesystems with to host different endianess
- Automatic endianess detection
- Longfilename support (with non-enfocing crc check)
- All blocksizes (512, 1024, 2048 and 4096 supported)
Signed-off-by: Kai Bankett <chaosman@ontika.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/qnx6/super_mmi.c')
-rw-r--r-- | fs/qnx6/super_mmi.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/fs/qnx6/super_mmi.c b/fs/qnx6/super_mmi.c new file mode 100644 index 000000000000..29c32cba62d6 --- /dev/null +++ b/fs/qnx6/super_mmi.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * QNX6 file system, Linux implementation. | ||
3 | * | ||
4 | * Version : 1.0.0 | ||
5 | * | ||
6 | * History : | ||
7 | * | ||
8 | * 01-02-2012 by Kai Bankett (chaosman@ontika.net) : first release. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/buffer_head.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/crc32.h> | ||
15 | #include "qnx6.h" | ||
16 | |||
17 | static void qnx6_mmi_copy_sb(struct qnx6_super_block *qsb, | ||
18 | struct qnx6_mmi_super_block *sb) | ||
19 | { | ||
20 | qsb->sb_magic = sb->sb_magic; | ||
21 | qsb->sb_checksum = sb->sb_checksum; | ||
22 | qsb->sb_serial = sb->sb_serial; | ||
23 | qsb->sb_blocksize = sb->sb_blocksize; | ||
24 | qsb->sb_num_inodes = sb->sb_num_inodes; | ||
25 | qsb->sb_free_inodes = sb->sb_free_inodes; | ||
26 | qsb->sb_num_blocks = sb->sb_num_blocks; | ||
27 | qsb->sb_free_blocks = sb->sb_free_blocks; | ||
28 | |||
29 | /* the rest of the superblock is the same */ | ||
30 | memcpy(&qsb->Inode, &sb->Inode, sizeof(sb->Inode)); | ||
31 | memcpy(&qsb->Bitmap, &sb->Bitmap, sizeof(sb->Bitmap)); | ||
32 | memcpy(&qsb->Longfile, &sb->Longfile, sizeof(sb->Longfile)); | ||
33 | } | ||
34 | |||
35 | struct qnx6_super_block *qnx6_mmi_fill_super(struct super_block *s, int silent) | ||
36 | { | ||
37 | struct buffer_head *bh1, *bh2 = NULL; | ||
38 | struct qnx6_mmi_super_block *sb1, *sb2; | ||
39 | struct qnx6_super_block *qsb = NULL; | ||
40 | struct qnx6_sb_info *sbi; | ||
41 | __u64 offset; | ||
42 | |||
43 | /* Check the superblock signatures | ||
44 | start with the first superblock */ | ||
45 | bh1 = sb_bread(s, 0); | ||
46 | if (!bh1) { | ||
47 | printk(KERN_ERR "qnx6: Unable to read first mmi superblock\n"); | ||
48 | return NULL; | ||
49 | } | ||
50 | sb1 = (struct qnx6_mmi_super_block *)bh1->b_data; | ||
51 | sbi = QNX6_SB(s); | ||
52 | if (fs32_to_cpu(sbi, sb1->sb_magic) != QNX6_SUPER_MAGIC) { | ||
53 | if (!silent) { | ||
54 | printk(KERN_ERR "qnx6: wrong signature (magic) in" | ||
55 | " superblock #1.\n"); | ||
56 | goto out; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /* checksum check - start at byte 8 and end at byte 512 */ | ||
61 | if (fs32_to_cpu(sbi, sb1->sb_checksum) != | ||
62 | crc32_be(0, (char *)(bh1->b_data + 8), 504)) { | ||
63 | printk(KERN_ERR "qnx6: superblock #1 checksum error\n"); | ||
64 | goto out; | ||
65 | } | ||
66 | |||
67 | /* calculate second superblock blocknumber */ | ||
68 | offset = fs32_to_cpu(sbi, sb1->sb_num_blocks) + QNX6_SUPERBLOCK_AREA / | ||
69 | fs32_to_cpu(sbi, sb1->sb_blocksize); | ||
70 | |||
71 | /* set new blocksize */ | ||
72 | if (!sb_set_blocksize(s, fs32_to_cpu(sbi, sb1->sb_blocksize))) { | ||
73 | printk(KERN_ERR "qnx6: unable to set blocksize\n"); | ||
74 | goto out; | ||
75 | } | ||
76 | /* blocksize invalidates bh - pull it back in */ | ||
77 | brelse(bh1); | ||
78 | bh1 = sb_bread(s, 0); | ||
79 | if (!bh1) | ||
80 | goto out; | ||
81 | sb1 = (struct qnx6_mmi_super_block *)bh1->b_data; | ||
82 | |||
83 | /* read second superblock */ | ||
84 | bh2 = sb_bread(s, offset); | ||
85 | if (!bh2) { | ||
86 | printk(KERN_ERR "qnx6: unable to read the second superblock\n"); | ||
87 | goto out; | ||
88 | } | ||
89 | sb2 = (struct qnx6_mmi_super_block *)bh2->b_data; | ||
90 | if (fs32_to_cpu(sbi, sb2->sb_magic) != QNX6_SUPER_MAGIC) { | ||
91 | if (!silent) | ||
92 | printk(KERN_ERR "qnx6: wrong signature (magic) in" | ||
93 | " superblock #2.\n"); | ||
94 | goto out; | ||
95 | } | ||
96 | |||
97 | /* checksum check - start at byte 8 and end at byte 512 */ | ||
98 | if (fs32_to_cpu(sbi, sb2->sb_checksum) | ||
99 | != crc32_be(0, (char *)(bh2->b_data + 8), 504)) { | ||
100 | printk(KERN_ERR "qnx6: superblock #1 checksum error\n"); | ||
101 | goto out; | ||
102 | } | ||
103 | |||
104 | qsb = kmalloc(sizeof(*qsb), GFP_KERNEL); | ||
105 | if (!qsb) { | ||
106 | printk(KERN_ERR "qnx6: unable to allocate memory.\n"); | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | if (fs64_to_cpu(sbi, sb1->sb_serial) > | ||
111 | fs64_to_cpu(sbi, sb2->sb_serial)) { | ||
112 | /* superblock #1 active */ | ||
113 | qnx6_mmi_copy_sb(qsb, sb1); | ||
114 | #ifdef CONFIG_QNX6FS_DEBUG | ||
115 | qnx6_superblock_debug(qsb, s); | ||
116 | #endif | ||
117 | memcpy(bh1->b_data, qsb, sizeof(struct qnx6_super_block)); | ||
118 | |||
119 | sbi->sb_buf = bh1; | ||
120 | sbi->sb = (struct qnx6_super_block *)bh1->b_data; | ||
121 | brelse(bh2); | ||
122 | printk(KERN_INFO "qnx6: superblock #1 active\n"); | ||
123 | } else { | ||
124 | /* superblock #2 active */ | ||
125 | qnx6_mmi_copy_sb(qsb, sb2); | ||
126 | #ifdef CONFIG_QNX6FS_DEBUG | ||
127 | qnx6_superblock_debug(qsb, s); | ||
128 | #endif | ||
129 | memcpy(bh2->b_data, qsb, sizeof(struct qnx6_super_block)); | ||
130 | |||
131 | sbi->sb_buf = bh2; | ||
132 | sbi->sb = (struct qnx6_super_block *)bh2->b_data; | ||
133 | brelse(bh1); | ||
134 | printk(KERN_INFO "qnx6: superblock #2 active\n"); | ||
135 | } | ||
136 | kfree(qsb); | ||
137 | |||
138 | /* offset for mmi_fs is just SUPERBLOCK_AREA bytes */ | ||
139 | sbi->s_blks_off = QNX6_SUPERBLOCK_AREA / s->s_blocksize; | ||
140 | |||
141 | /* success */ | ||
142 | return sbi->sb; | ||
143 | |||
144 | out: | ||
145 | if (bh1 != NULL) | ||
146 | brelse(bh1); | ||
147 | if (bh2 != NULL) | ||
148 | brelse(bh2); | ||
149 | return NULL; | ||
150 | } | ||