1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2017, Fedor Uporov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/kernel.h> 36 #include <sys/malloc.h> 37 #include <sys/vnode.h> 38 #include <sys/bio.h> 39 #include <sys/buf.h> 40 #include <sys/endian.h> 41 #include <sys/conf.h> 42 #include <sys/mount.h> 43 44 #include <fs/ext2fs/fs.h> 45 #include <fs/ext2fs/ext2fs.h> 46 #include <fs/ext2fs/ext2_dinode.h> 47 #include <fs/ext2fs/inode.h> 48 #include <fs/ext2fs/ext2_dir.h> 49 #include <fs/ext2fs/htree.h> 50 #include <fs/ext2fs/ext2_extattr.h> 51 #include <fs/ext2fs/ext2_extern.h> 52 53 #define EXT2_BG_INODE_BITMAP_CSUM_HI_END \ 54 (offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \ 55 sizeof(uint16_t)) 56 57 #define EXT2_INODE_CSUM_HI_EXTRA_END \ 58 (offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \ 59 E2FS_REV0_INODE_SIZE) 60 61 #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \ 62 (offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \ 63 sizeof(uint16_t)) 64 65 void 66 ext2_sb_csum_set_seed(struct m_ext2fs *fs) 67 { 68 69 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED)) 70 fs->e2fs_csum_seed = fs->e2fs->e4fs_chksum_seed; 71 else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 72 fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid, 73 sizeof(fs->e2fs->e2fs_uuid)); 74 } 75 else 76 fs->e2fs_csum_seed = 0; 77 } 78 79 int 80 ext2_sb_csum_verify(struct m_ext2fs *fs) 81 { 82 83 if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) { 84 printf( 85 "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt); 86 return (EINVAL); 87 } 88 if (fs->e2fs->e4fs_sbchksum != 89 calculate_crc32c(~0, (const char *)fs->e2fs, 90 offsetof(struct ext2fs, e4fs_sbchksum))) { 91 printf( 92 "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n", 93 fs->e2fs_fsmnt, fs->e2fs->e4fs_sbchksum, calculate_crc32c(~0, 94 (const char *)fs->e2fs, offsetof(struct ext2fs, e4fs_sbchksum))); 95 return (EINVAL); 96 } 97 98 return (0); 99 } 100 101 void 102 ext2_sb_csum_set(struct m_ext2fs *fs) 103 { 104 105 fs->e2fs->e4fs_sbchksum = calculate_crc32c(~0, (const char *)fs->e2fs, 106 offsetof(struct ext2fs, e4fs_sbchksum)); 107 } 108 109 static uint32_t 110 ext2_extattr_blk_csum(struct inode *ip, uint64_t facl, 111 struct ext2fs_extattr_header *header) 112 { 113 struct m_ext2fs *fs; 114 uint32_t crc, old_crc; 115 116 fs = ip->i_e2fs; 117 118 old_crc = header->h_checksum; 119 120 header->h_checksum = 0; 121 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl, sizeof(facl)); 122 crc = calculate_crc32c(crc, (uint8_t *)header, fs->e2fs_bsize); 123 header->h_checksum = old_crc; 124 125 return (crc); 126 } 127 128 int 129 ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp) 130 { 131 struct ext2fs_extattr_header *header; 132 133 header = (struct ext2fs_extattr_header *)bp->b_data; 134 135 if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) && 136 (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) { 137 printf("WARNING: bad extattr csum detected, ip=%lu - run fsck\n", 138 (unsigned long)ip->i_number); 139 return (EIO); 140 } 141 142 return (0); 143 } 144 145 void 146 ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp) 147 { 148 struct ext2fs_extattr_header *header; 149 150 if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 151 return; 152 153 header = (struct ext2fs_extattr_header *)bp->b_data; 154 header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header); 155 } 156 157 static struct ext2fs_direct_tail * 158 ext2_get_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 159 { 160 struct ext2fs_direct_tail *tp; 161 162 tp = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize); 163 if (tp->e2dt_reserved_zero1 || 164 tp->e2dt_rec_len != sizeof(struct ext2fs_direct_tail) || 165 tp->e2dt_reserved_zero2 || 166 tp->e2dt_reserved_ft != EXT2_FT_DIR_CSUM) 167 return (NULL); 168 169 return (tp); 170 } 171 172 static uint32_t 173 ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size) 174 { 175 struct m_ext2fs *fs; 176 char *buf; 177 uint32_t inum, gen, crc; 178 179 fs = ip->i_e2fs; 180 181 buf = (char *)ep; 182 183 inum = ip->i_number; 184 gen = ip->i_gen; 185 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 186 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 187 crc = calculate_crc32c(crc, (uint8_t *)buf, size); 188 189 return (crc); 190 } 191 192 static int 193 ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 194 { 195 uint32_t calculated; 196 struct ext2fs_direct_tail *tp; 197 198 tp = ext2_get_dirent_tail(ip, ep); 199 if (tp == NULL) 200 return (0); 201 202 calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); 203 if (calculated != tp->e2dt_checksum) 204 return (EIO); 205 206 return (0); 207 } 208 209 static struct ext2fs_htree_count * 210 ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset) 211 { 212 struct ext2fs_direct_2 *dp; 213 struct ext2fs_htree_root_info *root; 214 int count_offset; 215 216 if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs)) 217 count_offset = 8; 218 else if (ep->e2d_reclen == 12) { 219 dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12); 220 if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12) 221 return (NULL); 222 223 root = (struct ext2fs_htree_root_info *)(((char *)dp + 12)); 224 if (root->h_reserved1 || 225 root->h_info_len != sizeof(struct ext2fs_htree_root_info)) 226 return (NULL); 227 228 count_offset = 32; 229 } else 230 return (NULL); 231 232 if (offset) 233 *offset = count_offset; 234 235 return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset)); 236 } 237 238 static uint32_t 239 ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset, 240 int count, struct ext2fs_htree_tail *tp) 241 { 242 struct m_ext2fs *fs; 243 char *buf; 244 int size; 245 uint32_t inum, old_csum, gen, crc; 246 247 fs = ip->i_e2fs; 248 249 buf = (char *)ep; 250 251 size = count_offset + (count * sizeof(struct ext2fs_htree_entry)); 252 old_csum = tp->ht_checksum; 253 tp->ht_checksum = 0; 254 255 inum = ip->i_number; 256 gen = ip->i_gen; 257 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 258 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 259 crc = calculate_crc32c(crc, (uint8_t *)buf, size); 260 crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail)); 261 tp->ht_checksum = old_csum; 262 263 return (crc); 264 } 265 266 static int 267 ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 268 { 269 uint32_t calculated; 270 struct ext2fs_htree_count *cp; 271 struct ext2fs_htree_tail *tp; 272 int count_offset, limit, count; 273 274 cp = ext2_get_dx_count(ip, ep, &count_offset); 275 if (cp == NULL) 276 return (0); 277 278 limit = cp->h_entries_max; 279 count = cp->h_entries_num; 280 if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 281 ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 282 return (EIO); 283 284 tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 285 calculated = ext2_dx_csum(ip, ep, count_offset, count, tp); 286 287 if (tp->ht_checksum != calculated) 288 return (EIO); 289 290 return (0); 291 } 292 293 int 294 ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp) 295 { 296 struct m_ext2fs *fs; 297 struct ext2fs_direct_2 *ep; 298 int error = 0; 299 300 fs = ip->i_e2fs; 301 302 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 303 return (error); 304 305 ep = (struct ext2fs_direct_2 *)bp->b_data; 306 307 if (ext2_get_dirent_tail(ip, ep) != NULL) 308 error = ext2_dirent_csum_verify(ip, ep); 309 else if (ext2_get_dx_count(ip, ep, NULL) != NULL) 310 error = ext2_dx_csum_verify(ip, ep); 311 312 if (error) 313 printf("WARNING: bad directory csum detected, ip=%lu" 314 " - run fsck\n", (unsigned long)ip->i_number); 315 316 return (error); 317 } 318 319 static void 320 ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 321 { 322 struct ext2fs_direct_tail *tp; 323 324 tp = ext2_get_dirent_tail(ip, ep); 325 if (tp == NULL) 326 return; 327 328 tp->e2dt_checksum = 329 ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); 330 } 331 332 static void 333 ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 334 { 335 struct ext2fs_htree_count *cp; 336 struct ext2fs_htree_tail *tp; 337 int count_offset, limit, count; 338 339 cp = ext2_get_dx_count(ip, ep, &count_offset); 340 if (cp == NULL) 341 return; 342 343 limit = cp->h_entries_max; 344 count = cp->h_entries_num; 345 if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 346 ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 347 return; 348 349 tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 350 tp->ht_checksum = ext2_dx_csum(ip, ep, count_offset, count, tp); 351 } 352 353 void 354 ext2_dir_blk_csum_set_mem(struct inode *ip, char *buf, int size) 355 { 356 struct m_ext2fs *fs; 357 struct ext2fs_direct_2 *ep; 358 359 fs = ip->i_e2fs; 360 361 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 362 return; 363 364 ep = (struct ext2fs_direct_2 *)buf; 365 366 if (ext2_htree_has_idx(ip)) { 367 if (ext2_get_dx_count(ip, ep, NULL) != NULL) 368 ext2_dx_csum_set(ip, ep); 369 } else { 370 if (ext2_get_dirent_tail(ip, ep) != NULL) 371 ext2_dirent_csum_set(ip, ep); 372 } 373 } 374 375 void 376 ext2_dir_blk_csum_set(struct inode *ip, struct buf *bp) 377 { 378 379 ext2_dir_blk_csum_set_mem(ip, bp->b_data, bp->b_bufsize); 380 } 381 382 static uint32_t 383 ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp) 384 { 385 struct m_ext2fs *fs; 386 size_t size; 387 uint32_t inum, gen, crc; 388 389 fs = ip->i_e2fs; 390 391 size = EXT4_EXTENT_TAIL_OFFSET(ehp) + 392 offsetof(struct ext4_extent_tail, et_checksum); 393 394 inum = ip->i_number; 395 gen = ip->i_gen; 396 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 397 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 398 crc = calculate_crc32c(crc, (uint8_t *)ehp, size); 399 400 return (crc); 401 } 402 403 int 404 ext2_extent_blk_csum_verify(struct inode *ip, void *data) 405 { 406 struct m_ext2fs *fs; 407 struct ext4_extent_header *ehp; 408 struct ext4_extent_tail *etp; 409 uint32_t provided, calculated; 410 411 fs = ip->i_e2fs; 412 413 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 414 return (0); 415 416 ehp = (struct ext4_extent_header *)data; 417 etp = (struct ext4_extent_tail *)(((char *)ehp) + 418 EXT4_EXTENT_TAIL_OFFSET(ehp)); 419 420 provided = etp->et_checksum; 421 calculated = ext2_extent_blk_csum(ip, ehp); 422 423 if (provided != calculated) { 424 printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n", 425 (unsigned long)ip->i_number); 426 return (EIO); 427 } 428 429 return (0); 430 } 431 432 void 433 ext2_extent_blk_csum_set(struct inode *ip, void *data) 434 { 435 struct m_ext2fs *fs; 436 struct ext4_extent_header *ehp; 437 struct ext4_extent_tail *etp; 438 439 fs = ip->i_e2fs; 440 441 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 442 return; 443 444 ehp = (struct ext4_extent_header *)data; 445 etp = (struct ext4_extent_tail *)(((char *)data) + 446 EXT4_EXTENT_TAIL_OFFSET(ehp)); 447 448 etp->et_checksum = ext2_extent_blk_csum(ip, 449 (struct ext4_extent_header *)data); 450 } 451 452 int 453 ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 454 { 455 uint32_t hi, provided, calculated; 456 457 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 458 return (0); 459 460 provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum; 461 calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 462 fs->e2fs->e2fs_ipg / 8); 463 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) { 464 hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi; 465 provided |= (hi << 16); 466 } else 467 calculated &= 0xFFFF; 468 469 if (provided != calculated) { 470 printf("WARNING: bad inode bitmap csum detected, " 471 "cg=%d - run fsck\n", cg); 472 return (EIO); 473 } 474 475 return (0); 476 } 477 478 void 479 ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 480 { 481 uint32_t csum; 482 483 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 484 return; 485 486 csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 487 fs->e2fs->e2fs_ipg / 8); 488 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF; 489 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) 490 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16; 491 } 492 493 int 494 ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 495 { 496 uint32_t hi, provided, calculated, size; 497 498 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 499 return (0); 500 501 size = fs->e2fs_fpg / 8; 502 provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum; 503 calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 504 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) { 505 hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi; 506 provided |= (hi << 16); 507 } else 508 calculated &= 0xFFFF; 509 510 if (provided != calculated) { 511 printf("WARNING: bad block bitmap csum detected, " 512 "cg=%d - run fsck\n", cg); 513 return (EIO); 514 } 515 516 return (0); 517 } 518 519 void 520 ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 521 { 522 uint32_t csum, size; 523 524 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 525 return; 526 527 size = fs->e2fs_fpg / 8; 528 csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 529 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF; 530 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) 531 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16; 532 } 533 534 static uint32_t 535 ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei) 536 { 537 struct m_ext2fs *fs; 538 uint16_t old_hi; 539 uint32_t inum, gen, crc; 540 541 fs = ip->i_e2fs; 542 543 ei->e2di_chksum_lo = 0; 544 if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE && 545 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) { 546 old_hi = ei->e2di_chksum_hi; 547 ei->e2di_chksum_hi = 0; 548 } 549 550 inum = ip->i_number; 551 gen = ip->i_gen; 552 553 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 554 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 555 crc = calculate_crc32c(crc, (uint8_t *)ei, fs->e2fs->e2fs_inode_size); 556 557 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 558 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) 559 ei->e2di_chksum_hi = old_hi; 560 561 return (crc); 562 } 563 564 int 565 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei) 566 { 567 struct m_ext2fs *fs; 568 const static struct ext2fs_dinode ei_zero; 569 uint32_t hi, provided, calculated; 570 571 fs = ip->i_e2fs; 572 573 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 574 return (0); 575 576 /* Check case, when dinode was not initialized */ 577 if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) 578 return (0); 579 580 provided = ei->e2di_chksum_lo; 581 calculated = ext2_ei_csum(ip, ei); 582 583 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 584 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) { 585 hi = ei->e2di_chksum_hi; 586 provided |= hi << 16; 587 } else 588 calculated &= 0xFFFF; 589 590 if (provided != calculated) 591 return (EIO); 592 593 return (0); 594 } 595 596 void 597 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei) 598 { 599 struct m_ext2fs *fs; 600 uint32_t crc; 601 602 fs = ip->i_e2fs; 603 604 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 605 return; 606 607 crc = ext2_ei_csum(ip, ei); 608 609 ei->e2di_chksum_lo = crc & 0xFFFF; 610 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 611 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) 612 ei->e2di_chksum_hi = crc >> 16; 613 } 614 615 static uint16_t 616 ext2_crc16(uint16_t crc, const void *buffer, unsigned int len) 617 { 618 const unsigned char *cp = buffer; 619 /* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */ 620 static uint16_t const crc16_table[256] = { 621 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 622 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 623 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 624 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 625 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 626 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 627 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 628 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 629 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 630 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 631 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 632 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 633 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 634 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 635 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 636 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 637 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 638 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 639 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 640 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 641 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 642 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 643 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 644 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 645 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 646 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 647 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 648 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 649 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 650 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 651 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 652 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 653 }; 654 655 while (len--) 656 crc = (((crc >> 8) & 0xffU) ^ 657 crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; 658 return crc; 659 } 660 661 static uint16_t 662 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd) 663 { 664 size_t offset; 665 uint32_t csum32; 666 uint16_t crc, dummy_csum; 667 668 offset = offsetof(struct ext2_gd, ext4bgd_csum); 669 670 if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 671 csum32 = calculate_crc32c(fs->e2fs_csum_seed, 672 (uint8_t *)&block_group, sizeof(block_group)); 673 csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset); 674 dummy_csum = 0; 675 csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum, 676 sizeof(dummy_csum)); 677 offset += sizeof(dummy_csum); 678 if (offset < fs->e2fs->e3fs_desc_size) 679 csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset, 680 fs->e2fs->e3fs_desc_size - offset); 681 682 crc = csum32 & 0xFFFF; 683 return (crc); 684 } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { 685 crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid, 686 sizeof(fs->e2fs->e2fs_uuid)); 687 crc = ext2_crc16(crc, (uint8_t *)&block_group, 688 sizeof(block_group)); 689 crc = ext2_crc16(crc, (uint8_t *)gd, offset); 690 offset += sizeof(gd->ext4bgd_csum); /* skip checksum */ 691 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) && 692 offset < fs->e2fs->e3fs_desc_size) 693 crc = ext2_crc16(crc, (uint8_t *)gd + offset, 694 fs->e2fs->e3fs_desc_size - offset); 695 return (crc); 696 } 697 698 return (0); 699 } 700 701 int 702 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev) 703 { 704 unsigned int i; 705 int error = 0; 706 707 for (i = 0; i < fs->e2fs_gcount; i++) { 708 if (fs->e2fs_gd[i].ext4bgd_csum != 709 ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) { 710 printf( 711 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n", 712 devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum, 713 ext2_gd_csum(fs, i, &fs->e2fs_gd[i])); 714 error = EIO; 715 break; 716 } 717 } 718 719 return (error); 720 } 721 722 void 723 ext2_gd_csum_set(struct m_ext2fs *fs) 724 { 725 unsigned int i; 726 727 for (i = 0; i < fs->e2fs_gcount; i++) 728 fs->e2fs_gd[i].ext4bgd_csum = 729 ext2_gd_csum(fs, i, &fs->e2fs_gd[i]); 730 } 731