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