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