Whamcloud - gitweb
3918a4a753a422077b50c182b012c0bc6e34fb31
[tools/e2fsprogs.git] / lib / ext2fs / csum.c
1 /*
2  * csum.c --- checksumming of ext3 structures
3  *
4  * Copyright (C) 2006 Cluster File Systems, Inc.
5  * Copyright (C) 2006, 2007 by Andreas Dilger <adilger@clusterfs.com>
6  *
7  * %Begin-Header%
8  * This file may be redistributed under the terms of the GNU Library
9  * General Public License, version 2.
10  * %End-Header%
11  */
12
13 #include "config.h"
14 #if HAVE_SYS_TYPES_H
15 #include <sys/types.h>
16 #endif
17
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
33 int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
34                                     char *bitmap, int size)
35 {
36         struct ext4_group_desc *gdp = (struct ext4_group_desc *)
37                         ext2fs_group_desc(fs, fs->group_desc, group);
38         __u32 provided, calculated;
39
40         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
41                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
42                 return 1;
43         provided = gdp->bg_inode_bitmap_csum_lo;
44         calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
45                                       size);
46         if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
47                 provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16;
48         else
49                 calculated &= 0xFFFF;
50
51         return provided == calculated;
52 }
53
54 errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
55                                        char *bitmap, int size)
56 {
57         __u32 crc;
58         struct ext4_group_desc *gdp = (struct ext4_group_desc *)
59                         ext2fs_group_desc(fs, fs->group_desc, group);
60
61         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
62                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
63                 return 0;
64
65         crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
66         gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF;
67         if (fs->super->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END)
68                 gdp->bg_inode_bitmap_csum_hi = crc >> 16;
69
70         return 0;
71 }
72
73 int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group,
74                                     char *bitmap, int size)
75 {
76         struct ext4_group_desc *gdp = (struct ext4_group_desc *)
77                         ext2fs_group_desc(fs, fs->group_desc, group);
78         __u32 provided, calculated;
79
80         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
81                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
82                 return 1;
83         provided = gdp->bg_block_bitmap_csum_lo;
84         calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap,
85                                       size);
86         if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
87                 provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16;
88         else
89                 calculated &= 0xFFFF;
90
91         return provided == calculated;
92 }
93
94 errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group,
95                                        char *bitmap, int size)
96 {
97         __u32 crc;
98         struct ext4_group_desc *gdp = (struct ext4_group_desc *)
99                         ext2fs_group_desc(fs, fs->group_desc, group);
100
101         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
102                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
103                 return 0;
104
105         crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size);
106         gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF;
107         if (fs->super->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION)
108                 gdp->bg_block_bitmap_csum_hi = crc >> 16;
109
110         return 0;
111 }
112
113 static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum,
114                                struct ext2_inode_large *inode,
115                                __u32 *crc, int has_hi)
116 {
117         __u32 gen;
118         struct ext2_inode_large *desc = inode;
119         size_t size = fs->super->s_inode_size;
120         __u16 old_lo;
121         __u16 old_hi = 0;
122
123         old_lo = inode->i_checksum_lo;
124         inode->i_checksum_lo = 0;
125         if (has_hi) {
126                 old_hi = inode->i_checksum_hi;
127                 inode->i_checksum_hi = 0;
128         }
129
130         inum = ext2fs_cpu_to_le32(inum);
131         gen = inode->i_generation;
132         *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum,
133                                 sizeof(inum));
134         *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen));
135         *crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size);
136
137         inode->i_checksum_lo = old_lo;
138         if (has_hi)
139                 inode->i_checksum_hi = old_hi;
140         return 0;
141 }
142
143 int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum,
144                              struct ext2_inode_large *inode)
145 {
146         errcode_t retval;
147         __u32 provided, calculated;
148         int has_hi;
149
150         if (fs->super->s_creator_os != EXT2_OS_LINUX ||
151             !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
152                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
153                 return 1;
154
155         has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
156                   inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
157
158         provided = ext2fs_le16_to_cpu(inode->i_checksum_lo);
159         retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi);
160         if (retval)
161                 return 0;
162         if (has_hi) {
163                 __u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi);
164                 provided |= hi << 16;
165         } else
166                 calculated &= 0xFFFF;
167
168         return provided == calculated;
169 }
170
171 errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum,
172                            struct ext2_inode_large *inode)
173 {
174         errcode_t retval;
175         __u32 crc;
176         int has_hi;
177
178         if (fs->super->s_creator_os != EXT2_OS_LINUX ||
179             !EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
180                                         EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
181                 return 0;
182
183         has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE &&
184                   inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END);
185
186         retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi);
187         if (retval)
188                 return retval;
189         inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF);
190         if (has_hi)
191                 inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16);
192         return 0;
193 }
194
195 __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group)
196 {
197         __u16 crc = 0;
198         struct ext2_group_desc *desc;
199         size_t size;
200
201         size = fs->super->s_desc_size;
202         if (size < EXT2_MIN_DESC_SIZE)
203                 size = EXT2_MIN_DESC_SIZE;
204         if (size > sizeof(struct ext4_group_desc)) {
205                 /* This should never happen, but cap it for safety's sake */
206                 size = sizeof(struct ext4_group_desc);
207         }
208
209         desc = ext2fs_group_desc(fs, fs->group_desc, group);
210
211         if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
212                 size_t offset = offsetof(struct ext2_group_desc, bg_checksum);
213
214 #ifdef WORDS_BIGENDIAN
215                 struct ext4_group_desc swabdesc;
216
217                 /* Have to swab back to little-endian to do the checksum */
218                 memcpy(&swabdesc, desc, size);
219                 ext2fs_swap_group_desc2(fs,
220                                         (struct ext2_group_desc *) &swabdesc);
221                 desc = (struct ext2_group_desc *) &swabdesc;
222
223                 group = ext2fs_swab32(group);
224 #endif
225                 crc = ext2fs_crc16(~0, fs->super->s_uuid,
226                                    sizeof(fs->super->s_uuid));
227                 crc = ext2fs_crc16(crc, &group, sizeof(group));
228                 crc = ext2fs_crc16(crc, desc, offset);
229                 offset += sizeof(desc->bg_checksum); /* skip checksum */
230                 /* for checksum of struct ext4_group_desc do the rest...*/
231                 if (offset < size) {
232                         crc = ext2fs_crc16(crc, (char *)desc + offset,
233                                            size - offset);
234                 }
235         }
236
237         return crc;
238 }
239
240 int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group)
241 {
242         if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
243                                        EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
244             (ext2fs_bg_checksum(fs, group) !=
245              ext2fs_group_desc_csum(fs, group)))
246                 return 0;
247
248         return 1;
249 }
250
251 void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group)
252 {
253         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
254                                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
255                 return;
256
257         /* ext2fs_bg_checksum_set() sets the actual checksum field but
258          * does not calculate the checksum itself. */
259         ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group));
260 }
261
262 static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap,
263                                    __u32 inodes_per_grp, dgrp_t grp_no)
264 {
265         ext2_ino_t i, start_ino, end_ino;
266
267         start_ino = grp_no * inodes_per_grp + 1;
268         end_ino = start_ino + inodes_per_grp - 1;
269
270         for (i = end_ino; i >= start_ino; i--) {
271                 if (ext2fs_fast_test_inode_bitmap2(bitmap, i))
272                         return i - start_ino + 1;
273         }
274         return inodes_per_grp;
275 }
276
277 /* update the bitmap flags, set the itable high watermark, and calculate
278  * checksums for the group descriptors */
279 errcode_t ext2fs_set_gdt_csum(ext2_filsys fs)
280 {
281         struct ext2_super_block *sb = fs->super;
282         int dirty = 0;
283         dgrp_t i;
284
285         if (!fs->inode_map)
286                 return EXT2_ET_NO_INODE_BITMAP;
287
288         if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
289                                         EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
290                 return 0;
291
292         for (i = 0; i < fs->group_desc_count; i++) {
293                 __u32 old_csum = ext2fs_bg_checksum(fs, i);
294                 __u32 old_unused = ext2fs_bg_itable_unused(fs, i);
295                 __u32 old_flags = ext2fs_bg_flags(fs, i);
296                 __u32 old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i);
297
298                 if (old_free_inodes_count == sb->s_inodes_per_group) {
299                         ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT);
300                         ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group);
301                 } else {
302                         int unused =
303                                 sb->s_inodes_per_group -
304                                 find_last_inode_ingrp(fs->inode_map,
305                                                       sb->s_inodes_per_group, i);
306
307                         ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT);
308                         ext2fs_bg_itable_unused_set(fs, i, unused);
309                 }
310
311                 ext2fs_group_desc_csum_set(fs, i);
312                 if (old_flags != ext2fs_bg_flags(fs, i))
313                         dirty = 1;
314                 if (old_unused != ext2fs_bg_itable_unused(fs, i))
315                         dirty = 1;
316                 if (old_csum != ext2fs_bg_checksum(fs, i))
317                         dirty = 1;
318         }
319         if (dirty)
320                 ext2fs_mark_super_dirty(fs);
321         return 0;
322 }
323
324 #ifdef DEBUG
325 #include "e2p/e2p.h"
326
327 void print_csum(const char *msg, ext2_filsys fs, dgrp_t group)
328 {
329         __u16 crc1, crc2, crc3;
330         dgrp_t swabgroup;
331         struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, group);
332         size_t size;
333         struct ext2_super_block *sb = fs->super;
334         int offset = offsetof(struct ext2_group_desc, bg_checksum);
335 #ifdef WORDS_BIGENDIAN
336         struct ext4_group_desc swabdesc;
337 #endif
338
339         size = fs->super->s_desc_size;
340         if (size < EXT2_MIN_DESC_SIZE)
341                 size = EXT2_MIN_DESC_SIZE;
342         if (size > sizeof(struct ext4_group_desc))
343                 size = sizeof(struct ext4_group_desc);
344 #ifdef WORDS_BIGENDIAN
345         /* Have to swab back to little-endian to do the checksum */
346         memcpy(&swabdesc, desc, size);
347         ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc);
348         desc = (struct ext2_group_desc *) &swabdesc;
349
350         swabgroup = ext2fs_swab32(group);
351 #else
352         swabgroup = group;
353 #endif
354
355         crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid));
356         crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup));
357         crc3 = ext2fs_crc16(crc2, desc, offset);
358         offset += sizeof(desc->bg_checksum); /* skip checksum */
359         /* for checksum of struct ext4_group_desc do the rest...*/
360         if (offset < size)
361                 crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset);
362
363         printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n",
364                msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3,
365                ext2fs_group_desc_csum(fs, group));
366 }
367
368 unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23,
369                               0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb };
370
371 int main(int argc, char **argv)
372 {
373         struct ext2_super_block param;
374         errcode_t               retval;
375         ext2_filsys             fs;
376         int                     i;
377         __u16 csum1, csum2, csum_known = 0xd3a4;
378
379         memset(&param, 0, sizeof(param));
380         ext2fs_blocks_count_set(&param, 32768);
381
382         retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
383                                    test_io_manager, &fs);
384         if (retval) {
385                 com_err("setup", retval,
386                         "While initializing filesystem");
387                 exit(1);
388         }
389         memcpy(fs->super->s_uuid, sb_uuid, 16);
390         fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
391
392         for (i=0; i < fs->group_desc_count; i++) {
393                 ext2fs_block_bitmap_loc_set(fs, i, 124);
394                 ext2fs_inode_bitmap_loc_set(fs, i, 125);
395                 ext2fs_inode_table_loc_set(fs, i, 126);
396                 ext2fs_bg_free_blocks_count_set(fs, i, 31119);
397                 ext2fs_bg_free_inodes_count_set(fs, i, 15701);
398                 ext2fs_bg_used_dirs_count_set(fs, i, 2);
399                 ext2fs_bg_flags_zap(fs, i);
400         };
401
402         csum1 = ext2fs_group_desc_csum(fs, 0);
403         print_csum("csum0000", fs, 0);
404
405         if (csum1 != csum_known) {
406                 printf("checksum for group 0 should be %04x\n", csum_known);
407                 exit(1);
408         }
409         csum2 = ext2fs_group_desc_csum(fs, 1);
410         print_csum("csum0001", fs, 1);
411         if (csum1 == csum2) {
412                 printf("checksums for different groups shouldn't match\n");
413                 exit(1);
414         }
415         csum2 = ext2fs_group_desc_csum(fs, 2);
416         print_csum("csumffff", fs, 2);
417         if (csum1 == csum2) {
418                 printf("checksums for different groups shouldn't match\n");
419                 exit(1);
420         }
421         ext2fs_bg_checksum_set(fs, 0, csum1);
422         csum2 = ext2fs_group_desc_csum(fs, 0);
423         print_csum("csum_set", fs, 0);
424         if (csum1 != csum2) {
425                 printf("checksums should not depend on checksum field\n");
426                 exit(1);
427         }
428         if (!ext2fs_group_desc_csum_verify(fs, 0)) {
429                 printf("checksums should verify against gd_checksum\n");
430                 exit(1);
431         }
432         memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid));
433         print_csum("new_uuid", fs, 0);
434         if (ext2fs_group_desc_csum_verify(fs, 0) != 0) {
435                 printf("checksums for different filesystems shouldn't match\n");
436                 exit(1);
437         }
438         csum1 = ext2fs_group_desc_csum(fs, 0);
439         ext2fs_bg_checksum_set(fs, 0, csum1);
440         print_csum("csum_new", fs, 0);
441         ext2fs_bg_free_blocks_count_set(fs, 0, 1);
442         csum2 = ext2fs_group_desc_csum(fs, 0);
443         print_csum("csum_blk", fs, 0);
444         if (csum1 == csum2) {
445                 printf("checksums for different data shouldn't match\n");
446                 exit(1);
447         }
448
449         return 0;
450 }
451 #endif