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