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 void ext2fs_init_csum_seed(ext2_filsys fs) 34 { 35 if (ext2fs_has_feature_csum_seed(fs->super)) 36 fs->csum_seed = fs->super->s_checksum_seed; 37 else if (ext2fs_has_feature_metadata_csum(fs->super) || 38 ext2fs_has_feature_ea_inode(fs->super)) 39 fs->csum_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid, 40 sizeof(fs->super->s_uuid)); 41 } 42 43 static __u32 ext2fs_mmp_csum(ext2_filsys fs, struct mmp_struct *mmp) 44 { 45 int offset = offsetof(struct mmp_struct, mmp_checksum); 46 47 return ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)mmp, offset); 48 } 49 50 int ext2fs_mmp_csum_verify(ext2_filsys fs, struct mmp_struct *mmp) 51 { 52 __u32 calculated; 53 54 if (!ext2fs_has_feature_metadata_csum(fs->super)) 55 return 1; 56 57 calculated = ext2fs_mmp_csum(fs, mmp); 58 59 return ext2fs_le32_to_cpu(mmp->mmp_checksum) == calculated; 60 } 61 62 errcode_t ext2fs_mmp_csum_set(ext2_filsys fs, struct mmp_struct *mmp) 63 { 64 __u32 crc; 65 66 if (!ext2fs_has_feature_metadata_csum(fs->super)) 67 return 0; 68 69 crc = ext2fs_mmp_csum(fs, mmp); 70 mmp->mmp_checksum = ext2fs_cpu_to_le32(crc); 71 72 return 0; 73 } 74 75 int ext2fs_verify_csum_type(ext2_filsys fs, struct ext2_super_block *sb) 76 { 77 if (!ext2fs_has_feature_metadata_csum(fs->super)) 78 return 1; 79 80 return sb->s_checksum_type == EXT2_CRC32C_CHKSUM; 81 } 82 83 static __u32 ext2fs_superblock_csum(ext2_filsys fs EXT2FS_ATTR((unused)), 84 struct ext2_super_block *sb) 85 { 86 int offset = offsetof(struct ext2_super_block, s_checksum); 87 88 return ext2fs_crc32c_le(~0, (unsigned char *)sb, offset); 89 } 90 91 /* NOTE: The input to this function MUST be in LE order */ 92 int ext2fs_superblock_csum_verify(ext2_filsys fs, struct ext2_super_block *sb) 93 { 94 __u32 flag, calculated; 95 96 if (fs->flags & EXT2_FLAG_SWAP_BYTES) 97 flag = EXT4_FEATURE_RO_COMPAT_METADATA_CSUM; 98 else 99 flag = ext2fs_cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM); 100 101 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, flag)) 102 return 1; 103 104 calculated = ext2fs_superblock_csum(fs, sb); 105 106 return ext2fs_le32_to_cpu(sb->s_checksum) == calculated; 107 } 108 109 /* NOTE: The input to this function MUST be in LE order */ 110 errcode_t ext2fs_superblock_csum_set(ext2_filsys fs, 111 struct ext2_super_block *sb) 112 { 113 __u32 flag, crc; 114 115 if (fs->flags & EXT2_FLAG_SWAP_BYTES) 116 flag = EXT4_FEATURE_RO_COMPAT_METADATA_CSUM; 117 else 118 flag = ext2fs_cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM); 119 120 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, flag)) 121 return 0; 122 123 crc = ext2fs_superblock_csum(fs, sb); 124 sb->s_checksum = ext2fs_cpu_to_le32(crc); 125 126 return 0; 127 } 128 129 static errcode_t ext2fs_ext_attr_block_csum(ext2_filsys fs, 130 ext2_ino_t inum EXT2FS_ATTR((unused)), 131 blk64_t block, 132 struct ext2_ext_attr_header *hdr, 133 __u32 *crc) 134 { 135 char *buf = (char *)hdr; 136 __u32 old_crc = hdr->h_checksum; 137 138 hdr->h_checksum = 0; 139 block = ext2fs_cpu_to_le64(block); 140 *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&block, 141 sizeof(block)); 142 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, fs->blocksize); 143 hdr->h_checksum = old_crc; 144 145 return 0; 146 } 147 148 int ext2fs_ext_attr_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, 149 blk64_t block, 150 struct ext2_ext_attr_header *hdr) 151 { 152 __u32 calculated; 153 errcode_t retval; 154 155 if (!ext2fs_has_feature_metadata_csum(fs->super)) 156 return 1; 157 158 retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &calculated); 159 if (retval) 160 return 0; 161 162 return ext2fs_le32_to_cpu(hdr->h_checksum) == calculated; 163 } 164 165 errcode_t ext2fs_ext_attr_block_csum_set(ext2_filsys fs, ext2_ino_t inum, 166 blk64_t block, 167 struct ext2_ext_attr_header *hdr) 168 { 169 errcode_t retval; 170 __u32 crc; 171 172 if (!ext2fs_has_feature_metadata_csum(fs->super)) 173 return 0; 174 175 retval = ext2fs_ext_attr_block_csum(fs, inum, block, hdr, &crc); 176 if (retval) 177 return retval; 178 hdr->h_checksum = ext2fs_cpu_to_le32(crc); 179 return 0; 180 } 181 182 static __u16 do_nothing16(__u16 x) 183 { 184 return x; 185 } 186 187 static __u16 disk_to_host16(__u16 x) 188 { 189 return ext2fs_le16_to_cpu(x); 190 } 191 192 static errcode_t __get_dx_countlimit(ext2_filsys fs, 193 struct ext2_dir_entry *dirent, 194 struct ext2_dx_countlimit **cc, 195 int *offset, 196 int need_swab) 197 { 198 struct ext2_dir_entry *dp; 199 struct ext2_dx_root_info *root; 200 struct ext2_dx_countlimit *c; 201 int count_offset, max_sane_entries; 202 unsigned int rec_len; 203 __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16); 204 205 rec_len = translate(dirent->rec_len); 206 207 if (rec_len == fs->blocksize && translate(dirent->name_len) == 0) 208 count_offset = 8; 209 else if (rec_len == 12) { 210 dp = (struct ext2_dir_entry *)(((char *)dirent) + rec_len); 211 rec_len = translate(dp->rec_len); 212 if (rec_len != fs->blocksize - 12) 213 return EXT2_ET_DB_NOT_FOUND; 214 root = (struct ext2_dx_root_info *)(((char *)dp + 12)); 215 if (root->reserved_zero || 216 root->info_length != sizeof(struct ext2_dx_root_info)) 217 return EXT2_ET_DB_NOT_FOUND; 218 count_offset = 32; 219 } else 220 return EXT2_ET_DB_NOT_FOUND; 221 222 c = (struct ext2_dx_countlimit *)(((char *)dirent) + count_offset); 223 max_sane_entries = (fs->blocksize - count_offset) / 224 sizeof(struct ext2_dx_entry); 225 if (ext2fs_le16_to_cpu(c->limit) > max_sane_entries || 226 ext2fs_le16_to_cpu(c->count) > max_sane_entries) 227 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; 228 229 if (offset) 230 *offset = count_offset; 231 if (cc) 232 *cc = c; 233 234 return 0; 235 } 236 237 errcode_t ext2fs_get_dx_countlimit(ext2_filsys fs, 238 struct ext2_dir_entry *dirent, 239 struct ext2_dx_countlimit **cc, 240 int *offset) 241 { 242 return __get_dx_countlimit(fs, dirent, cc, offset, 0); 243 } 244 245 void ext2fs_initialize_dirent_tail(ext2_filsys fs, 246 struct ext2_dir_entry_tail *t) 247 { 248 memset(t, 0, sizeof(struct ext2_dir_entry_tail)); 249 ext2fs_set_rec_len(fs, sizeof(struct ext2_dir_entry_tail), 250 (struct ext2_dir_entry *)t); 251 t->det_reserved_name_len = EXT2_DIR_NAME_LEN_CSUM; 252 } 253 254 static errcode_t __get_dirent_tail(ext2_filsys fs, 255 struct ext2_dir_entry *dirent, 256 struct ext2_dir_entry_tail **tt, 257 int need_swab) 258 { 259 struct ext2_dir_entry *d; 260 void *top; 261 struct ext2_dir_entry_tail *t; 262 unsigned int rec_len; 263 errcode_t retval = 0; 264 __u16 (*translate)(__u16) = (need_swab ? disk_to_host16 : do_nothing16); 265 266 if (fs->blocksize < 1024) 267 return EXT2_FILSYS_CORRUPTED; /* Should never happen */ 268 269 d = dirent; 270 top = EXT2_DIRENT_TAIL(dirent, fs->blocksize); 271 272 while ((void *) d < top) { 273 rec_len = translate(d->rec_len); 274 if ((rec_len < 8) || (rec_len & 0x03)) 275 return EXT2_ET_DIR_CORRUPTED; 276 d = (struct ext2_dir_entry *)(((char *)d) + rec_len); 277 } 278 279 if ((char *)d > ((char *)dirent + fs->blocksize)) 280 return EXT2_ET_DIR_CORRUPTED; 281 if (d != top) 282 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; 283 284 t = (struct ext2_dir_entry_tail *)d; 285 if (t->det_reserved_zero1 || 286 translate(t->det_rec_len) != sizeof(struct ext2_dir_entry_tail) || 287 translate(t->det_reserved_name_len) != EXT2_DIR_NAME_LEN_CSUM) 288 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; 289 290 if (tt) 291 *tt = t; 292 return retval; 293 } 294 295 int ext2fs_dirent_has_tail(ext2_filsys fs, struct ext2_dir_entry *dirent) 296 { 297 return __get_dirent_tail(fs, dirent, NULL, 0) != 298 EXT2_ET_DIR_NO_SPACE_FOR_CSUM; 299 } 300 301 static errcode_t ext2fs_dirent_csum(ext2_filsys fs, ext2_ino_t inum, 302 struct ext2_dir_entry *dirent, __u32 *crc, 303 int size) 304 { 305 errcode_t retval; 306 char *buf = (char *)dirent; 307 __u32 gen; 308 struct ext2_inode inode; 309 310 retval = ext2fs_read_inode(fs, inum, &inode); 311 if (retval) 312 return retval; 313 314 inum = ext2fs_cpu_to_le32(inum); 315 gen = ext2fs_cpu_to_le32(inode.i_generation); 316 *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, 317 sizeof(inum)); 318 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); 319 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size); 320 321 return 0; 322 } 323 324 int ext2fs_dirent_csum_verify(ext2_filsys fs, ext2_ino_t inum, 325 struct ext2_dir_entry *dirent) 326 { 327 errcode_t retval; 328 __u32 calculated; 329 struct ext2_dir_entry_tail *t; 330 331 retval = __get_dirent_tail(fs, dirent, &t, 1); 332 if (retval) 333 return 1; 334 335 /* 336 * The checksum field is overlaid with the dirent->name field 337 * so the swapfs.c functions won't change the endianness. 338 */ 339 retval = ext2fs_dirent_csum(fs, inum, dirent, &calculated, 340 (char *)t - (char *)dirent); 341 if (retval) 342 return 0; 343 return ext2fs_le32_to_cpu(t->det_checksum) == calculated; 344 } 345 346 static errcode_t ext2fs_dirent_csum_set(ext2_filsys fs, ext2_ino_t inum, 347 struct ext2_dir_entry *dirent) 348 { 349 errcode_t retval; 350 __u32 crc; 351 struct ext2_dir_entry_tail *t; 352 353 retval = __get_dirent_tail(fs, dirent, &t, 1); 354 if (retval) 355 return retval; 356 357 /* swapfs.c functions don't change the checksum endianness */ 358 retval = ext2fs_dirent_csum(fs, inum, dirent, &crc, 359 (char *)t - (char *)dirent); 360 if (retval) 361 return retval; 362 t->det_checksum = ext2fs_cpu_to_le32(crc); 363 return 0; 364 } 365 366 errcode_t ext2fs_dx_csum(ext2_filsys fs, ext2_ino_t inum, 367 struct ext2_dir_entry *dirent, 368 __u32 *crc, struct ext2_dx_tail **ret_t) 369 { 370 errcode_t retval; 371 char *buf = (char *)dirent; 372 int size; 373 __u32 gen, dummy_csum = 0; 374 struct ext2_inode inode; 375 struct ext2_dx_tail *t; 376 struct ext2_dx_countlimit *c; 377 int count_offset, limit, count; 378 379 retval = __get_dx_countlimit(fs, dirent, &c, &count_offset, 1); 380 if (retval) 381 return retval; 382 limit = ext2fs_le16_to_cpu(c->limit); 383 count = ext2fs_le16_to_cpu(c->count); 384 if (count_offset + (limit * sizeof(struct ext2_dx_entry)) > 385 fs->blocksize - sizeof(struct ext2_dx_tail)) 386 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; 387 /* htree structs are accessed in LE order */ 388 t = (struct ext2_dx_tail *)(((struct ext2_dx_entry *)c) + limit); 389 390 size = count_offset + (count * sizeof(struct ext2_dx_entry)); 391 392 retval = ext2fs_read_inode(fs, inum, &inode); 393 if (retval) 394 return retval; 395 396 inum = ext2fs_cpu_to_le32(inum); 397 gen = ext2fs_cpu_to_le32(inode.i_generation); 398 *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, 399 sizeof(inum)); 400 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); 401 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)buf, size); 402 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)t, 4); 403 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&dummy_csum, 4); 404 405 if (ret_t) 406 *ret_t = t; 407 return 0; 408 } 409 410 static int ext2fs_dx_csum_verify(ext2_filsys fs, ext2_ino_t inum, 411 struct ext2_dir_entry *dirent) 412 { 413 __u32 calculated; 414 errcode_t retval; 415 struct ext2_dx_tail *t; 416 417 retval = ext2fs_dx_csum(fs, inum, dirent, &calculated, &t); 418 if (retval) 419 return 0; 420 421 return ext2fs_le32_to_cpu(t->dt_checksum) == calculated; 422 } 423 424 static errcode_t ext2fs_dx_csum_set(ext2_filsys fs, ext2_ino_t inum, 425 struct ext2_dir_entry *dirent) 426 { 427 __u32 crc; 428 errcode_t retval = 0; 429 struct ext2_dx_tail *t; 430 431 retval = ext2fs_dx_csum(fs, inum, dirent, &crc, &t); 432 if (retval) 433 return retval; 434 t->dt_checksum = ext2fs_cpu_to_le32(crc); 435 return retval; 436 } 437 438 int ext2fs_dir_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, 439 struct ext2_dir_entry *dirent) 440 { 441 if (!ext2fs_has_feature_metadata_csum(fs->super)) 442 return 1; 443 444 if (__get_dirent_tail(fs, dirent, NULL, 1) == 0) 445 return ext2fs_dirent_csum_verify(fs, inum, dirent); 446 if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0) 447 return ext2fs_dx_csum_verify(fs, inum, dirent); 448 449 return 0; 450 } 451 452 errcode_t ext2fs_dir_block_csum_set(ext2_filsys fs, ext2_ino_t inum, 453 struct ext2_dir_entry *dirent) 454 { 455 if (!ext2fs_has_feature_metadata_csum(fs->super)) 456 return 0; 457 458 if (__get_dirent_tail(fs, dirent, NULL, 1) == 0) 459 return ext2fs_dirent_csum_set(fs, inum, dirent); 460 if (__get_dx_countlimit(fs, dirent, NULL, NULL, 1) == 0) 461 return ext2fs_dx_csum_set(fs, inum, dirent); 462 463 if (fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) 464 return 0; 465 return EXT2_ET_DIR_NO_SPACE_FOR_CSUM; 466 } 467 468 #define EXT3_EXTENT_TAIL_OFFSET(hdr) (sizeof(struct ext3_extent_header) + \ 469 (sizeof(struct ext3_extent) * ext2fs_le16_to_cpu((hdr)->eh_max))) 470 471 static struct ext3_extent_tail *get_extent_tail(struct ext3_extent_header *h) 472 { 473 return (struct ext3_extent_tail *)(((char *)h) + 474 EXT3_EXTENT_TAIL_OFFSET(h)); 475 } 476 477 static errcode_t ext2fs_extent_block_csum(ext2_filsys fs, ext2_ino_t inum, 478 struct ext3_extent_header *eh, 479 __u32 *crc) 480 { 481 int size; 482 __u32 gen; 483 errcode_t retval; 484 struct ext2_inode inode; 485 486 size = EXT3_EXTENT_TAIL_OFFSET(eh) + offsetof(struct ext3_extent_tail, 487 et_checksum); 488 489 retval = ext2fs_read_inode(fs, inum, &inode); 490 if (retval) 491 return retval; 492 inum = ext2fs_cpu_to_le32(inum); 493 gen = ext2fs_cpu_to_le32(inode.i_generation); 494 *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, 495 sizeof(inum)); 496 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); 497 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)eh, size); 498 499 return 0; 500 } 501 502 int ext2fs_extent_block_csum_verify(ext2_filsys fs, ext2_ino_t inum, 503 struct ext3_extent_header *eh) 504 { 505 errcode_t retval; 506 __u32 provided, calculated; 507 struct ext3_extent_tail *t = get_extent_tail(eh); 508 509 /* 510 * The extent tree structures are accessed in LE order, so we must 511 * swap the checksum bytes here. 512 */ 513 if (!ext2fs_has_feature_metadata_csum(fs->super)) 514 return 1; 515 516 provided = ext2fs_le32_to_cpu(t->et_checksum); 517 retval = ext2fs_extent_block_csum(fs, inum, eh, &calculated); 518 if (retval) 519 return 0; 520 521 return provided == calculated; 522 } 523 524 errcode_t ext2fs_extent_block_csum_set(ext2_filsys fs, ext2_ino_t inum, 525 struct ext3_extent_header *eh) 526 { 527 errcode_t retval; 528 __u32 crc; 529 struct ext3_extent_tail *t = get_extent_tail(eh); 530 531 if (!ext2fs_has_feature_metadata_csum(fs->super)) 532 return 0; 533 534 /* 535 * The extent tree structures are accessed in LE order, so we must 536 * swap the checksum bytes here. 537 */ 538 retval = ext2fs_extent_block_csum(fs, inum, eh, &crc); 539 if (retval) 540 return retval; 541 t->et_checksum = ext2fs_cpu_to_le32(crc); 542 return retval; 543 } 544 545 int ext2fs_inode_bitmap_csum_verify(ext2_filsys fs, dgrp_t group, 546 char *bitmap, int size) 547 { 548 struct ext4_group_desc *gdp = (struct ext4_group_desc *) 549 ext2fs_group_desc(fs, fs->group_desc, group); 550 __u32 provided, calculated; 551 552 if (!ext2fs_has_feature_metadata_csum(fs->super)) 553 return 1; 554 provided = gdp->bg_inode_bitmap_csum_lo; 555 calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, 556 size); 557 if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) 558 provided |= (__u32)gdp->bg_inode_bitmap_csum_hi << 16; 559 else 560 calculated &= 0xFFFF; 561 562 return provided == calculated; 563 } 564 565 errcode_t ext2fs_inode_bitmap_csum_set(ext2_filsys fs, dgrp_t group, 566 char *bitmap, int size) 567 { 568 __u32 crc; 569 struct ext4_group_desc *gdp = (struct ext4_group_desc *) 570 ext2fs_group_desc(fs, fs->group_desc, group); 571 572 if (!ext2fs_has_feature_metadata_csum(fs->super)) 573 return 0; 574 575 crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size); 576 gdp->bg_inode_bitmap_csum_lo = crc & 0xFFFF; 577 if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) 578 gdp->bg_inode_bitmap_csum_hi = crc >> 16; 579 580 return 0; 581 } 582 583 int ext2fs_block_bitmap_csum_verify(ext2_filsys fs, dgrp_t group, 584 char *bitmap, int size) 585 { 586 struct ext4_group_desc *gdp = (struct ext4_group_desc *) 587 ext2fs_group_desc(fs, fs->group_desc, group); 588 __u32 provided, calculated; 589 590 if (!ext2fs_has_feature_metadata_csum(fs->super)) 591 return 1; 592 provided = gdp->bg_block_bitmap_csum_lo; 593 calculated = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, 594 size); 595 if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) 596 provided |= (__u32)gdp->bg_block_bitmap_csum_hi << 16; 597 else 598 calculated &= 0xFFFF; 599 600 return provided == calculated; 601 } 602 603 errcode_t ext2fs_block_bitmap_csum_set(ext2_filsys fs, dgrp_t group, 604 char *bitmap, int size) 605 { 606 __u32 crc; 607 struct ext4_group_desc *gdp = (struct ext4_group_desc *) 608 ext2fs_group_desc(fs, fs->group_desc, group); 609 610 if (!ext2fs_has_feature_metadata_csum(fs->super)) 611 return 0; 612 613 crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)bitmap, size); 614 gdp->bg_block_bitmap_csum_lo = crc & 0xFFFF; 615 if (EXT2_DESC_SIZE(fs->super) >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) 616 gdp->bg_block_bitmap_csum_hi = crc >> 16; 617 618 return 0; 619 } 620 621 static errcode_t ext2fs_inode_csum(ext2_filsys fs, ext2_ino_t inum, 622 struct ext2_inode_large *inode, 623 __u32 *crc, int has_hi) 624 { 625 __u32 gen; 626 struct ext2_inode_large *desc = inode; 627 size_t size = EXT2_INODE_SIZE(fs->super); 628 __u16 old_lo; 629 __u16 old_hi = 0; 630 631 old_lo = inode->i_checksum_lo; 632 inode->i_checksum_lo = 0; 633 if (has_hi) { 634 old_hi = inode->i_checksum_hi; 635 inode->i_checksum_hi = 0; 636 } 637 638 inum = ext2fs_cpu_to_le32(inum); 639 gen = inode->i_generation; 640 *crc = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&inum, 641 sizeof(inum)); 642 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)&gen, sizeof(gen)); 643 *crc = ext2fs_crc32c_le(*crc, (unsigned char *)desc, size); 644 645 inode->i_checksum_lo = old_lo; 646 if (has_hi) 647 inode->i_checksum_hi = old_hi; 648 return 0; 649 } 650 651 int ext2fs_inode_csum_verify(ext2_filsys fs, ext2_ino_t inum, 652 struct ext2_inode_large *inode) 653 { 654 errcode_t retval; 655 __u32 provided, calculated; 656 unsigned int i, has_hi; 657 char *cp; 658 659 if (!ext2fs_has_feature_metadata_csum(fs->super)) 660 return 1; 661 662 has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE && 663 inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END); 664 665 provided = ext2fs_le16_to_cpu(inode->i_checksum_lo); 666 retval = ext2fs_inode_csum(fs, inum, inode, &calculated, has_hi); 667 if (retval) 668 return 0; 669 if (has_hi) { 670 __u32 hi = ext2fs_le16_to_cpu(inode->i_checksum_hi); 671 provided |= hi << 16; 672 } else 673 calculated &= 0xFFFF; 674 675 if (provided == calculated) 676 return 1; 677 678 /* 679 * If the checksum didn't match, it's possible it was due to 680 * the inode being all zero's. It's unlikely this is the 681 * case, but it can happen. So check for it here. (We only 682 * check the base inode since that's good enough, and it's not 683 * worth the bother to figure out how much of the extended 684 * inode, if any, is present.) 685 */ 686 for (cp = (char *) inode, i = 0; 687 i < sizeof(struct ext2_inode); 688 cp++, i++) 689 if (*cp) 690 return 0; 691 return 1; /* Inode must have been all zero's */ 692 } 693 694 errcode_t ext2fs_inode_csum_set(ext2_filsys fs, ext2_ino_t inum, 695 struct ext2_inode_large *inode) 696 { 697 errcode_t retval; 698 __u32 crc; 699 int has_hi; 700 701 if (!ext2fs_has_feature_metadata_csum(fs->super)) 702 return 0; 703 704 has_hi = (EXT2_INODE_SIZE(fs->super) > EXT2_GOOD_OLD_INODE_SIZE && 705 inode->i_extra_isize >= EXT4_INODE_CSUM_HI_EXTRA_END); 706 707 retval = ext2fs_inode_csum(fs, inum, inode, &crc, has_hi); 708 if (retval) 709 return retval; 710 inode->i_checksum_lo = ext2fs_cpu_to_le16(crc & 0xFFFF); 711 if (has_hi) 712 inode->i_checksum_hi = ext2fs_cpu_to_le16(crc >> 16); 713 return 0; 714 } 715 716 __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) 717 { 718 struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, 719 group); 720 size_t offset, size = EXT2_DESC_SIZE(fs->super); 721 __u16 crc = 0; 722 #ifdef WORDS_BIGENDIAN 723 struct ext4_group_desc swabdesc; 724 size_t save_size = size; 725 const size_t ext4_bg_size = sizeof(struct ext4_group_desc); 726 struct ext2_group_desc *save_desc = desc; 727 728 /* Have to swab back to little-endian to do the checksum */ 729 if (size > ext4_bg_size) 730 size = ext4_bg_size; 731 memcpy(&swabdesc, desc, size); 732 ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); 733 desc = (struct ext2_group_desc *) &swabdesc; 734 group = ext2fs_swab32(group); 735 #endif 736 737 if (ext2fs_has_feature_metadata_csum(fs->super)) { 738 /* new metadata csum code */ 739 __u16 old_crc; 740 __u32 crc32; 741 742 old_crc = desc->bg_checksum; 743 desc->bg_checksum = 0; 744 crc32 = ext2fs_crc32c_le(fs->csum_seed, (unsigned char *)&group, 745 sizeof(group)); 746 crc32 = ext2fs_crc32c_le(crc32, (unsigned char *)desc, 747 size); 748 desc->bg_checksum = old_crc; 749 #ifdef WORDS_BIGENDIAN 750 if (save_size > ext4_bg_size) 751 crc32 = ext2fs_crc32c_le(crc32, 752 (unsigned char *)save_desc + ext4_bg_size, 753 save_size - ext4_bg_size); 754 #endif 755 crc = crc32 & 0xFFFF; 756 goto out; 757 } 758 759 /* old crc16 code */ 760 offset = offsetof(struct ext2_group_desc, bg_checksum); 761 crc = ext2fs_crc16(~0, fs->super->s_uuid, 762 sizeof(fs->super->s_uuid)); 763 crc = ext2fs_crc16(crc, &group, sizeof(group)); 764 crc = ext2fs_crc16(crc, desc, offset); 765 offset += sizeof(desc->bg_checksum); /* skip checksum */ 766 /* for checksum of struct ext4_group_desc do the rest...*/ 767 if (offset < size) { 768 crc = ext2fs_crc16(crc, (char *)desc + offset, 769 size - offset); 770 } 771 #ifdef WORDS_BIGENDIAN 772 /* 773 * If the size of the bg descriptor is greater than 64 774 * bytes, which is the size of the traditional ext4 bg 775 * descriptor, checksum the rest of the descriptor here 776 */ 777 if (save_size > ext4_bg_size) 778 crc = ext2fs_crc16(crc, (char *)save_desc + ext4_bg_size, 779 save_size - ext4_bg_size); 780 #endif 781 782 out: 783 return crc; 784 } 785 786 int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) 787 { 788 if (ext2fs_has_group_desc_csum(fs) && 789 (ext2fs_bg_checksum(fs, group) != 790 ext2fs_group_desc_csum(fs, group))) 791 return 0; 792 793 return 1; 794 } 795 796 void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) 797 { 798 if (!ext2fs_has_group_desc_csum(fs)) 799 return; 800 801 /* ext2fs_bg_checksum_set() sets the actual checksum field but 802 * does not calculate the checksum itself. */ 803 ext2fs_bg_checksum_set(fs, group, ext2fs_group_desc_csum(fs, group)); 804 } 805 806 static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, 807 __u32 inodes_per_grp, dgrp_t grp_no) 808 { 809 ext2_ino_t i, start_ino, end_ino; 810 811 start_ino = grp_no * inodes_per_grp + 1; 812 end_ino = start_ino + inodes_per_grp - 1; 813 814 for (i = end_ino; i >= start_ino; i--) { 815 if (ext2fs_fast_test_inode_bitmap2(bitmap, i)) 816 return i - start_ino + 1; 817 } 818 return inodes_per_grp; 819 } 820 821 /* update the bitmap flags, set the itable high watermark, and calculate 822 * checksums for the group descriptors */ 823 errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) 824 { 825 struct ext2_super_block *sb = fs->super; 826 int dirty = 0; 827 dgrp_t i; 828 829 if (!fs->inode_map) 830 return EXT2_ET_NO_INODE_BITMAP; 831 832 if (!ext2fs_has_group_desc_csum(fs)) 833 return 0; 834 835 for (i = 0; i < fs->group_desc_count; i++) { 836 __u32 old_csum = ext2fs_bg_checksum(fs, i); 837 __u32 old_unused = ext2fs_bg_itable_unused(fs, i); 838 __u32 old_flags = ext2fs_bg_flags(fs, i); 839 __u32 old_free_inodes_count = ext2fs_bg_free_inodes_count(fs, i); 840 __u32 old_free_blocks_count = ext2fs_bg_free_blocks_count(fs, i); 841 842 if (old_free_blocks_count == sb->s_blocks_per_group && 843 i != fs->group_desc_count - 1) 844 ext2fs_bg_flags_set(fs, i, EXT2_BG_BLOCK_UNINIT); 845 846 if (old_free_inodes_count == sb->s_inodes_per_group) { 847 ext2fs_bg_flags_set(fs, i, EXT2_BG_INODE_UNINIT); 848 ext2fs_bg_itable_unused_set(fs, i, sb->s_inodes_per_group); 849 } else { 850 int unused = 851 sb->s_inodes_per_group - 852 find_last_inode_ingrp(fs->inode_map, 853 sb->s_inodes_per_group, i); 854 855 ext2fs_bg_flags_clear(fs, i, EXT2_BG_INODE_UNINIT); 856 ext2fs_bg_itable_unused_set(fs, i, unused); 857 } 858 859 ext2fs_group_desc_csum_set(fs, i); 860 if (old_flags != ext2fs_bg_flags(fs, i)) 861 dirty = 1; 862 if (old_unused != ext2fs_bg_itable_unused(fs, i)) 863 dirty = 1; 864 if (old_csum != ext2fs_bg_checksum(fs, i)) 865 dirty = 1; 866 } 867 if (dirty) 868 ext2fs_mark_super_dirty(fs); 869 return 0; 870 } 871 872 #ifdef DEBUG 873 #include "e2p/e2p.h" 874 875 void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) 876 { 877 __u16 crc1, crc2, crc3; 878 dgrp_t swabgroup; 879 struct ext2_group_desc *desc = ext2fs_group_desc(fs, fs->group_desc, 880 group); 881 size_t size = EXT2_DESC_SIZE(fs->super); 882 struct ext2_super_block *sb = fs->super; 883 int offset = offsetof(struct ext2_group_desc, bg_checksum); 884 #ifdef WORDS_BIGENDIAN 885 struct ext4_group_desc swabdesc; 886 struct ext2_group_desc *save_desc = desc; 887 const size_t ext4_bg_size = sizeof(struct ext4_group_desc); 888 size_t save_size = size; 889 #endif 890 891 #ifdef WORDS_BIGENDIAN 892 /* Have to swab back to little-endian to do the checksum */ 893 if (size > ext4_bg_size) 894 size = ext4_bg_size; 895 memcpy(&swabdesc, desc, size); 896 ext2fs_swap_group_desc2(fs, (struct ext2_group_desc *) &swabdesc); 897 desc = (struct ext2_group_desc *) &swabdesc; 898 899 swabgroup = ext2fs_swab32(group); 900 #else 901 swabgroup = group; 902 #endif 903 904 crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); 905 crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); 906 crc3 = ext2fs_crc16(crc2, desc, offset); 907 offset += sizeof(desc->bg_checksum); /* skip checksum */ 908 /* for checksum of struct ext4_group_desc do the rest...*/ 909 if (offset < size) 910 crc3 = ext2fs_crc16(crc3, (char *)desc + offset, size - offset); 911 #ifdef WORDS_BIGENDIAN 912 if (save_size > ext4_bg_size) 913 crc3 = ext2fs_crc16(crc3, (char *)save_desc + ext4_bg_size, 914 save_size - ext4_bg_size); 915 #endif 916 917 printf("%s UUID %s=%04x, grp %u=%04x: %04x=%04x\n", 918 msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2, crc3, 919 ext2fs_group_desc_csum(fs, group)); 920 } 921 922 unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, 923 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; 924 925 int main(int argc, char **argv) 926 { 927 struct ext2_super_block param; 928 errcode_t retval; 929 ext2_filsys fs; 930 int i; 931 __u16 csum1, csum2, csum_known = 0xd3a4; 932 933 memset(¶m, 0, sizeof(param)); 934 ext2fs_blocks_count_set(¶m, 32768); 935 #if 0 936 param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_64BIT; 937 param.s_desc_size = 128; 938 csum_known = 0x5b6e; 939 #endif 940 941 retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, ¶m, 942 test_io_manager, &fs); 943 if (retval) { 944 com_err("setup", retval, 945 "While initializing filesystem"); 946 exit(1); 947 } 948 memcpy(fs->super->s_uuid, sb_uuid, 16); 949 fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; 950 951 for (i=0; i < fs->group_desc_count; i++) { 952 ext2fs_block_bitmap_loc_set(fs, i, 124); 953 ext2fs_inode_bitmap_loc_set(fs, i, 125); 954 ext2fs_inode_table_loc_set(fs, i, 126); 955 ext2fs_bg_free_blocks_count_set(fs, i, 31119); 956 ext2fs_bg_free_inodes_count_set(fs, i, 15701); 957 ext2fs_bg_used_dirs_count_set(fs, i, 2); 958 ext2fs_bg_flags_zap(fs, i); 959 }; 960 961 csum1 = ext2fs_group_desc_csum(fs, 0); 962 print_csum("csum0000", fs, 0); 963 964 if (csum1 != csum_known) { 965 printf("checksum for group 0 should be %04x\n", csum_known); 966 exit(1); 967 } 968 csum2 = ext2fs_group_desc_csum(fs, 1); 969 print_csum("csum0001", fs, 1); 970 if (csum1 == csum2) { 971 printf("checksums for different groups shouldn't match\n"); 972 exit(1); 973 } 974 csum2 = ext2fs_group_desc_csum(fs, 2); 975 print_csum("csumffff", fs, 2); 976 if (csum1 == csum2) { 977 printf("checksums for different groups shouldn't match\n"); 978 exit(1); 979 } 980 ext2fs_bg_checksum_set(fs, 0, csum1); 981 csum2 = ext2fs_group_desc_csum(fs, 0); 982 print_csum("csum_set", fs, 0); 983 if (csum1 != csum2) { 984 printf("checksums should not depend on checksum field\n"); 985 exit(1); 986 } 987 if (!ext2fs_group_desc_csum_verify(fs, 0)) { 988 printf("checksums should verify against gd_checksum\n"); 989 exit(1); 990 } 991 memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); 992 print_csum("new_uuid", fs, 0); 993 if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { 994 printf("checksums for different filesystems shouldn't match\n"); 995 exit(1); 996 } 997 csum1 = ext2fs_group_desc_csum(fs, 0); 998 ext2fs_bg_checksum_set(fs, 0, csum1); 999 print_csum("csum_new", fs, 0); 1000 ext2fs_bg_free_blocks_count_set(fs, 0, 1); 1001 csum2 = ext2fs_group_desc_csum(fs, 0); 1002 print_csum("csum_blk", fs, 0); 1003 if (csum1 == csum2) { 1004 printf("checksums for different data shouldn't match\n"); 1005 exit(1); 1006 } 1007 ext2fs_free(fs); 1008 1009 return 0; 1010 } 1011 #endif