1 /* 2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org> 3 * Copyright (c) 2019 The DragonFly Project 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The DragonFly Project 7 * by Matthew Dillon <dillon@dragonflybsd.org> 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 3. Neither the name of The DragonFly Project nor the names of its 20 * contributors may be used to endorse or promote products derived 21 * from this software without specific, prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 // # gcc -Wall -g -I../../sys/vfs/hammer2 -I../hammer2 38 // ../../sys/libkern/icrc32.c ../../sys/vfs/hammer2/xxhash/xxhash.c 39 // ../hammer2/subs.c ./reconstruct.c -o reconstruct 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <unistd.h> 44 #include <fcntl.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <stdbool.h> 48 #include <string.h> 49 #include <assert.h> 50 51 #include <openssl/sha.h> 52 53 #include <vfs/hammer2/hammer2_disk.h> 54 #include <vfs/hammer2/hammer2_xxhash.h> 55 56 #include "hammer2_subs.h" 57 58 static int modify_volume_header(int, hammer2_volume_data_t *, 59 const hammer2_blockref_t *); 60 static int modify_blockref(int, const hammer2_volume_data_t *, int, 61 hammer2_blockref_t *, hammer2_blockref_t *, int); 62 static int modify_check(int, int, hammer2_blockref_t *, 63 const hammer2_blockref_t *, hammer2_media_data_t *, size_t, int); 64 65 static bool ForceOpt = false; 66 67 static int 68 reconstruct_volume_header(int fd) 69 { 70 bool failed = false; 71 int i; 72 73 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 74 hammer2_volume_data_t voldata; 75 hammer2_blockref_t broot; 76 ssize_t ret; 77 78 memset(&broot, 0, sizeof(broot)); 79 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 80 if (lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 81 SEEK_SET) == -1) { 82 perror("lseek"); 83 return -1; 84 } 85 86 ret = read(fd, &voldata, HAMMER2_PBUFSIZE); 87 if (ret == HAMMER2_PBUFSIZE) { 88 fprintf(stdout, "zone.%d %016jx\n", 89 i, (uintmax_t)broot.data_off); 90 if (modify_volume_header(fd, &voldata, &broot) == -1) 91 failed = true; 92 } else if (ret == -1) { 93 perror("read"); 94 return -1; 95 } else { 96 fprintf(stderr, "Failed to read volume header\n"); 97 return -1; 98 } 99 } 100 101 return failed ? -1 : 0; 102 } 103 104 static int 105 reconstruct_blockref(int fd, uint8_t type) 106 { 107 bool failed = false; 108 int i; 109 110 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 111 hammer2_volume_data_t voldata; 112 hammer2_blockref_t broot; 113 ssize_t ret; 114 115 memset(&broot, 0, sizeof(broot)); 116 broot.type = type; 117 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 118 if (lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 119 SEEK_SET) == -1) { 120 perror("lseek"); 121 return -1; 122 } 123 124 ret = read(fd, &voldata, HAMMER2_PBUFSIZE); 125 if (ret == HAMMER2_PBUFSIZE) { 126 fprintf(stdout, "zone.%d %016jx\n", 127 i, (uintmax_t)broot.data_off); 128 if (modify_blockref(fd, &voldata, -1, &broot, NULL, -1) 129 == -1) 130 failed = true; 131 } else if (ret == -1) { 132 perror("read"); 133 return -1; 134 } else { 135 fprintf(stderr, "Failed to read volume header\n"); 136 return -1; 137 } 138 } 139 140 return failed ? -1 : 0; 141 } 142 143 static int 144 modify_volume_header(int fd, hammer2_volume_data_t *voldata, 145 const hammer2_blockref_t *bref) 146 { 147 hammer2_crc32_t crc0, crc1; 148 const char *s = NULL; 149 bool found = false; 150 151 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) && 152 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) { 153 fprintf(stderr, "Bad magic %jX\n", voldata->magic); 154 return -1; 155 } 156 157 if (voldata->magic == HAMMER2_VOLUME_ID_ABO) 158 fprintf(stderr, "Reverse endian\n"); 159 160 /* Need to test HAMMER2_VOL_ICRC_SECT1 first. */ 161 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]; 162 crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRC1_OFF, 163 HAMMER2_VOLUME_ICRC1_SIZE); 164 if (crc0 != crc1) { 165 if (ForceOpt) 166 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1] = crc1; 167 found = true; 168 s = "HAMMER2_VOL_ICRC_SECT1"; 169 printf("%s%016jx %s\n", ForceOpt ? "Modified " : "", 170 (uintmax_t)bref->data_off, s); 171 } 172 173 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]; 174 crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRC0_OFF, 175 HAMMER2_VOLUME_ICRC0_SIZE); 176 if (crc0 != crc1) { 177 if (ForceOpt) 178 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0] = crc1; 179 found = true; 180 s = "HAMMER2_VOL_ICRC_SECT0"; 181 printf("%s%016jx %s\n", ForceOpt ? "Modified " : "", 182 (uintmax_t)bref->data_off, s); 183 } 184 185 crc0 = voldata->icrc_volheader; 186 crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRCVH_OFF, 187 HAMMER2_VOLUME_ICRCVH_SIZE); 188 if (crc0 != crc1) { 189 if (ForceOpt) 190 voldata->icrc_volheader = crc1; 191 found = true; 192 s = "volume header CRC"; 193 printf("%s%016jx %s\n", ForceOpt ? "Modified " : "", 194 (uintmax_t)bref->data_off, s); 195 } 196 197 if (found && ForceOpt) { 198 ssize_t ret; 199 if (lseek(fd, bref->data_off & ~HAMMER2_OFF_MASK_RADIX, 200 SEEK_SET) == -1) { 201 perror("lseek"); 202 return -1; 203 } 204 ret = write(fd, voldata, HAMMER2_PBUFSIZE); 205 if (ret == -1) { 206 perror("write"); 207 return -1; 208 } else if (ret != (ssize_t)HAMMER2_PBUFSIZE) { 209 fprintf(stderr, "Failed to write volume header\n"); 210 return -1; 211 } 212 if (fsync(fd) == -1) { 213 perror("fsync"); 214 return -1; 215 } 216 } 217 218 return 0; 219 } 220 221 static int 222 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media, 223 size_t *media_bytes) 224 { 225 hammer2_off_t io_off, io_base; 226 size_t bytes, io_bytes, boff; 227 ssize_t ret; 228 229 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 230 if (bytes) 231 bytes = (size_t)1 << bytes; 232 if (media_bytes) 233 *media_bytes = bytes; 234 235 if (!bytes) 236 return 0; 237 238 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 239 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 240 boff = io_off - io_base; 241 242 io_bytes = HAMMER2_MINIOSIZE; 243 while (io_bytes + boff < bytes) 244 io_bytes <<= 1; 245 246 if (io_bytes > sizeof(*media)) { 247 fprintf(stderr, "Bad I/O bytes\n"); 248 return -1; 249 } 250 if (lseek(fd, io_base, SEEK_SET) == -1) { 251 perror("lseek"); 252 return -1; 253 } 254 ret = read(fd, media, io_bytes); 255 if (ret == -1) { 256 perror("read"); 257 return -1; 258 } else if (ret != (ssize_t)io_bytes) { 259 fprintf(stderr, "Failed to read media\n"); 260 return -1; 261 } 262 if (boff) 263 memmove(media, (char *)media + boff, bytes); 264 265 return 0; 266 } 267 268 static int 269 write_media(int fd, const hammer2_blockref_t *bref, 270 const hammer2_media_data_t *media, size_t media_bytes) 271 { 272 hammer2_off_t io_off, io_base; 273 char buf[HAMMER2_PBUFSIZE]; 274 size_t bytes, io_bytes, boff; 275 ssize_t ret; 276 277 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 278 if (bytes) 279 bytes = (size_t)1 << bytes; 280 assert(bytes != 0); 281 assert(bytes == media_bytes); 282 283 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 284 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 285 boff = io_off - io_base; 286 287 io_bytes = HAMMER2_MINIOSIZE; 288 while (io_bytes + boff < bytes) 289 io_bytes <<= 1; 290 291 if (io_bytes > sizeof(buf)) { 292 fprintf(stderr, "Bad I/O bytes\n"); 293 return -1; 294 } 295 if (lseek(fd, io_base, SEEK_SET) == -1) { 296 perror("lseek"); 297 return -1; 298 } 299 if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) { 300 perror("read"); 301 return -1; 302 } 303 304 memcpy(buf + boff, media, media_bytes); 305 if (lseek(fd, io_base, SEEK_SET) == -1) { 306 perror("lseek"); 307 return -1; 308 } 309 ret = write(fd, buf, io_bytes); 310 if (ret == -1) { 311 perror("write"); 312 return -1; 313 } else if (ret != (ssize_t)io_bytes) { 314 fprintf(stderr, "Failed to write media\n"); 315 return -1; 316 } 317 if (fsync(fd) == -1) { 318 perror("fsync"); 319 return -1; 320 } 321 322 return 0; 323 } 324 325 static int 326 modify_blockref(int fd, const hammer2_volume_data_t *voldata, int bi, 327 hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref, int depth) 328 { 329 hammer2_media_data_t media; 330 hammer2_blockref_t *bscan; 331 int i, bcount; 332 size_t bytes; 333 334 if (read_media(fd, bref, &media, &bytes) == -1) 335 return -1; 336 337 if (!bytes) 338 return 0; 339 340 switch (bref->type) { 341 case HAMMER2_BREF_TYPE_INODE: 342 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { 343 bscan = &media.ipdata.u.blockset.blockref[0]; 344 bcount = HAMMER2_SET_COUNT; 345 } else { 346 bscan = NULL; 347 bcount = 0; 348 } 349 break; 350 case HAMMER2_BREF_TYPE_INDIRECT: 351 bscan = &media.npdata[0]; 352 bcount = bytes / sizeof(hammer2_blockref_t); 353 break; 354 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 355 bscan = &media.npdata[0]; 356 bcount = bytes / sizeof(hammer2_blockref_t); 357 break; 358 case HAMMER2_BREF_TYPE_VOLUME: 359 bscan = &media.voldata.sroot_blockset.blockref[0]; 360 bcount = HAMMER2_SET_COUNT; 361 break; 362 case HAMMER2_BREF_TYPE_FREEMAP: 363 bscan = &media.voldata.freemap_blockset.blockref[0]; 364 bcount = HAMMER2_SET_COUNT; 365 break; 366 default: 367 bscan = NULL; 368 bcount = 0; 369 break; 370 } 371 372 for (i = 0; i < bcount; ++i) 373 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) 374 if (modify_blockref(fd, voldata, i, &bscan[i], bref, 375 depth + 1) == -1) 376 return -1; 377 378 if (ForceOpt) 379 if (read_media(fd, bref, &media, &bytes) == -1) 380 return -1; 381 if (modify_check(fd, bi, prev_bref, bref, &media, bytes, depth) == -1) 382 return -1; 383 384 return 0; 385 } 386 387 static int 388 modify_check(int fd, int bi, hammer2_blockref_t *prev_bref, 389 const hammer2_blockref_t *bref, hammer2_media_data_t *media, 390 size_t media_bytes, int depth) 391 { 392 hammer2_media_data_t bscan_media; 393 hammer2_blockref_t *bscan; 394 bool found = false; 395 size_t bytes; 396 uint32_t cv; 397 uint64_t cv64; 398 399 //SHA256_CTX hash_ctx; 400 union { 401 uint8_t digest[SHA256_DIGEST_LENGTH]; 402 uint64_t digest64[SHA256_DIGEST_LENGTH/8]; 403 } u; 404 405 406 if (!prev_bref) 407 return 0; 408 if (read_media(fd, prev_bref, &bscan_media, &bytes) == -1) 409 return -1; 410 assert(bytes); 411 412 switch (prev_bref->type) { 413 case HAMMER2_BREF_TYPE_INODE: 414 if (!(bscan_media.ipdata.meta.op_flags & 415 HAMMER2_OPFLAG_DIRECTDATA)) 416 bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; 417 else 418 bscan = NULL; 419 break; 420 case HAMMER2_BREF_TYPE_INDIRECT: 421 bscan = &bscan_media.npdata[bi]; 422 break; 423 case HAMMER2_BREF_TYPE_VOLUME: 424 bscan = &bscan_media.voldata.sroot_blockset.blockref[bi]; 425 break; 426 case HAMMER2_BREF_TYPE_FREEMAP: 427 bscan = &bscan_media.voldata.freemap_blockset.blockref[bi]; 428 break; 429 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 430 bscan = &bscan_media.npdata[bi]; 431 break; 432 default: 433 assert(0); 434 break; 435 } 436 437 if (memcmp(bref, bscan, sizeof(*bref))) { 438 fprintf(stderr, "Blockref contents mismatch\n"); 439 return -1; 440 } 441 442 switch (HAMMER2_DEC_CHECK(bscan->methods)) { 443 case HAMMER2_CHECK_ISCSI32: 444 cv = hammer2_icrc32(media, media_bytes); 445 if (bscan->check.iscsi32.value != cv) { 446 if (ForceOpt) 447 bscan->check.iscsi32.value = cv; 448 found = true; 449 } 450 break; 451 case HAMMER2_CHECK_XXHASH64: 452 cv64 = XXH64(media, media_bytes, XXH_HAMMER2_SEED); 453 if (bscan->check.xxhash64.value != cv64) { 454 if (ForceOpt) 455 bscan->check.xxhash64.value = cv64; 456 found = true; 457 } 458 break; 459 case HAMMER2_CHECK_SHA192: 460 #if 0 461 SHA256_Init(&hash_ctx); 462 SHA256_Update(&hash_ctx, &media, bytes); 463 SHA256_Final(u.digest, &hash_ctx); 464 #endif 465 u.digest64[2] ^= u.digest64[3]; 466 if (memcmp(u.digest, bscan->check.sha192.data, 467 sizeof(bscan->check.sha192.data))) { 468 if (ForceOpt) 469 memcpy(&bscan->check.sha192.data, u.digest, 470 sizeof(bscan->check.sha192.data)); 471 found = true; 472 } 473 fprintf(stderr, "HAMMER2_CHECK_SHA192 unsupported\n"); 474 assert(0); 475 break; 476 case HAMMER2_CHECK_FREEMAP: 477 cv = hammer2_icrc32(media, media_bytes); 478 if (bscan->check.freemap.icrc32 != cv) { 479 if (ForceOpt) 480 bscan->check.freemap.icrc32 = cv; 481 found = true; 482 } 483 break; 484 } 485 486 if (found) { 487 if (ForceOpt) { 488 if (write_media(fd, prev_bref, &bscan_media, bytes) 489 == -1) 490 return -1; 491 } 492 /* If !ForceOpt, only first bad blockref is printed. */ 493 printf("%s%2d %-8s blockref[%-3d] %016jx %02x %s\n", 494 ForceOpt ? "Modified " : "", 495 depth, hammer2_breftype_to_str(prev_bref->type), bi, 496 (uintmax_t)bscan->data_off, bscan->methods, 497 hammer2_breftype_to_str(bscan->type)); 498 } 499 500 return 0; 501 } 502 503 int 504 main(int argc, char **argv) 505 { 506 struct stat st; 507 int ch, fd; 508 const char *binpath = argv[0]; 509 const char *devpath; 510 511 while ((ch = getopt(argc, argv, "f")) != -1) { 512 switch(ch) { 513 case 'f': 514 ForceOpt = true; 515 break; 516 default: 517 break; 518 } 519 } 520 argc -= optind; 521 argv += optind; 522 523 if (argc < 1) { 524 fprintf(stderr, "%s [-f] special\n", binpath); 525 exit(1); 526 } 527 devpath = argv[0]; 528 529 fd = open(devpath, O_RDWR); 530 if (fd == -1) { 531 perror("open"); 532 exit(1); 533 } 534 535 if (fstat(fd, &st) == -1) { 536 perror("fstat"); 537 exit(1); 538 } 539 if (!S_ISCHR(st.st_mode)) { 540 fprintf(stderr, "%s is not a block device\n", devpath); 541 exit(1); 542 } 543 544 printf("freemap\n"); 545 if (reconstruct_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) 546 exit(1); 547 printf("volume\n"); 548 if (reconstruct_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) 549 exit(1); 550 551 printf("volume header\n"); 552 if (reconstruct_volume_header(fd) == -1) 553 exit(1); 554 555 close(fd); 556 557 return 0; 558 } 559