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/crc16.h> 45 #include <sys/mount.h> 46 47 #include <fs/ext2fs/fs.h> 48 #include <fs/ext2fs/ext2fs.h> 49 #include <fs/ext2fs/ext2_dinode.h> 50 #include <fs/ext2fs/inode.h> 51 #include <fs/ext2fs/ext2_dir.h> 52 #include <fs/ext2fs/htree.h> 53 #include <fs/ext2fs/ext2_extattr.h> 54 #include <fs/ext2fs/ext2_extern.h> 55 56 SDT_PROVIDER_DECLARE(ext2fs); 57 /* 58 * ext2fs trace probe: 59 * arg0: verbosity. Higher numbers give more verbose messages 60 * arg1: Textual message 61 */ 62 SDT_PROBE_DEFINE2(ext2fs, , trace, csum, "int", "char*"); 63 64 #define EXT2_BG_INODE_BITMAP_CSUM_HI_END \ 65 (offsetof(struct ext2_gd, ext4bgd_i_bmap_csum_hi) + \ 66 sizeof(uint16_t)) 67 68 #define EXT2_INODE_CSUM_HI_EXTRA_END \ 69 (offsetof(struct ext2fs_dinode, e2di_chksum_hi) + sizeof(uint16_t) - \ 70 E2FS_REV0_INODE_SIZE) 71 72 #define EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION \ 73 (offsetof(struct ext2_gd, ext4bgd_b_bmap_csum_hi) + \ 74 sizeof(uint16_t)) 75 76 void 77 ext2_sb_csum_set_seed(struct m_ext2fs *fs) 78 { 79 80 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_CSUM_SEED)) 81 fs->e2fs_csum_seed = le32toh(fs->e2fs->e4fs_chksum_seed); 82 else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 83 fs->e2fs_csum_seed = calculate_crc32c(~0, fs->e2fs->e2fs_uuid, 84 sizeof(fs->e2fs->e2fs_uuid)); 85 } 86 else 87 fs->e2fs_csum_seed = 0; 88 } 89 90 int 91 ext2_sb_csum_verify(struct m_ext2fs *fs) 92 { 93 94 if (fs->e2fs->e4fs_chksum_type != EXT4_CRC32C_CHKSUM) { 95 printf( 96 "WARNING: mount of %s denied due bad sb csum type\n", fs->e2fs_fsmnt); 97 return (EINVAL); 98 } 99 if (le32toh(fs->e2fs->e4fs_sbchksum) != 100 calculate_crc32c(~0, (const char *)fs->e2fs, 101 offsetof(struct ext2fs, e4fs_sbchksum))) { 102 printf( 103 "WARNING: mount of %s denied due bad sb csum=0x%x, expected=0x%x - run fsck\n", 104 fs->e2fs_fsmnt, le32toh(fs->e2fs->e4fs_sbchksum), 105 calculate_crc32c(~0, (const char *)fs->e2fs, 106 offsetof(struct ext2fs, e4fs_sbchksum))); 107 return (EINVAL); 108 } 109 110 return (0); 111 } 112 113 void 114 ext2_sb_csum_set(struct m_ext2fs *fs) 115 { 116 117 fs->e2fs->e4fs_sbchksum = 118 htole32(calculate_crc32c(~0, (const char *)fs->e2fs, 119 offsetof(struct ext2fs, e4fs_sbchksum))); 120 } 121 122 static uint32_t 123 ext2_extattr_blk_csum(struct inode *ip, uint64_t facl, 124 struct ext2fs_extattr_header *header) 125 { 126 struct m_ext2fs *fs; 127 uint32_t crc, dummy_crc = 0; 128 uint64_t facl_bn = htole64(facl); 129 int offset = offsetof(struct ext2fs_extattr_header, h_checksum); 130 131 fs = ip->i_e2fs; 132 133 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&facl_bn, 134 sizeof(facl_bn)); 135 crc = calculate_crc32c(crc, (uint8_t *)header, offset); 136 crc = calculate_crc32c(crc, (uint8_t *)&dummy_crc, 137 sizeof(dummy_crc)); 138 offset += sizeof(dummy_crc); 139 crc = calculate_crc32c(crc, (uint8_t *)header + offset, 140 fs->e2fs_bsize - offset); 141 142 return (htole32(crc)); 143 } 144 145 int 146 ext2_extattr_blk_csum_verify(struct inode *ip, struct buf *bp) 147 { 148 struct ext2fs_extattr_header *header; 149 150 header = (struct ext2fs_extattr_header *)bp->b_data; 151 152 if (EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM) && 153 (header->h_checksum != ext2_extattr_blk_csum(ip, ip->i_facl, header))) { 154 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extattr csum detected"); 155 return (EIO); 156 } 157 158 return (0); 159 } 160 161 void 162 ext2_extattr_blk_csum_set(struct inode *ip, struct buf *bp) 163 { 164 struct ext2fs_extattr_header *header; 165 166 if (!EXT2_HAS_RO_COMPAT_FEATURE(ip->i_e2fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 167 return; 168 169 header = (struct ext2fs_extattr_header *)bp->b_data; 170 header->h_checksum = ext2_extattr_blk_csum(ip, ip->i_facl, header); 171 } 172 173 void 174 ext2_init_dirent_tail(struct ext2fs_direct_tail *tp) 175 { 176 memset(tp, 0, sizeof(struct ext2fs_direct_tail)); 177 tp->e2dt_rec_len = le16toh(sizeof(struct ext2fs_direct_tail)); 178 tp->e2dt_reserved_ft = EXT2_FT_DIR_CSUM; 179 } 180 181 int 182 ext2_is_dirent_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 183 { 184 struct m_ext2fs *fs; 185 struct ext2fs_direct_tail *tp; 186 187 fs = ip->i_e2fs; 188 189 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 190 return (0); 191 192 tp = (struct ext2fs_direct_tail *)ep; 193 if (tp->e2dt_reserved_zero1 == 0 && 194 le16toh(tp->e2dt_rec_len) == sizeof(struct ext2fs_direct_tail) && 195 tp->e2dt_reserved_zero2 == 0 && 196 tp->e2dt_reserved_ft == EXT2_FT_DIR_CSUM) 197 return (1); 198 199 return (0); 200 } 201 202 struct ext2fs_direct_tail * 203 ext2_dirent_get_tail(struct inode *ip, struct ext2fs_direct_2 *ep) 204 { 205 struct ext2fs_direct_2 *dep; 206 void *top; 207 unsigned int rec_len; 208 209 dep = ep; 210 top = EXT2_DIRENT_TAIL(ep, ip->i_e2fs->e2fs_bsize); 211 rec_len = le16toh(dep->e2d_reclen); 212 213 while (rec_len && !(rec_len & 0x3)) { 214 dep = (struct ext2fs_direct_2 *)(((char *)dep) + rec_len); 215 if ((void *)dep >= top) 216 break; 217 rec_len = le16toh(dep->e2d_reclen); 218 } 219 220 if (dep != top) 221 return (NULL); 222 223 if (ext2_is_dirent_tail(ip, dep)) 224 return ((struct ext2fs_direct_tail *)dep); 225 226 return (NULL); 227 } 228 229 static uint32_t 230 ext2_dirent_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int size) 231 { 232 struct m_ext2fs *fs; 233 char *buf; 234 uint32_t inum, gen, crc; 235 236 fs = ip->i_e2fs; 237 238 buf = (char *)ep; 239 240 inum = htole32(ip->i_number); 241 gen = htole32(ip->i_gen); 242 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 243 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 244 crc = calculate_crc32c(crc, (uint8_t *)buf, size); 245 246 return (crc); 247 } 248 249 int 250 ext2_dirent_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 251 { 252 uint32_t calculated; 253 struct ext2fs_direct_tail *tp; 254 255 tp = ext2_dirent_get_tail(ip, ep); 256 if (tp == NULL) 257 return (0); 258 259 calculated = ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep); 260 if (calculated != le32toh(tp->e2dt_checksum)) 261 return (EIO); 262 263 return (0); 264 } 265 266 static struct ext2fs_htree_count * 267 ext2_get_dx_count(struct inode *ip, struct ext2fs_direct_2 *ep, int *offset) 268 { 269 struct ext2fs_direct_2 *dp; 270 struct ext2fs_htree_root_info *root; 271 int count_offset; 272 273 if (le16toh(ep->e2d_reclen) == EXT2_BLOCK_SIZE(ip->i_e2fs)) 274 count_offset = 8; 275 else if (le16toh(ep->e2d_reclen) == 12) { 276 dp = (struct ext2fs_direct_2 *)(((char *)ep) + 12); 277 if (le16toh(dp->e2d_reclen) != EXT2_BLOCK_SIZE(ip->i_e2fs) - 12) 278 return (NULL); 279 280 root = (struct ext2fs_htree_root_info *)(((char *)dp + 12)); 281 if (root->h_reserved1 || 282 root->h_info_len != sizeof(struct ext2fs_htree_root_info)) 283 return (NULL); 284 285 count_offset = 32; 286 } else 287 return (NULL); 288 289 if (offset) 290 *offset = count_offset; 291 292 return ((struct ext2fs_htree_count *)(((char *)ep) + count_offset)); 293 } 294 295 static uint32_t 296 ext2_dx_csum(struct inode *ip, struct ext2fs_direct_2 *ep, int count_offset, 297 int count, struct ext2fs_htree_tail *tp) 298 { 299 struct m_ext2fs *fs; 300 char *buf; 301 int size; 302 uint32_t inum, old_csum, gen, crc; 303 304 fs = ip->i_e2fs; 305 306 buf = (char *)ep; 307 308 size = count_offset + (count * sizeof(struct ext2fs_htree_entry)); 309 old_csum = tp->ht_checksum; 310 tp->ht_checksum = 0; 311 312 inum = htole32(ip->i_number); 313 gen = htole32(ip->i_gen); 314 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 315 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 316 crc = calculate_crc32c(crc, (uint8_t *)buf, size); 317 crc = calculate_crc32c(crc, (uint8_t *)tp, sizeof(struct ext2fs_htree_tail)); 318 tp->ht_checksum = old_csum; 319 320 return htole32(crc); 321 } 322 323 int 324 ext2_dx_csum_verify(struct inode *ip, struct ext2fs_direct_2 *ep) 325 { 326 uint32_t calculated; 327 struct ext2fs_htree_count *cp; 328 struct ext2fs_htree_tail *tp; 329 int count_offset, limit, count; 330 331 cp = ext2_get_dx_count(ip, ep, &count_offset); 332 if (cp == NULL) 333 return (0); 334 335 limit = le16toh(cp->h_entries_max); 336 count = le16toh(cp->h_entries_num); 337 if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 338 ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 339 return (EIO); 340 341 tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 342 calculated = ext2_dx_csum(ip, ep, count_offset, count, tp); 343 344 if (tp->ht_checksum != calculated) 345 return (EIO); 346 347 return (0); 348 } 349 350 int 351 ext2_dir_blk_csum_verify(struct inode *ip, struct buf *bp) 352 { 353 struct m_ext2fs *fs; 354 struct ext2fs_direct_2 *ep; 355 int error = 0; 356 357 fs = ip->i_e2fs; 358 359 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 360 return (error); 361 362 ep = (struct ext2fs_direct_2 *)bp->b_data; 363 364 if (ext2_dirent_get_tail(ip, ep) != NULL) 365 error = ext2_dirent_csum_verify(ip, ep); 366 else if (ext2_get_dx_count(ip, ep, NULL) != NULL) 367 error = ext2_dx_csum_verify(ip, ep); 368 369 if (error) 370 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad directory csum detected"); 371 372 return (error); 373 } 374 375 void 376 ext2_dirent_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 377 { 378 struct m_ext2fs *fs; 379 struct ext2fs_direct_tail *tp; 380 381 fs = ip->i_e2fs; 382 383 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 384 return; 385 386 tp = ext2_dirent_get_tail(ip, ep); 387 if (tp == NULL) 388 return; 389 390 tp->e2dt_checksum = 391 htole32(ext2_dirent_csum(ip, ep, (char *)tp - (char *)ep)); 392 } 393 394 void 395 ext2_dx_csum_set(struct inode *ip, struct ext2fs_direct_2 *ep) 396 { 397 struct m_ext2fs *fs; 398 struct ext2fs_htree_count *cp; 399 struct ext2fs_htree_tail *tp; 400 int count_offset, limit, count; 401 402 fs = ip->i_e2fs; 403 404 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 405 return; 406 407 cp = ext2_get_dx_count(ip, ep, &count_offset); 408 if (cp == NULL) 409 return; 410 411 limit = le16toh(cp->h_entries_max); 412 count = le16toh(cp->h_entries_num); 413 if (count_offset + (limit * sizeof(struct ext2fs_htree_entry)) > 414 ip->i_e2fs->e2fs_bsize - sizeof(struct ext2fs_htree_tail)) 415 return; 416 417 tp = (struct ext2fs_htree_tail *)(((struct ext2fs_htree_entry *)cp) + limit); 418 tp->ht_checksum = ext2_dx_csum(ip, ep, count_offset, count, tp); 419 } 420 421 static uint32_t 422 ext2_extent_blk_csum(struct inode *ip, struct ext4_extent_header *ehp) 423 { 424 struct m_ext2fs *fs; 425 size_t size; 426 uint32_t inum, gen, crc; 427 428 fs = ip->i_e2fs; 429 430 size = EXT4_EXTENT_TAIL_OFFSET(ehp) + 431 offsetof(struct ext4_extent_tail, et_checksum); 432 433 inum = htole32(ip->i_number); 434 gen = htole32(ip->i_gen); 435 crc = calculate_crc32c(fs->e2fs_csum_seed, (uint8_t *)&inum, sizeof(inum)); 436 crc = calculate_crc32c(crc, (uint8_t *)&gen, sizeof(gen)); 437 crc = calculate_crc32c(crc, (uint8_t *)ehp, size); 438 439 return (crc); 440 } 441 442 int 443 ext2_extent_blk_csum_verify(struct inode *ip, void *data) 444 { 445 struct m_ext2fs *fs; 446 struct ext4_extent_header *ehp; 447 struct ext4_extent_tail *etp; 448 uint32_t provided, calculated; 449 450 fs = ip->i_e2fs; 451 452 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 453 return (0); 454 455 ehp = (struct ext4_extent_header *)data; 456 etp = (struct ext4_extent_tail *)(((char *)ehp) + 457 EXT4_EXTENT_TAIL_OFFSET(ehp)); 458 459 provided = le32toh(etp->et_checksum); 460 calculated = ext2_extent_blk_csum(ip, ehp); 461 462 if (provided != calculated) { 463 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad extent csum detected"); 464 return (EIO); 465 } 466 467 return (0); 468 } 469 470 void 471 ext2_extent_blk_csum_set(struct inode *ip, void *data) 472 { 473 struct m_ext2fs *fs; 474 struct ext4_extent_header *ehp; 475 struct ext4_extent_tail *etp; 476 477 fs = ip->i_e2fs; 478 479 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 480 return; 481 482 ehp = (struct ext4_extent_header *)data; 483 etp = (struct ext4_extent_tail *)(((char *)data) + 484 EXT4_EXTENT_TAIL_OFFSET(ehp)); 485 486 etp->et_checksum = htole32(ext2_extent_blk_csum(ip, 487 (struct ext4_extent_header *)data)); 488 } 489 490 int 491 ext2_gd_i_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 492 { 493 uint32_t hi, provided, calculated; 494 495 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 496 return (0); 497 498 provided = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum); 499 calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 500 fs->e2fs_ipg / 8); 501 if (le16toh(fs->e2fs->e3fs_desc_size) >= 502 EXT2_BG_INODE_BITMAP_CSUM_HI_END) { 503 hi = le16toh(fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi); 504 provided |= (hi << 16); 505 } else 506 calculated &= 0xFFFF; 507 508 if (provided != calculated) { 509 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode bitmap csum detected"); 510 return (EIO); 511 } 512 513 return (0); 514 } 515 516 void 517 ext2_gd_i_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 518 { 519 uint32_t csum; 520 521 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 522 return; 523 524 csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, 525 fs->e2fs_ipg / 8); 526 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum = htole16(csum & 0xFFFF); 527 if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_INODE_BITMAP_CSUM_HI_END) 528 fs->e2fs_gd[cg].ext4bgd_i_bmap_csum_hi = htole16(csum >> 16); 529 } 530 531 int 532 ext2_gd_b_bitmap_csum_verify(struct m_ext2fs *fs, int cg, struct buf *bp) 533 { 534 uint32_t hi, provided, calculated, size; 535 536 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 537 return (0); 538 539 size = fs->e2fs_fpg / 8; 540 provided = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum); 541 calculated = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 542 if (le16toh(fs->e2fs->e3fs_desc_size) >= 543 EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) { 544 hi = le16toh(fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi); 545 provided |= (hi << 16); 546 } else 547 calculated &= 0xFFFF; 548 549 if (provided != calculated) { 550 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad block bitmap csum detected"); 551 return (EIO); 552 } 553 554 return (0); 555 } 556 557 void 558 ext2_gd_b_bitmap_csum_set(struct m_ext2fs *fs, int cg, struct buf *bp) 559 { 560 uint32_t csum, size; 561 562 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 563 return; 564 565 size = fs->e2fs_fpg / 8; 566 csum = calculate_crc32c(fs->e2fs_csum_seed, bp->b_data, size); 567 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum = htole16(csum & 0xFFFF); 568 if (le16toh(fs->e2fs->e3fs_desc_size) >= EXT2_BG_BLOCK_BITMAP_CSUM_HI_LOCATION) 569 fs->e2fs_gd[cg].ext4bgd_b_bmap_csum_hi = htole16(csum >> 16); 570 } 571 572 static uint32_t 573 ext2_ei_csum(struct inode *ip, struct ext2fs_dinode *ei) 574 { 575 struct m_ext2fs *fs; 576 uint32_t inode_csum_seed, inum, gen, crc; 577 uint16_t dummy_csum = 0; 578 unsigned int offset, csum_size; 579 580 fs = ip->i_e2fs; 581 offset = offsetof(struct ext2fs_dinode, e2di_chksum_lo); 582 csum_size = sizeof(dummy_csum); 583 inum = htole32(ip->i_number); 584 crc = calculate_crc32c(fs->e2fs_csum_seed, 585 (uint8_t *)&inum, sizeof(inum)); 586 gen = htole32(ip->i_gen); 587 inode_csum_seed = calculate_crc32c(crc, 588 (uint8_t *)&gen, sizeof(gen)); 589 590 crc = calculate_crc32c(inode_csum_seed, (uint8_t *)ei, offset); 591 crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, csum_size); 592 offset += csum_size; 593 crc = calculate_crc32c(crc, (uint8_t *)ei + offset, 594 E2FS_REV0_INODE_SIZE - offset); 595 596 if (EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE) { 597 offset = offsetof(struct ext2fs_dinode, e2di_chksum_hi); 598 crc = calculate_crc32c(crc, (uint8_t *)ei + 599 E2FS_REV0_INODE_SIZE, offset - E2FS_REV0_INODE_SIZE); 600 601 if ((EXT2_INODE_SIZE(ip->i_e2fs) > E2FS_REV0_INODE_SIZE && 602 le16toh(ei->e2di_extra_isize) >= 603 EXT2_INODE_CSUM_HI_EXTRA_END)) { 604 crc = calculate_crc32c(crc, (uint8_t *)&dummy_csum, 605 csum_size); 606 offset += csum_size; 607 } 608 609 crc = calculate_crc32c(crc, (uint8_t *)ei + offset, 610 EXT2_INODE_SIZE(fs) - offset); 611 } 612 613 return (crc); 614 } 615 616 int 617 ext2_ei_csum_verify(struct inode *ip, struct ext2fs_dinode *ei) 618 { 619 struct m_ext2fs *fs; 620 const static struct ext2fs_dinode ei_zero; 621 uint32_t hi, provided, calculated; 622 623 fs = ip->i_e2fs; 624 625 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 626 return (0); 627 628 provided = le16toh(ei->e2di_chksum_lo); 629 calculated = ext2_ei_csum(ip, ei); 630 631 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 632 le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END)) { 633 hi = le16toh(ei->e2di_chksum_hi); 634 provided |= hi << 16; 635 } else 636 calculated &= 0xFFFF; 637 638 if (provided != calculated) { 639 /* 640 * If it is first time used dinode, 641 * it is expected that it will be zeroed 642 * and we will not return checksum error in this case. 643 */ 644 if (!memcmp(ei, &ei_zero, sizeof(struct ext2fs_dinode))) 645 return (0); 646 647 SDT_PROBE2(ext2fs, , trace, csum, 1, "bad inode csum"); 648 649 return (EIO); 650 } 651 652 return (0); 653 } 654 655 void 656 ext2_ei_csum_set(struct inode *ip, struct ext2fs_dinode *ei) 657 { 658 struct m_ext2fs *fs; 659 uint32_t crc; 660 661 fs = ip->i_e2fs; 662 663 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) 664 return; 665 666 crc = ext2_ei_csum(ip, ei); 667 668 ei->e2di_chksum_lo = htole16(crc & 0xFFFF); 669 if ((EXT2_INODE_SIZE(fs) > E2FS_REV0_INODE_SIZE && 670 le16toh(ei->e2di_extra_isize) >= EXT2_INODE_CSUM_HI_EXTRA_END)) 671 ei->e2di_chksum_hi = htole16(crc >> 16); 672 } 673 674 static uint16_t 675 ext2_gd_csum(struct m_ext2fs *fs, uint32_t block_group, struct ext2_gd *gd) 676 { 677 size_t offset; 678 uint32_t csum32; 679 uint16_t crc, dummy_csum; 680 681 offset = offsetof(struct ext2_gd, ext4bgd_csum); 682 683 block_group = htole32(block_group); 684 685 if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_METADATA_CKSUM)) { 686 csum32 = calculate_crc32c(fs->e2fs_csum_seed, 687 (uint8_t *)&block_group, sizeof(block_group)); 688 csum32 = calculate_crc32c(csum32, (uint8_t *)gd, offset); 689 dummy_csum = 0; 690 csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum, 691 sizeof(dummy_csum)); 692 offset += sizeof(dummy_csum); 693 if (offset < le16toh(fs->e2fs->e3fs_desc_size)) 694 csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset, 695 le16toh(fs->e2fs->e3fs_desc_size) - offset); 696 697 crc = csum32 & 0xFFFF; 698 return (htole16(crc)); 699 } else if (EXT2_HAS_RO_COMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM)) { 700 crc = crc16(~0, fs->e2fs->e2fs_uuid, 701 sizeof(fs->e2fs->e2fs_uuid)); 702 crc = crc16(crc, (uint8_t *)&block_group, 703 sizeof(block_group)); 704 crc = crc16(crc, (uint8_t *)gd, offset); 705 offset += sizeof(gd->ext4bgd_csum); /* skip checksum */ 706 if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) && 707 offset < le16toh(fs->e2fs->e3fs_desc_size)) 708 crc = crc16(crc, (uint8_t *)gd + offset, 709 le16toh(fs->e2fs->e3fs_desc_size) - offset); 710 return (htole16(crc)); 711 } 712 713 return (0); 714 } 715 716 int 717 ext2_gd_csum_verify(struct m_ext2fs *fs, struct cdev *dev) 718 { 719 unsigned int i; 720 int error = 0; 721 722 for (i = 0; i < fs->e2fs_gcount; i++) { 723 if (fs->e2fs_gd[i].ext4bgd_csum != 724 ext2_gd_csum(fs, i, &fs->e2fs_gd[i])) { 725 printf( 726 "WARNING: mount of %s denied due bad gd=%d csum=0x%x, expected=0x%x - run fsck\n", 727 devtoname(dev), i, fs->e2fs_gd[i].ext4bgd_csum, 728 ext2_gd_csum(fs, i, &fs->e2fs_gd[i])); 729 error = EIO; 730 break; 731 } 732 } 733 734 return (error); 735 } 736 737 void 738 ext2_gd_csum_set(struct m_ext2fs *fs) 739 { 740 unsigned int i; 741 742 for (i = 0; i < fs->e2fs_gcount; i++) 743 fs->e2fs_gd[i].ext4bgd_csum = ext2_gd_csum(fs, i, &fs->e2fs_gd[i]); 744 } 745