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