]>
Commit | Line | Data |
---|---|---|
ca2634a4 JS |
1 | /* |
2 | * csum.c --- checksumming of ext3 structures | |
3 | * | |
4 | * Copyright (C) 2006 Cluster File Systems, Inc. | |
470e737a | 5 | * Copyright (C) 2006, 2007 by Andreas Dilger <adilger@clusterfs.com> |
ca2634a4 JS |
6 | * |
7 | * %Begin-Header% | |
543547a5 TT |
8 | * This file may be redistributed under the terms of the GNU Library |
9 | * General Public License, version 2. | |
ca2634a4 JS |
10 | * %End-Header% |
11 | */ | |
12 | ||
d1154eb4 | 13 | #include "config.h" |
0eeec8ac TT |
14 | #if HAVE_SYS_TYPES_H |
15 | #include <sys/types.h> | |
16 | #endif | |
17 | ||
ca2634a4 JS |
18 | #include "ext2_fs.h" |
19 | #include "ext2fs.h" | |
20 | #include "crc16.h" | |
21 | #include <assert.h> | |
22 | ||
23 | #ifndef offsetof | |
24 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | |
25 | #endif | |
26 | ||
27 | #ifdef DEBUG | |
28 | #define STATIC | |
29 | #else | |
30 | #define STATIC static | |
31 | #endif | |
32 | ||
87141781 | 33 | __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) |
ca2634a4 | 34 | { |
2bc30417 AD |
35 | struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, |
36 | group); | |
37 | size_t size = EXT2_DESC_SIZE(fs->super); | |
38 | size_t offset; | |
39 | __u16 crc; | |
ca2634a4 JS |
40 | |
41 | if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { | |
d32c915a | 42 | size_t offset = offsetof(struct ext2_group_desc, bg_checksum); |
ca2634a4 JS |
43 | |
44 | #ifdef WORDS_BIGENDIAN | |
1d18a55c | 45 | struct ext4_group_desc swabdesc; |
ca2634a4 JS |
46 | |
47 | /* Have to swab back to little-endian to do the checksum */ | |
1d18a55c TT |
48 | memcpy(&swabdesc, desc, size); |
49 | ext2fs_swap_group_desc2(fs, | |
50 | (struct ext2_group_desc *) &swabdesc); | |
51 | desc = (struct ext2_group_desc *) &swabdesc; | |
ca2634a4 JS |
52 | |
53 | group = ext2fs_swab32(group); | |
54 | #endif | |
c4dcb1c1 TT |
55 | crc = ext2fs_crc16(~0, fs->super->s_uuid, |
56 | sizeof(fs->super->s_uuid)); | |
57 | crc = ext2fs_crc16(crc, &group, sizeof(group)); | |
58 | crc = ext2fs_crc16(crc, desc, offset); | |
ca2634a4 | 59 | offset += sizeof(desc->bg_checksum); /* skip checksum */ |
ca2634a4 | 60 | /* for checksum of struct ext4_group_desc do the rest...*/ |
1d18a55c | 61 | if (offset < size) { |
c4dcb1c1 | 62 | crc = ext2fs_crc16(crc, (char *)desc + offset, |
1d18a55c | 63 | size - offset); |
ca2634a4 JS |
64 | } |
65 | } | |
66 | ||
67 | return crc; | |
68 | } | |
69 | ||
70 | int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) | |
71 | { | |
4729455f TT |
72 | if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
73 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && | |
d7cca6b0 | 74 | (ext2fs_bg_checksum(fs, group) != |
4729455f | 75 | ext2fs_group_desc_csum(fs, group))) |
ca2634a4 JS |
76 | return 0; |
77 | ||
78 | return 1; | |
79 | } | |
80 | ||
81 | void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) | |
82 | { | |
d7cca6b0 VAH |
83 | if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
84 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) | |
85 | return; | |
86 | ||
87 | /* ext2fs_bg_checksum_set() sets the actual checksum field but | |
88 | * does not calculate the checksum itself. */ | |
89 | ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group)); | |
ca2634a4 JS |
90 | } |
91 | ||
92 | static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, | |
93 | __u32 inodes_per_grp, dgrp_t grp_no) | |
94 | { | |
95 | ext2_ino_t i, start_ino, end_ino; | |
96 | ||
97 | start_ino = grp_no * inodes_per_grp + 1; | |
98 | end_ino = start_ino + inodes_per_grp - 1; | |
99 | ||
100 | for (i = end_ino; i >= start_ino; i--) { | |
8f82ef98 | 101 | if (ext2fs_fast_test_inode_bitmap2(bitmap, i)) |
ca2634a4 JS |
102 | return i - start_ino + 1; |
103 | } | |
104 | return inodes_per_grp; | |
105 | } | |
106 | ||
107 | /* update the bitmap flags, set the itable high watermark, and calculate | |
108 | * checksums for the group descriptors */ | |
f628acea | 109 | errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) |
ca2634a4 JS |
110 | { |
111 | struct ext2_super_block *sb = fs->super; | |
8895f43a | 112 | int dirty = 0; |
ca2634a4 JS |
113 | dgrp_t i; |
114 | ||
f628acea AD |
115 | if (!fs->inode_map) |
116 | return EXT2_ET_NO_INODE_BITMAP; | |
117 | ||
16b851cd TT |
118 | if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, |
119 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) | |
f628acea | 120 | return 0; |
ca2634a4 | 121 | |
d7cca6b0 | 122 | for (i = 0; i < fs->group_desc_count; i++) { |
d32c915a TT |
123 | __u32 old_csum = ext2fs_bg_checksum(fs, i); |
124 | __u32 old_unused = ext2fs_bg_itable_unused(fs, i); | |
125 | __u32 old_flags = ext2fs_bg_flags(fs, i); | |
126 | __u32 old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i); | |
ca2634a4 | 127 | |
d7cca6b0 VAH |
128 | if (old_free_inodes_count == sb->s_inodes_per_group) { |
129 | ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); | |
130 | ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group); | |
16b851cd | 131 | } else { |
d7cca6b0 VAH |
132 | int unused = |
133 | sb->s_inodes_per_group - | |
ca2634a4 | 134 | find_last_inode_ingrp(fs->inode_map, |
d7cca6b0 VAH |
135 | sb->s_inodes_per_group, i); |
136 | ||
137 | ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT); | |
138 | ext2fs_bg_itable_unused_set(fs, i, unused); | |
ca2634a4 JS |
139 | } |
140 | ||
ca2634a4 | 141 | ext2fs_group_desc_csum_set(fs, i); |
d7cca6b0 | 142 | if (old_flags != ext2fs_bg_flags(fs, i)) |
80fc4e69 | 143 | dirty = 1; |
d7cca6b0 | 144 | if (old_unused != ext2fs_bg_itable_unused(fs, i)) |
80fc4e69 | 145 | dirty = 1; |
d7cca6b0 | 146 | if (old_csum != ext2fs_bg_checksum(fs, i)) |
ca2634a4 JS |
147 | dirty = 1; |
148 | } | |
149 | if (dirty) | |
150 | ext2fs_mark_super_dirty(fs); | |
f628acea | 151 | return 0; |
ca2634a4 | 152 | } |
470e737a TT |
153 | |
154 | #ifdef DEBUG | |
c816ecb2 ES |
155 | #include "e2p/e2p.h" |
156 | ||
470e737a TT |
157 | void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) |
158 | { | |
159 | __u16 crc1, crc2, crc3; | |
160 | dgrp_t swabgroup; | |
f797cf3e | 161 | struct ext2_group_desc *desc; |
1d18a55c | 162 | size_t size; |
470e737a | 163 | struct ext2_super_block *sb = fs->super; |
1d18a55c | 164 | int offset = offsetof(struct ext2_group_desc, bg_checksum); |
470e737a | 165 | #ifdef WORDS_BIGENDIAN |
1d18a55c TT |
166 | struct ext4_group_desc swabdesc; |
167 | #endif | |
470e737a | 168 | |
f797cf3e | 169 | desc = ext2fs_group_desc(fs, fs->group_desc, group); |
2bc30417 | 170 | size = EXT2_DESC_SIZE(fs->super); |
1d18a55c | 171 | #ifdef WORDS_BIGENDIAN |
470e737a | 172 | /* Have to swab back to little-endian to do the checksum */ |
1d18a55c TT |
173 | memcpy(&swabdesc, desc, size); |
174 | ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); | |
175 | desc = (struct ext2_group_desc *) &swabdesc; | |
470e737a TT |
176 | |
177 | swabgroup = ext2fs_swab32(group); | |
178 | #else | |
179 | swabgroup = group; | |
180 | #endif | |
181 | ||
182 | crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); | |
183 | crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); | |
1d18a55c TT |
184 | crc3 = ext2fs_crc16(crc2, desc, offset); |
185 | offset += sizeof(desc->bg_checksum); /* skip checksum */ | |
186 | /* for checksum of struct ext4_group_desc do the rest...*/ | |
187 | if (offset < size) | |
188 | crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); | |
189 | ||
f797cf3e | 190 | printf("%s UUID %s=%04x, grp %u=%04x: %04x=%04x\n", |
562f2642 | 191 | msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, |
c816ecb2 | 192 | ext2fs_group_desc_csum(fs, group)); |
470e737a TT |
193 | } |
194 | ||
195 | unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, | |
196 | 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; | |
197 | ||
198 | int main(int argc, char **argv) | |
199 | { | |
200 | struct ext2_super_block param; | |
201 | errcode_t retval; | |
202 | ext2_filsys fs; | |
203 | int i; | |
204 | __u16 csum1, csum2, csum_known = 0xd3a4; | |
205 | ||
206 | memset(¶m, 0, sizeof(param)); | |
4efbac6f | 207 | ext2fs_blocks_count_set(¶m, 32768); |
470e737a | 208 | |
8f82ef98 | 209 | retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, |
470e737a TT |
210 | test_io_manager, &fs); |
211 | if (retval) { | |
212 | com_err("setup", retval, | |
213 | "While initializing filesystem"); | |
214 | exit(1); | |
215 | } | |
216 | memcpy(fs->super->s_uuid, sb_uuid, 16); | |
217 | fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; | |
218 | ||
219 | for (i=0; i < fs->group_desc_count; i++) { | |
d7cca6b0 VAH |
220 | ext2fs_block_bitmap_loc_set(fs, i, 124); |
221 | ext2fs_inode_bitmap_loc_set(fs, i, 125); | |
222 | ext2fs_inode_table_loc_set(fs, i, 126); | |
223 | ext2fs_bg_free_blocks_count_set(fs, i, 31119); | |
224 | ext2fs_bg_free_inodes_count_set(fs, i, 15701); | |
225 | ext2fs_bg_used_dirs_count_set(fs, i, 2); | |
e633b58a | 226 | ext2fs_bg_flags_zap(fs, i); |
470e737a TT |
227 | }; |
228 | ||
229 | csum1 = ext2fs_group_desc_csum(fs, 0); | |
230 | print_csum("csum0000", fs, 0); | |
231 | ||
232 | if (csum1 != csum_known) { | |
233 | printf("checksum for group 0 should be %04x\n", csum_known); | |
234 | exit(1); | |
235 | } | |
236 | csum2 = ext2fs_group_desc_csum(fs, 1); | |
237 | print_csum("csum0001", fs, 1); | |
238 | if (csum1 == csum2) { | |
239 | printf("checksums for different groups shouldn't match\n"); | |
240 | exit(1); | |
241 | } | |
242 | csum2 = ext2fs_group_desc_csum(fs, 2); | |
243 | print_csum("csumffff", fs, 2); | |
244 | if (csum1 == csum2) { | |
245 | printf("checksums for different groups shouldn't match\n"); | |
246 | exit(1); | |
247 | } | |
d7cca6b0 | 248 | ext2fs_bg_checksum_set(fs, 0, csum1); |
470e737a TT |
249 | csum2 = ext2fs_group_desc_csum(fs, 0); |
250 | print_csum("csum_set", fs, 0); | |
251 | if (csum1 != csum2) { | |
252 | printf("checksums should not depend on checksum field\n"); | |
253 | exit(1); | |
254 | } | |
255 | if (!ext2fs_group_desc_csum_verify(fs, 0)) { | |
256 | printf("checksums should verify against gd_checksum\n"); | |
257 | exit(1); | |
258 | } | |
259 | memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); | |
260 | print_csum("new_uuid", fs, 0); | |
261 | if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { | |
262 | printf("checksums for different filesystems shouldn't match\n"); | |
263 | exit(1); | |
264 | } | |
d7cca6b0 VAH |
265 | csum1 = ext2fs_group_desc_csum(fs, 0); |
266 | ext2fs_bg_checksum_set(fs, 0, csum1); | |
470e737a | 267 | print_csum("csum_new", fs, 0); |
d7cca6b0 | 268 | ext2fs_bg_free_blocks_count_set(fs, 0, 1); |
470e737a TT |
269 | csum2 = ext2fs_group_desc_csum(fs, 0); |
270 | print_csum("csum_blk", fs, 0); | |
271 | if (csum1 == csum2) { | |
272 | printf("checksums for different data shouldn't match\n"); | |
273 | exit(1); | |
274 | } | |
275 | ||
276 | return 0; | |
277 | } | |
278 | #endif |