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