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