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