csum.c revision 87141781aabb4dc01359428d2feecdc7f43eeac0
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 = ext2fs_group_desc(fs, fs->group_desc, group); 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 size = fs->super->s_desc_size; 178 if (size < EXT2_MIN_DESC_SIZE) 179 size = EXT2_MIN_DESC_SIZE; 180 if (size > sizeof(struct ext4_group_desc)) 181 size = sizeof(struct ext4_group_desc); 182#ifdef WORDS_BIGENDIAN 183 /* Have to swab back to little-endian to do the checksum */ 184 memcpy(&swabdesc, desc, size); 185 ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); 186 desc = (struct ext2_group_desc *) &swabdesc; 187 188 swabgroup = ext2fs_swab32(group); 189#else 190 swabgroup = group; 191#endif 192 193 crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); 194 crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); 195 crc3 = ext2fs_crc16(crc2, desc, offset); 196 offset += sizeof(desc->bg_checksum); /* skip checksum */ 197 /* for checksum of struct ext4_group_desc do the rest...*/ 198 if (offset < size) 199 crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); 200 201 printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n", 202 msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, 203 ext2fs_group_desc_csum(fs, group)); 204} 205 206unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, 207 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; 208 209int main(int argc, char **argv) 210{ 211 struct ext2_super_block param; 212 errcode_t retval; 213 ext2_filsys fs; 214 int i; 215 __u16 csum1, csum2, csum_known = 0xd3a4; 216 217 memset(¶m, 0, sizeof(param)); 218 ext2fs_blocks_count_set(¶m, 32768); 219 220 retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, 221 test_io_manager, &fs); 222 if (retval) { 223 com_err("setup", retval, 224 "While initializing filesystem"); 225 exit(1); 226 } 227 memcpy(fs->super->s_uuid, sb_uuid, 16); 228 fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; 229 230 for (i=0; i < fs->group_desc_count; i++) { 231 ext2fs_block_bitmap_loc_set(fs, i, 124); 232 ext2fs_inode_bitmap_loc_set(fs, i, 125); 233 ext2fs_inode_table_loc_set(fs, i, 126); 234 ext2fs_bg_free_blocks_count_set(fs, i, 31119); 235 ext2fs_bg_free_inodes_count_set(fs, i, 15701); 236 ext2fs_bg_used_dirs_count_set(fs, i, 2); 237 ext2fs_bg_flags_zap(fs, i); 238 }; 239 240 csum1 = ext2fs_group_desc_csum(fs, 0); 241 print_csum("csum0000", fs, 0); 242 243 if (csum1 != csum_known) { 244 printf("checksum for group 0 should be %04x\n", csum_known); 245 exit(1); 246 } 247 csum2 = ext2fs_group_desc_csum(fs, 1); 248 print_csum("csum0001", fs, 1); 249 if (csum1 == csum2) { 250 printf("checksums for different groups shouldn't match\n"); 251 exit(1); 252 } 253 csum2 = ext2fs_group_desc_csum(fs, 2); 254 print_csum("csumffff", fs, 2); 255 if (csum1 == csum2) { 256 printf("checksums for different groups shouldn't match\n"); 257 exit(1); 258 } 259 ext2fs_bg_checksum_set(fs, 0, csum1); 260 csum2 = ext2fs_group_desc_csum(fs, 0); 261 print_csum("csum_set", fs, 0); 262 if (csum1 != csum2) { 263 printf("checksums should not depend on checksum field\n"); 264 exit(1); 265 } 266 if (!ext2fs_group_desc_csum_verify(fs, 0)) { 267 printf("checksums should verify against gd_checksum\n"); 268 exit(1); 269 } 270 memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); 271 print_csum("new_uuid", fs, 0); 272 if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { 273 printf("checksums for different filesystems shouldn't match\n"); 274 exit(1); 275 } 276 csum1 = ext2fs_group_desc_csum(fs, 0); 277 ext2fs_bg_checksum_set(fs, 0, csum1); 278 print_csum("csum_new", fs, 0); 279 ext2fs_bg_free_blocks_count_set(fs, 0, 1); 280 csum2 = ext2fs_group_desc_csum(fs, 0); 281 print_csum("csum_blk", fs, 0); 282 if (csum1 == csum2) { 283 printf("checksums for different data shouldn't match\n"); 284 exit(1); 285 } 286 287 return 0; 288} 289#endif 290