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