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