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 (at) 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 #if HAVE_SYS_TYPES_H 14 #include <sys/types.h> 15 #endif 16 17 #include "ext2_fs.h" 18 #include "ext2fs.h" 19 #include "crc16.h" 20 #include <assert.h> 21 22 #ifndef offsetof 23 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 24 #endif 25 26 #ifdef DEBUG 27 #define STATIC 28 #else 29 #define STATIC static 30 #endif 31 32 STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) 33 { 34 __u16 crc = 0; 35 struct ext2_group_desc *desc; 36 37 desc = &fs->group_desc[group]; 38 39 if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { 40 int offset = offsetof(struct ext2_group_desc, bg_checksum); 41 42 #ifdef WORDS_BIGENDIAN 43 struct ext2_group_desc swabdesc = *desc; 44 45 /* Have to swab back to little-endian to do the checksum */ 46 ext2fs_swap_group_desc(&swabdesc); 47 desc = &swabdesc; 48 49 group = ext2fs_swab32(group); 50 #endif 51 crc = ext2fs_crc16(~0, fs->super->s_uuid, 52 sizeof(fs->super->s_uuid)); 53 crc = ext2fs_crc16(crc, &group, sizeof(group)); 54 crc = ext2fs_crc16(crc, desc, offset); 55 offset += sizeof(desc->bg_checksum); /* skip checksum */ 56 assert(offset == sizeof(*desc)); 57 /* for checksum of struct ext4_group_desc do the rest...*/ 58 if (offset < fs->super->s_desc_size) { 59 crc = ext2fs_crc16(crc, (char *)desc + offset, 60 fs->super->s_desc_size - offset); 61 } 62 } 63 64 return crc; 65 } 66 67 int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) 68 { 69 if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 70 EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && 71 (fs->group_desc[group].bg_checksum != 72 ext2fs_group_desc_csum(fs, group))) 73 return 0; 74 75 return 1; 76 } 77 78 void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) 79 { 80 if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 81 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) 82 fs->group_desc[group].bg_checksum = 83 ext2fs_group_desc_csum(fs, group); 84 } 85 86 static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, 87 __u32 inodes_per_grp, dgrp_t grp_no) 88 { 89 ext2_ino_t i, start_ino, end_ino; 90 91 start_ino = grp_no * inodes_per_grp + 1; 92 end_ino = start_ino + inodes_per_grp - 1; 93 94 for (i = end_ino; i >= start_ino; i--) { 95 if (ext2fs_fast_test_inode_bitmap(bitmap, i)) 96 return i - start_ino + 1; 97 } 98 return inodes_per_grp; 99 } 100 101 /* update the bitmap flags, set the itable high watermark, and calculate 102 * checksums for the group descriptors */ 103 errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) 104 { 105 struct ext2_super_block *sb = fs->super; 106 struct ext2_group_desc *bg = fs->group_desc; 107 int dirty = 0; 108 dgrp_t i; 109 110 if (!fs->inode_map) 111 return EXT2_ET_NO_INODE_BITMAP; 112 113 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 114 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) 115 return 0; 116 117 for (i = 0; i < fs->group_desc_count; i++, bg++) { 118 int old_csum = bg->bg_checksum; 119 int old_unused = bg->bg_itable_unused; 120 int old_flags = bg->bg_flags; 121 122 if (bg->bg_free_inodes_count == sb->s_inodes_per_group) { 123 bg->bg_flags |= EXT2_BG_INODE_UNINIT; 124 bg->bg_itable_unused = sb->s_inodes_per_group; 125 } else { 126 bg->bg_flags &= ~EXT2_BG_INODE_UNINIT; 127 bg->bg_itable_unused = sb->s_inodes_per_group - 128 find_last_inode_ingrp(fs->inode_map, 129 sb->s_inodes_per_group,i); 130 } 131 132 ext2fs_group_desc_csum_set(fs, i); 133 if (old_flags != bg->bg_flags) 134 dirty = 1; 135 if (old_unused != bg->bg_itable_unused) 136 dirty = 1; 137 if (old_csum != bg->bg_checksum) 138 dirty = 1; 139 } 140 if (dirty) 141 ext2fs_mark_super_dirty(fs); 142 return 0; 143 } 144 145 #ifdef DEBUG 146 #include "e2p/e2p.h" 147 148 void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) 149 { 150 __u16 crc1, crc2, crc3; 151 dgrp_t swabgroup; 152 struct ext2_group_desc *desc = &fs->group_desc[group]; 153 struct ext2_super_block *sb = fs->super; 154 155 #ifdef WORDS_BIGENDIAN 156 struct ext2_group_desc swabdesc = fs->group_desc[group]; 157 158 /* Have to swab back to little-endian to do the checksum */ 159 ext2fs_swap_group_desc(&swabdesc); 160 desc = &swabdesc; 161 162 swabgroup = ext2fs_swab32(group); 163 #else 164 swabgroup = group; 165 #endif 166 167 crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); 168 crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); 169 crc3 = ext2fs_crc16(crc2, desc, 170 offsetof(struct ext2_group_desc, bg_checksum)); 171 printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n", 172 msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2,crc3, 173 ext2fs_group_desc_csum(fs, group)); 174 } 175 176 unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, 177 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; 178 179 int main(int argc, char **argv) 180 { 181 struct ext2_super_block param; 182 errcode_t retval; 183 ext2_filsys fs; 184 int i; 185 __u16 csum1, csum2, csum_known = 0xd3a4; 186 187 memset(¶m, 0, sizeof(param)); 188 param.s_blocks_count = 32768; 189 190 retval = ext2fs_initialize("test fs", 0, ¶m, 191 test_io_manager, &fs); 192 if (retval) { 193 com_err("setup", retval, 194 "While initializing filesystem"); 195 exit(1); 196 } 197 memcpy(fs->super->s_uuid, sb_uuid, 16); 198 fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; 199 200 for (i=0; i < fs->group_desc_count; i++) { 201 fs->group_desc[i].bg_block_bitmap = 124; 202 fs->group_desc[i].bg_inode_bitmap = 125; 203 fs->group_desc[i].bg_inode_table = 126; 204 fs->group_desc[i].bg_free_blocks_count = 31119; 205 fs->group_desc[i].bg_free_inodes_count = 15701; 206 fs->group_desc[i].bg_used_dirs_count = 2; 207 fs->group_desc[i].bg_flags = 0; 208 }; 209 210 csum1 = ext2fs_group_desc_csum(fs, 0); 211 print_csum("csum0000", fs, 0); 212 213 if (csum1 != csum_known) { 214 printf("checksum for group 0 should be %04x\n", csum_known); 215 exit(1); 216 } 217 csum2 = ext2fs_group_desc_csum(fs, 1); 218 print_csum("csum0001", fs, 1); 219 if (csum1 == csum2) { 220 printf("checksums for different groups shouldn't match\n"); 221 exit(1); 222 } 223 csum2 = ext2fs_group_desc_csum(fs, 2); 224 print_csum("csumffff", fs, 2); 225 if (csum1 == csum2) { 226 printf("checksums for different groups shouldn't match\n"); 227 exit(1); 228 } 229 fs->group_desc[0].bg_checksum = csum1; 230 csum2 = ext2fs_group_desc_csum(fs, 0); 231 print_csum("csum_set", fs, 0); 232 if (csum1 != csum2) { 233 printf("checksums should not depend on checksum field\n"); 234 exit(1); 235 } 236 if (!ext2fs_group_desc_csum_verify(fs, 0)) { 237 printf("checksums should verify against gd_checksum\n"); 238 exit(1); 239 } 240 memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); 241 print_csum("new_uuid", fs, 0); 242 if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { 243 printf("checksums for different filesystems shouldn't match\n"); 244 exit(1); 245 } 246 csum1 = fs->group_desc[0].bg_checksum = ext2fs_group_desc_csum(fs, 0); 247 print_csum("csum_new", fs, 0); 248 fs->group_desc[0].bg_free_blocks_count = 1; 249 csum2 = ext2fs_group_desc_csum(fs, 0); 250 print_csum("csum_blk", fs, 0); 251 if (csum1 == csum2) { 252 printf("checksums for different data shouldn't match\n"); 253 exit(1); 254 } 255 256 return 0; 257 } 258 #endif 259