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