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 void 158 ext2_init_dirent_tail(struct ext2fs_direct_tail *tp) 159 { 160 memset(tp, 0, sizeof(struct ext2fs_direct_tail)); 161 tp->e2dt_rec_len = sizeof(struct ext2fs_direct_tail); 162 tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM; 163 } 164 165 int 166 ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 167 { 168 struct m_ext2fs *fs; 169 struct ext2fs_direct_tail *tp; 170 171 fs = ip->i_e2fs; 172 173 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 174 return (0); 175 176 tp = (struct ext2fs_direct_tail *)ep; 177 if (tp->e2dt_reserved_zero1 == 0 && 178 tp->e2dt_rec_len == sizeof(struct ext2fs_direct_tail) && 179 tp->e2dt_reserved_zero2 == 0 && 180 tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM) 181 return (1); 182 183 return (0); 184 } 185 186 struct ext2fs_direct_tail * 187 ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 188 { 189 struct ext2fs_direct_2 *dep; 190 void *top; 191 unsigned int rec_len; 192 193 dep = ep; 194 top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize); 195 rec_len = dep->e2d_reclen; 196 197 while (rec_len && !(rec_len & 0x3)) { 198 dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len); 199 if ((void *)dep >= top) 200 break; 201 rec_len = dep->e2d_reclen; 202 } 203 204 if (dep != top) 205 return (NULL); 206 207 if (ext2_is_dirent_tail(ip, dep)) 208 return ((struct ext2fs_direct_tail *)dep); 209 210 return (NULL); 211 } 212 213 static uint32_t 214 ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size) 215 { 216 struct m_ext2fs *fs; 217 char *buf; 218 uint32_t inum, gen, crc; 219 220 fs = ip->i_e2fs; 221 222 buf = (char *)ep; 223 224 inum = ip->i_number; 225 gen = ip->i_gen; 226 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 227 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 228 crc = calculate_crc32c(crc, (uint8_t *)buf, size); 229 230 return (crc); 231 } 232 233 int 234 ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 235 { 236 uint32_t calculated; 237 struct ext2fs_direct_tail *tp; 238 239 tp = ext2_dirent_get_tail(ip, ep); 240 if (tp == NULL) 241 return (0); 242 243 calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); 244 if (calculated != tp->e2dt_checksum) 245 return (EIO); 246 247 return (0); 248 } 249 250 static struct ext2fs_htree_count * 251 ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset) 252 { 253 struct ext2fs_direct_2 *dp; 254 struct ext2fs_htree_root_info *root; 255 int count_offset; 256 257 if (ep->e2d_reclen == EXT2_BLOCK_SIZE(ip->i_e2fs)) 258 count_offset = 8; 259 else if (ep->e2d_reclen == 12) { 260 dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12); 261 if (dp->e2d_reclen != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12) 262 return (NULL); 263 264 root = (struct ext2fs_htree_root_info *)(((char *)dp + 12)); 265 if (root->h_reserved1 || 266 root->h_info_len != sizeof(struct ext2fs_htree_root_info)) 267 return (NULL); 268 269 count_offset = 32; 270 } else 271 return (NULL); 272 273 if (offset) 274 *offset = count_offset; 275 276 return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset)); 277 } 278 279 static uint32_t 280 ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset, 281 int count, struct ext2fs_htree_tail *tp) 282 { 283 struct m_ext2fs *fs; 284 char *buf; 285 int size; 286 uint32_t inum, old_csum, gen, crc; 287 288 fs = ip->i_e2fs; 289 290 buf = (char *)ep; 291 292 size = count_offset + (count * sizeof(struct ext2fs_htree_entry)); 293 old_csum = tp->ht_checksum; 294 tp->ht_checksum = 0; 295 296 inum = ip->i_number; 297 gen = ip->i_gen; 298 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 299 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 300 crc = calculate_crc32c(crc, (uint8_t *)buf, size); 301 crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail)); 302 tp->ht_checksum = old_csum; 303 304 return (crc); 305 } 306 307 int 308 ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 309 { 310 uint32_t calculated; 311 struct ext2fs_htree_count *cp; 312 struct ext2fs_htree_tail *tp; 313 int count_offset, limit, count; 314 315 cp = ext2_get_dx_count(ip, ep, &count_offset); 316 if (cp == NULL) 317 return (0); 318 319 limit = cp->h_entries_max; 320 count = cp->h_entries_num; 321 if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 322 ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 323 return (EIO); 324 325 tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 326 calculated = ext2_dx_csum(ip, ep, count_offset, count, tp); 327 328 if (tp->ht_checksum != calculated) 329 return (EIO); 330 331 return (0); 332 } 333 334 int 335 ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp) 336 { 337 struct m_ext2fs *fs; 338 struct ext2fs_direct_2 *ep; 339 int error = 0; 340 341 fs = ip->i_e2fs; 342 343 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 344 return (error); 345 346 ep = (struct ext2fs_direct_2 *)bp->b_data; 347 348 if (ext2_dirent_get_tail(ip, ep) != NULL) 349 error = ext2_dirent_csum_verify(ip, ep); 350 else if (ext2_get_dx_count(ip, ep, NULL) != NULL) 351 error = ext2_dx_csum_verify(ip, ep); 352 353 if (error) 354 printf("WARNING: bad directory csum detected, ip=%lu" 355 " - run fsck\n", (unsigned long)ip->i_number); 356 357 return (error); 358 } 359 360 void 361 ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 362 { 363 struct m_ext2fs *fs; 364 struct ext2fs_direct_tail *tp; 365 366 fs = ip->i_e2fs; 367 368 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 369 return; 370 371 tp = ext2_dirent_get_tail(ip, ep); 372 if (tp == NULL) 373 return; 374 375 tp->e2dt_checksum = 376 ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); 377 } 378 379 void 380 ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 381 { 382 struct m_ext2fs *fs; 383 struct ext2fs_htree_count *cp; 384 struct ext2fs_htree_tail *tp; 385 int count_offset, limit, count; 386 387 fs = ip->i_e2fs; 388 389 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 390 return; 391 392 cp = ext2_get_dx_count(ip, ep, &count_offset); 393 if (cp == NULL) 394 return; 395 396 limit = cp->h_entries_max; 397 count = cp->h_entries_num; 398 if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 399 ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 400 return; 401 402 tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 403 tp->ht_checksum = ext2_dx_csum(ip, ep, count_offset, count, tp); 404 } 405 406 static uint32_t 407 ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp) 408 { 409 struct m_ext2fs *fs; 410 size_t size; 411 uint32_t inum, gen, crc; 412 413 fs = ip->i_e2fs; 414 415 size = EXT4_EXTENT_TAIL_OFFSET(ehp) + 416 offsetof(struct ext4_extent_tail, et_checksum); 417 418 inum = ip->i_number; 419 gen = ip->i_gen; 420 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 421 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 422 crc = calculate_crc32c(crc, (uint8_t *)ehp, size); 423 424 return (crc); 425 } 426 427 int 428 ext2_extent_blk_csum_verify(struct inode *ip, void *data) 429 { 430 struct m_ext2fs *fs; 431 struct ext4_extent_header *ehp; 432 struct ext4_extent_tail *etp; 433 uint32_t provided, calculated; 434 435 fs = ip->i_e2fs; 436 437 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 438 return (0); 439 440 ehp = (struct ext4_extent_header *)data; 441 etp = (struct ext4_extent_tail *)(((char *)ehp) + 442 EXT4_EXTENT_TAIL_OFFSET(ehp)); 443 444 provided = etp->et_checksum; 445 calculated = ext2_extent_blk_csum(ip, ehp); 446 447 if (provided != calculated) { 448 printf("WARNING: bad extent csum detected, ip=%lu - run fsck\n", 449 (unsigned long)ip->i_number); 450 return (EIO); 451 } 452 453 return (0); 454 } 455 456 void 457 ext2_extent_blk_csum_set(struct inode *ip, void *data) 458 { 459 struct m_ext2fs *fs; 460 struct ext4_extent_header *ehp; 461 struct ext4_extent_tail *etp; 462 463 fs = ip->i_e2fs; 464 465 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 466 return; 467 468 ehp = (struct ext4_extent_header *)data; 469 etp = (struct ext4_extent_tail *)(((char *)data) + 470 EXT4_EXTENT_TAIL_OFFSET(ehp)); 471 472 etp->et_checksum = ext2_extent_blk_csum(ip, 473 (struct ext4_extent_header *)data); 474 } 475 476 int 477 ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 478 { 479 uint32_t hi, provided, calculated; 480 481 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 482 return (0); 483 484 provided = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum; 485 calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 486 fs->e2fs->e2fs_ipg / 8); 487 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) { 488 hi = fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi; 489 provided |= (hi << 16); 490 } else 491 calculated &= 0xFFFF; 492 493 if (provided != calculated) { 494 printf("WARNING: bad inode bitmap csum detected, " 495 "cg=%d - run fsck\n", cg); 496 return (EIO); 497 } 498 499 return (0); 500 } 501 502 void 503 ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 504 { 505 uint32_t csum; 506 507 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 508 return; 509 510 csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 511 fs->e2fs->e2fs_ipg / 8); 512 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = csum & 0xFFFF; 513 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) 514 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = csum >> 16; 515 } 516 517 int 518 ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 519 { 520 uint32_t hi, provided, calculated, size; 521 522 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 523 return (0); 524 525 size = fs->e2fs_fpg / 8; 526 provided = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum; 527 calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 528 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) { 529 hi = fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi; 530 provided |= (hi << 16); 531 } else 532 calculated &= 0xFFFF; 533 534 if (provided != calculated) { 535 printf("WARNING: bad block bitmap csum detected, " 536 "cg=%d - run fsck\n", cg); 537 return (EIO); 538 } 539 540 return (0); 541 } 542 543 void 544 ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 545 { 546 uint32_t csum, size; 547 548 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 549 return; 550 551 size = fs->e2fs_fpg / 8; 552 csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 553 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = csum & 0xFFFF; 554 if (fs->e2fs->e3fs_desc_size >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) 555 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = csum >> 16; 556 } 557 558 static uint32_t 559 ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei) 560 { 561 struct m_ext2fs *fs; 562 uint32_t inode_csum_seed, inum, gen, crc; 563 uint16_t dummy_csum = 0; 564 unsigned int offset, csum_size; 565 566 fs = ip->i_e2fs; 567 offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo); 568 csum_size = sizeof(dummy_csum); 569 inum = ip->i_number; 570 crc = calculate_crc32c(fs->e2fs_csum_seed, 571 (uint8_t *)&inum, sizeof(inum)); 572 gen = ip->i_gen; 573 inode_csum_seed = calculate_crc32c(crc, 574 (uint8_t *)&gen, sizeof(gen)); 575 576 crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset); 577 crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size); 578 offset += csum_size; 579 crc = calculate_crc32c(crc, (uint8_t *)ei + offset, 580 E2FS_REV0_INODE_SIZE - offset); 581 582 if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) { 583 offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi); 584 crc = calculate_crc32c(crc, (uint8_t *)ei + 585 E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE); 586 587 if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE && 588 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) { 589 crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, 590 csum_size); 591 offset += csum_size; 592 } 593 594 crc = calculate_crc32c(crc, (uint8_t *)ei + offset, 595 EXT2_INODE_SIZE(fs) - offset); 596 } 597 598 return (crc); 599 } 600 601 int 602 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei) 603 { 604 struct m_ext2fs *fs; 605 const static struct ext2fs_dinode ei_zero; 606 uint32_t hi, provided, calculated; 607 608 fs = ip->i_e2fs; 609 610 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 611 return (0); 612 613 provided = ei->e2di_chksum_lo; 614 calculated = ext2_ei_csum(ip, ei); 615 616 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 617 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) { 618 hi = ei->e2di_chksum_hi; 619 provided |= hi << 16; 620 } else 621 calculated &= 0xFFFF; 622 623 if (provided != calculated) { 624 /* 625 * If it is first time used dinode, 626 * it is expected that it will be zeroed 627 * and we will not return checksum error in this case. 628 */ 629 if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) 630 return (0); 631 632 printf("WARNING: Bad inode %ju csum - run fsck\n", ip->i_number); 633 634 return (EIO); 635 } 636 637 return (0); 638 } 639 640 void 641 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei) 642 { 643 struct m_ext2fs *fs; 644 uint32_t crc; 645 646 fs = ip->i_e2fs; 647 648 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 649 return; 650 651 crc = ext2_ei_csum(ip, ei); 652 653 ei->e2di_chksum_lo = crc & 0xFFFF; 654 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 655 ei->e2di_extra_isize >= EXT2_INODE_CSUM_HI_EXTRA_END)) 656 ei->e2di_chksum_hi = crc >> 16; 657 } 658 659 static uint16_t 660 ext2_crc16(uint16_t crc, const void *buffer, unsigned int len) 661 { 662 const unsigned char *cp = buffer; 663 /* CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1). */ 664 static uint16_t const crc16_table[256] = { 665 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 666 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 667 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 668 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 669 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 670 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 671 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 672 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 673 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 674 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 675 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 676 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 677 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 678 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 679 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 680 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 681 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 682 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 683 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 684 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 685 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 686 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 687 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 688 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 689 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 690 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 691 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 692 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 693 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 694 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 695 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 696 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 697 }; 698 699 while (len--) 700 crc = (((crc >> 8) & 0xffU) ^ 701 crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; 702 return crc; 703 } 704 705 static uint16_t 706 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd) 707 { 708 size_t offset; 709 uint32_t csum32; 710 uint16_t crc, dummy_csum; 711 712 offset = offsetof(struct ext2_gd, ext4bgd_csum); 713 714 if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 715 csum32 = calculate_crc32c(fs->e2fs_csum_seed, 716 (uint8_t *)&block_group, sizeof(block_group)); 717 csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset); 718 dummy_csum = 0; 719 csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum, 720 sizeof(dummy_csum)); 721 offset += sizeof(dummy_csum); 722 if (offset < fs->e2fs->e3fs_desc_size) 723 csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset, 724 fs->e2fs->e3fs_desc_size - offset); 725 726 crc = csum32 & 0xFFFF; 727 return (crc); 728 } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { 729 crc = ext2_crc16(~0, fs->e2fs->e2fs_uuid, 730 sizeof(fs->e2fs->e2fs_uuid)); 731 crc = ext2_crc16(crc, (uint8_t *)&block_group, 732 sizeof(block_group)); 733 crc = ext2_crc16(crc, (uint8_t *)gd, offset); 734 offset += sizeof(gd->ext4bgd_csum); /* skip checksum */ 735 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) && 736 offset < fs->e2fs->e3fs_desc_size) 737 crc = ext2_crc16(crc, (uint8_t *)gd + offset, 738 fs->e2fs->e3fs_desc_size - offset); 739 return (crc); 740 } 741 742 return (0); 743 } 744 745 int 746 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev) 747 { 748 unsigned int i; 749 int error = 0; 750 751 for (i = 0; i < fs->e2fs_gcount; i++) { 752 if (fs->e2fs_gd[i].ext4bgd_csum != 753 ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) { 754 printf( 755 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n", 756 devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum, 757 ext2_gd_csum(fs, i, &fs->e2fs_gd[i])); 758 error = EIO; 759 break; 760 } 761 } 762 763 return (error); 764 } 765 766 void 767 ext2_gd_csum_set(struct m_ext2fs *fs) 768 { 769 unsigned int i; 770 771 for (i = 0; i < fs->e2fs_gcount; i++) 772 fs->e2fs_gd[i].ext4bgd_csum = 773 ext2_gd_csum(fs, i, &fs->e2fs_gd[i]); 774 } 775