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