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 __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) 33 { 34 struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, 35 group); 36 size_t size = EXT2_DESC_SIZE(fs->super); 37 size_t offset; 38 __u16 crc; 39 40 if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { 41 size_t offset = offsetof(struct ext2_group_desc, bg_checksum); 42 43 #ifdef WORDS_BIGENDIAN 44 struct ext4_group_desc swabdesc; 45 size_t save_size = size; 46 const size_t ext4_bg_size = sizeof(struct ext4_group_desc); 47 struct ext2_group_desc *save_desc = desc; 48 49 /* Have to swab back to little-endian to do the checksum */ 50 if (size > ext4_bg_size) 51 size = ext4_bg_size; 52 memcpy(&swabdesc, desc, size); 53 ext2fs_swap_group_desc2(fs, 54 (struct ext2_group_desc *) &swabdesc); 55 desc = (struct ext2_group_desc *) &swabdesc; 56 57 group = ext2fs_swab32(group); 58 #endif 59 crc = ext2fs_crc16(~0, fs->super->s_uuid, 60 sizeof(fs->super->s_uuid)); 61 crc = ext2fs_crc16(crc, &group, sizeof(group)); 62 crc = ext2fs_crc16(crc, desc, offset); 63 offset += sizeof(desc->bg_checksum); /* skip checksum */ 64 /* for checksum of struct ext4_group_desc do the rest...*/ 65 if (offset < size) { 66 crc = ext2fs_crc16(crc, (char *)desc + offset, 67 size - offset); 68 } 69 #ifdef WORDS_BIGENDIAN 70 /* 71 * If the size of the bg descriptor is greater than 64 72 * bytes, which is the size of the traditional ext4 bg 73 * descriptor, checksum the rest of the descriptor here 74 */ 75 if (save_size > ext4_bg_size) 76 crc = ext2fs_crc16(crc, 77 (char *)save_desc + ext4_bg_size, 78 save_size - ext4_bg_size); 79 #endif 80 } 81 82 return crc; 83 } 84 85 int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) 86 { 87 if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 88 EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && 89 (ext2fs_bg_checksum(fs, group) != 90 ext2fs_group_desc_csum(fs, group))) 91 return 0; 92 93 return 1; 94 } 95 96 void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) 97 { 98 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 99 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) 100 return; 101 102 /* ext2fs_bg_checksum_set() sets the actual checksum field but 103 * does not calculate the checksum itself. */ 104 ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group)); 105 } 106 107 static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, 108 __u32 inodes_per_grp, dgrp_t grp_no) 109 { 110 ext2_ino_t i, start_ino, end_ino; 111 112 start_ino = grp_no * inodes_per_grp + 1; 113 end_ino = start_ino + inodes_per_grp - 1; 114 115 for (i = end_ino; i >= start_ino; i--) { 116 if (ext2fs_fast_test_inode_bitmap2(bitmap, i)) 117 return i - start_ino + 1; 118 } 119 return inodes_per_grp; 120 } 121 122 /* update the bitmap flags, set the itable high watermark, and calculate 123 * checksums for the group descriptors */ 124 errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) 125 { 126 struct ext2_super_block *sb = fs->super; 127 int dirty = 0; 128 dgrp_t i; 129 130 if (!fs->inode_map) 131 return EXT2_ET_NO_INODE_BITMAP; 132 133 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, 134 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) 135 return 0; 136 137 for (i = 0; i < fs->group_desc_count; i++) { 138 __u32 old_csum = ext2fs_bg_checksum(fs, i); 139 __u32 old_unused = ext2fs_bg_itable_unused(fs, i); 140 __u32 old_flags = ext2fs_bg_flags(fs, i); 141 __u32 old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i); 142 143 if (old_free_inodes_count == sb->s_inodes_per_group) { 144 ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); 145 ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group); 146 } else { 147 int unused = 148 sb->s_inodes_per_group - 149 find_last_inode_ingrp(fs->inode_map, 150 sb->s_inodes_per_group, i); 151 152 ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT); 153 ext2fs_bg_itable_unused_set(fs, i, unused); 154 } 155 156 ext2fs_group_desc_csum_set(fs, i); 157 if (old_flags != ext2fs_bg_flags(fs, i)) 158 dirty = 1; 159 if (old_unused != ext2fs_bg_itable_unused(fs, i)) 160 dirty = 1; 161 if (old_csum != ext2fs_bg_checksum(fs, i)) 162 dirty = 1; 163 } 164 if (dirty) 165 ext2fs_mark_super_dirty(fs); 166 return 0; 167 } 168 169 #ifdef DEBUG 170 #include "e2p/e2p.h" 171 172 void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) 173 { 174 __u16 crc1, crc2, crc3; 175 dgrp_t swabgroup; 176 struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, 177 group); 178 size_t size = EXT2_DESC_SIZE(fs->super); 179 struct ext2_super_block *sb = fs->super; 180 int offset = offsetof(struct ext2_group_desc, bg_checksum); 181 #ifdef WORDS_BIGENDIAN 182 struct ext4_group_desc swabdesc; 183 struct ext2_group_desc *save_desc = desc; 184 const size_t ext4_bg_size = sizeof(struct ext4_group_desc); 185 size_t save_size = size; 186 #endif 187 188 #ifdef WORDS_BIGENDIAN 189 /* Have to swab back to little-endian to do the checksum */ 190 if (size > ext4_bg_size) 191 size = ext4_bg_size; 192 memcpy(&swabdesc, desc, size); 193 ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); 194 desc = (struct ext2_group_desc *) &swabdesc; 195 196 swabgroup = ext2fs_swab32(group); 197 #else 198 swabgroup = group; 199 #endif 200 201 crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); 202 crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); 203 crc3 = ext2fs_crc16(crc2, desc, offset); 204 offset += sizeof(desc->bg_checksum); /* skip checksum */ 205 /* for checksum of struct ext4_group_desc do the rest...*/ 206 if (offset < size) 207 crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); 208 #ifdef WORDS_BIGENDIAN 209 if (save_size > ext4_bg_size) 210 crc3 = ext2fs_crc16(crc3, (char *)save_desc + ext4_bg_size, 211 save_size - ext4_bg_size); 212 #endif 213 214 printf("%s UUID %s=%04x, grp %u=%04x: %04x=%04x\n", 215 msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, 216 ext2fs_group_desc_csum(fs, group)); 217 } 218 219 unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, 220 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; 221 222 int main(int argc, char **argv) 223 { 224 struct ext2_super_block param; 225 errcode_t retval; 226 ext2_filsys fs; 227 int i; 228 __u16 csum1, csum2, csum_known = 0xd3a4; 229 230 memset(¶m, 0, sizeof(param)); 231 ext2fs_blocks_count_set(¶m, 32768); 232 #if 0 233 param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT; 234 param.s_desc_size = 128; 235 csum_known = 0x5b6e; 236 #endif 237 238 retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, 239 test_io_manager, &fs); 240 if (retval) { 241 com_err("setup", retval, 242 "While initializing filesystem"); 243 exit(1); 244 } 245 memcpy(fs->super->s_uuid, sb_uuid, 16); 246 fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; 247 248 for (i=0; i < fs->group_desc_count; i++) { 249 ext2fs_block_bitmap_loc_set(fs, i, 124); 250 ext2fs_inode_bitmap_loc_set(fs, i, 125); 251 ext2fs_inode_table_loc_set(fs, i, 126); 252 ext2fs_bg_free_blocks_count_set(fs, i, 31119); 253 ext2fs_bg_free_inodes_count_set(fs, i, 15701); 254 ext2fs_bg_used_dirs_count_set(fs, i, 2); 255 ext2fs_bg_flags_zap(fs, i); 256 }; 257 258 csum1 = ext2fs_group_desc_csum(fs, 0); 259 print_csum("csum0000", fs, 0); 260 261 if (csum1 != csum_known) { 262 printf("checksum for group 0 should be %04x\n", csum_known); 263 exit(1); 264 } 265 csum2 = ext2fs_group_desc_csum(fs, 1); 266 print_csum("csum0001", fs, 1); 267 if (csum1 == csum2) { 268 printf("checksums for different groups shouldn't match\n"); 269 exit(1); 270 } 271 csum2 = ext2fs_group_desc_csum(fs, 2); 272 print_csum("csumffff", fs, 2); 273 if (csum1 == csum2) { 274 printf("checksums for different groups shouldn't match\n"); 275 exit(1); 276 } 277 ext2fs_bg_checksum_set(fs, 0, csum1); 278 csum2 = ext2fs_group_desc_csum(fs, 0); 279 print_csum("csum_set", fs, 0); 280 if (csum1 != csum2) { 281 printf("checksums should not depend on checksum field\n"); 282 exit(1); 283 } 284 if (!ext2fs_group_desc_csum_verify(fs, 0)) { 285 printf("checksums should verify against gd_checksum\n"); 286 exit(1); 287 } 288 memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); 289 print_csum("new_uuid", fs, 0); 290 if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { 291 printf("checksums for different filesystems shouldn't match\n"); 292 exit(1); 293 } 294 csum1 = ext2fs_group_desc_csum(fs, 0); 295 ext2fs_bg_checksum_set(fs, 0, csum1); 296 print_csum("csum_new", fs, 0); 297 ext2fs_bg_free_blocks_count_set(fs, 0, 1); 298 csum2 = ext2fs_group_desc_csum(fs, 0); 299 print_csum("csum_blk", fs, 0); 300 if (csum1 == csum2) { 301 printf("checksums for different data shouldn't match\n"); 302 exit(1); 303 } 304 305 return 0; 306 } 307 #endif 308