csum.c revision 18a1444b4f1e6a0948fd38fa0de382d86cfe04de
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#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 85int 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 96void 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 107static __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 */ 124errcode_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 172void 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 219unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, 220 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; 221 222int 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