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 -I../hammer2 ../../sys/vfs/hammer2/xxhash/xxhash.c 38 // ../../sys/libkern/icrc32.c ../hammer2/subs.c ./reconstruct.c -o reconstruct 39 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <fcntl.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <stdbool.h> 47 #include <string.h> 48 #include <assert.h> 49 50 #include <openssl/sha.h> 51 52 #include <vfs/hammer2/hammer2_disk.h> 53 #include <vfs/hammer2/hammer2_xxhash.h> 54 55 #include "hammer2_subs.h" 56 57 static int modify_volume_header(int, hammer2_volume_data_t *, 58 const hammer2_blockref_t *); 59 static int modify_blockref(int, const hammer2_volume_data_t *, int, 60 hammer2_blockref_t *, hammer2_blockref_t *, int); 61 static int modify_check(int, int, hammer2_blockref_t *, 62 const hammer2_blockref_t *, hammer2_media_data_t *, size_t, int); 63 64 static bool ForceOpt = false; 65 66 static int 67 reconstruct_volume_header(int fd) 68 { 69 bool failed = false; 70 int i; 71 72 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 73 hammer2_volume_data_t voldata; 74 hammer2_blockref_t broot; 75 ssize_t ret; 76 77 memset(&broot, 0, sizeof(broot)); 78 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 79 if (lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 80 SEEK_SET) == -1) { 81 perror("lseek"); 82 return -1; 83 } 84 85 ret = read(fd, &voldata, HAMMER2_PBUFSIZE); 86 if (ret == HAMMER2_PBUFSIZE) { 87 fprintf(stdout, "zone.%d %016jx\n", 88 i, (uintmax_t)broot.data_off); 89 if (modify_volume_header(fd, &voldata, &broot) == -1) 90 failed = true; 91 } else if (ret == -1) { 92 perror("read"); 93 return -1; 94 } else { 95 fprintf(stderr, "Failed to read volume header\n"); 96 return -1; 97 } 98 } 99 100 return failed ? -1 : 0; 101 } 102 103 static int 104 reconstruct_blockref(int fd, uint8_t type) 105 { 106 bool failed = false; 107 int i; 108 109 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 110 hammer2_volume_data_t voldata; 111 hammer2_blockref_t broot; 112 ssize_t ret; 113 114 memset(&broot, 0, sizeof(broot)); 115 broot.type = type; 116 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX; 117 if (lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 118 SEEK_SET) == -1) { 119 perror("lseek"); 120 return -1; 121 } 122 123 ret = read(fd, &voldata, HAMMER2_PBUFSIZE); 124 if (ret == HAMMER2_PBUFSIZE) { 125 fprintf(stdout, "zone.%d %016jx\n", 126 i, (uintmax_t)broot.data_off); 127 if (modify_blockref(fd, &voldata, -1, &broot, NULL, -1) 128 == -1) 129 failed = true; 130 } else if (ret == -1) { 131 perror("read"); 132 return -1; 133 } else { 134 fprintf(stderr, "Failed to read volume header\n"); 135 return -1; 136 } 137 } 138 139 return failed ? -1 : 0; 140 } 141 142 static int 143 modify_volume_header(int fd, hammer2_volume_data_t *voldata, 144 const hammer2_blockref_t *bref) 145 { 146 hammer2_crc32_t crc0, crc1; 147 const char *s = NULL; 148 bool found = false; 149 150 if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) && 151 (voldata->magic != HAMMER2_VOLUME_ID_ABO)) { 152 fprintf(stderr, "Bad magic %jX\n", voldata->magic); 153 return -1; 154 } 155 156 if (voldata->magic == HAMMER2_VOLUME_ID_ABO) 157 fprintf(stderr, "Reverse endian\n"); 158 159 /* Need to test HAMMER2_VOL_ICRC_SECT1 first. */ 160 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1]; 161 crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRC1_OFF, 162 HAMMER2_VOLUME_ICRC1_SIZE); 163 if (crc0 != crc1) { 164 if (ForceOpt) 165 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1] = crc1; 166 found = true; 167 s = "HAMMER2_VOL_ICRC_SECT1"; 168 printf("%s%016jx %s\n", ForceOpt ? "Modified " : "", 169 (uintmax_t)bref->data_off, s); 170 } 171 172 crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0]; 173 crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRC0_OFF, 174 HAMMER2_VOLUME_ICRC0_SIZE); 175 if (crc0 != crc1) { 176 if (ForceOpt) 177 voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0] = crc1; 178 found = true; 179 s = "HAMMER2_VOL_ICRC_SECT0"; 180 printf("%s%016jx %s\n", ForceOpt ? "Modified " : "", 181 (uintmax_t)bref->data_off, s); 182 } 183 184 crc0 = voldata->icrc_volheader; 185 crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRCVH_OFF, 186 HAMMER2_VOLUME_ICRCVH_SIZE); 187 if (crc0 != crc1) { 188 if (ForceOpt) 189 voldata->icrc_volheader = crc1; 190 found = true; 191 s = "volume header CRC"; 192 printf("%s%016jx %s\n", ForceOpt ? "Modified " : "", 193 (uintmax_t)bref->data_off, s); 194 } 195 196 if (found && ForceOpt) { 197 ssize_t ret; 198 if (lseek(fd, bref->data_off & ~HAMMER2_OFF_MASK_RADIX, 199 SEEK_SET) == -1) { 200 perror("lseek"); 201 return -1; 202 } 203 ret = write(fd, voldata, HAMMER2_PBUFSIZE); 204 if (ret == -1) { 205 perror("write"); 206 return -1; 207 } else if (ret != (ssize_t)HAMMER2_PBUFSIZE) { 208 fprintf(stderr, "Failed to write volume header\n"); 209 return -1; 210 } 211 if (fsync(fd) == -1) { 212 perror("fsync"); 213 return -1; 214 } 215 } 216 217 return 0; 218 } 219 220 static int 221 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media, 222 size_t *media_bytes) 223 { 224 hammer2_off_t io_off, io_base; 225 size_t bytes, io_bytes, boff; 226 ssize_t ret; 227 228 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 229 if (bytes) 230 bytes = (size_t)1 << bytes; 231 if (media_bytes) 232 *media_bytes = bytes; 233 234 if (!bytes) 235 return 0; 236 237 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 238 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 239 boff = io_off - io_base; 240 241 io_bytes = HAMMER2_MINIOSIZE; 242 while (io_bytes + boff < bytes) 243 io_bytes <<= 1; 244 245 if (io_bytes > sizeof(*media)) { 246 fprintf(stderr, "Bad I/O bytes\n"); 247 return -1; 248 } 249 if (lseek(fd, io_base, SEEK_SET) == -1) { 250 perror("lseek"); 251 return -1; 252 } 253 ret = read(fd, media, io_bytes); 254 if (ret == -1) { 255 perror("read"); 256 return -1; 257 } else if (ret != (ssize_t)io_bytes) { 258 fprintf(stderr, "Failed to read media\n"); 259 return -1; 260 } 261 if (boff) 262 memmove(media, (char *)media + boff, bytes); 263 264 return 0; 265 } 266 267 static int 268 write_media(int fd, const hammer2_blockref_t *bref, 269 const hammer2_media_data_t *media, size_t media_bytes) 270 { 271 hammer2_off_t io_off, io_base; 272 char buf[HAMMER2_PBUFSIZE]; 273 size_t bytes, io_bytes, boff; 274 ssize_t ret; 275 276 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 277 if (bytes) 278 bytes = (size_t)1 << bytes; 279 assert(bytes != 0); 280 assert(bytes == media_bytes); 281 282 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 283 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 284 boff = io_off - io_base; 285 286 io_bytes = HAMMER2_MINIOSIZE; 287 while (io_bytes + boff < bytes) 288 io_bytes <<= 1; 289 290 if (io_bytes > sizeof(buf)) { 291 fprintf(stderr, "Bad I/O bytes\n"); 292 return -1; 293 } 294 if (lseek(fd, io_base, SEEK_SET) == -1) { 295 perror("lseek"); 296 return -1; 297 } 298 if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) { 299 perror("read"); 300 return -1; 301 } 302 303 memcpy(buf + boff, media, media_bytes); 304 if (lseek(fd, io_base, SEEK_SET) == -1) { 305 perror("lseek"); 306 return -1; 307 } 308 ret = write(fd, buf, io_bytes); 309 if (ret == -1) { 310 perror("write"); 311 return -1; 312 } else if (ret != (ssize_t)io_bytes) { 313 fprintf(stderr, "Failed to write media\n"); 314 return -1; 315 } 316 if (fsync(fd) == -1) { 317 perror("fsync"); 318 return -1; 319 } 320 321 return 0; 322 } 323 324 static int 325 modify_blockref(int fd, const hammer2_volume_data_t *voldata, int bi, 326 hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref, int depth) 327 { 328 hammer2_media_data_t media; 329 hammer2_blockref_t *bscan; 330 int i, bcount; 331 size_t bytes; 332 333 if (read_media(fd, bref, &media, &bytes) == -1) 334 return -1; 335 336 if (!bytes) 337 return 0; 338 339 switch (bref->type) { 340 case HAMMER2_BREF_TYPE_INODE: 341 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { 342 bscan = &media.ipdata.u.blockset.blockref[0]; 343 bcount = HAMMER2_SET_COUNT; 344 } else { 345 bscan = NULL; 346 bcount = 0; 347 } 348 break; 349 case HAMMER2_BREF_TYPE_INDIRECT: 350 bscan = &media.npdata[0]; 351 bcount = bytes / sizeof(hammer2_blockref_t); 352 break; 353 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 354 bscan = &media.npdata[0]; 355 bcount = bytes / sizeof(hammer2_blockref_t); 356 break; 357 case HAMMER2_BREF_TYPE_VOLUME: 358 bscan = &media.voldata.sroot_blockset.blockref[0]; 359 bcount = HAMMER2_SET_COUNT; 360 break; 361 case HAMMER2_BREF_TYPE_FREEMAP: 362 bscan = &media.voldata.freemap_blockset.blockref[0]; 363 bcount = HAMMER2_SET_COUNT; 364 break; 365 default: 366 bscan = NULL; 367 bcount = 0; 368 break; 369 } 370 371 for (i = 0; i < bcount; ++i) 372 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) 373 if (modify_blockref(fd, voldata, i, &bscan[i], bref, 374 depth + 1) == -1) 375 return -1; 376 377 if (ForceOpt) 378 if (read_media(fd, bref, &media, &bytes) == -1) 379 return -1; 380 if (modify_check(fd, bi, prev_bref, bref, &media, bytes, depth) == -1) 381 return -1; 382 383 return 0; 384 } 385 386 static int 387 modify_check(int fd, int bi, hammer2_blockref_t *prev_bref, 388 const hammer2_blockref_t *bref, hammer2_media_data_t *media, 389 size_t media_bytes, int depth) 390 { 391 hammer2_media_data_t bscan_media; 392 hammer2_blockref_t *bscan; 393 bool found = false; 394 size_t bytes; 395 uint32_t cv; 396 uint64_t cv64; 397 398 //SHA256_CTX hash_ctx; 399 union { 400 uint8_t digest[SHA256_DIGEST_LENGTH]; 401 uint64_t digest64[SHA256_DIGEST_LENGTH/8]; 402 } u; 403 404 405 if (!prev_bref) 406 return 0; 407 if (read_media(fd, prev_bref, &bscan_media, &bytes) == -1) 408 return -1; 409 assert(bytes); 410 411 switch (prev_bref->type) { 412 case HAMMER2_BREF_TYPE_INODE: 413 if (!(bscan_media.ipdata.meta.op_flags & 414 HAMMER2_OPFLAG_DIRECTDATA)) 415 bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; 416 else 417 bscan = NULL; 418 break; 419 case HAMMER2_BREF_TYPE_INDIRECT: 420 bscan = &bscan_media.npdata[bi]; 421 break; 422 case HAMMER2_BREF_TYPE_VOLUME: 423 bscan = &bscan_media.voldata.sroot_blockset.blockref[bi]; 424 break; 425 case HAMMER2_BREF_TYPE_FREEMAP: 426 bscan = &bscan_media.voldata.freemap_blockset.blockref[bi]; 427 break; 428 case HAMMER2_BREF_TYPE_FREEMAP_NODE: 429 bscan = &bscan_media.npdata[bi]; 430 break; 431 default: 432 assert(0); 433 break; 434 } 435 436 if (memcmp(bref, bscan, sizeof(*bref))) { 437 fprintf(stderr, "Blockref contents mismatch\n"); 438 return -1; 439 } 440 441 switch (HAMMER2_DEC_CHECK(bscan->methods)) { 442 case HAMMER2_CHECK_ISCSI32: 443 cv = hammer2_icrc32(media, media_bytes); 444 if (bscan->check.iscsi32.value != cv) { 445 if (ForceOpt) 446 bscan->check.iscsi32.value = cv; 447 found = true; 448 } 449 break; 450 case HAMMER2_CHECK_XXHASH64: 451 cv64 = XXH64(media, media_bytes, XXH_HAMMER2_SEED); 452 if (bscan->check.xxhash64.value != cv64) { 453 if (ForceOpt) 454 bscan->check.xxhash64.value = cv64; 455 found = true; 456 } 457 break; 458 case HAMMER2_CHECK_SHA192: 459 #if 0 460 SHA256_Init(&hash_ctx); 461 SHA256_Update(&hash_ctx, &media, bytes); 462 SHA256_Final(u.digest, &hash_ctx); 463 #endif 464 u.digest64[2] ^= u.digest64[3]; 465 if (memcmp(u.digest, bscan->check.sha192.data, 466 sizeof(bscan->check.sha192.data))) { 467 if (ForceOpt) 468 memcpy(&bscan->check.sha192.data, u.digest, 469 sizeof(bscan->check.sha192.data)); 470 found = true; 471 } 472 fprintf(stderr, "HAMMER2_CHECK_SHA192 unsupported\n"); 473 assert(0); 474 break; 475 case HAMMER2_CHECK_FREEMAP: 476 cv = hammer2_icrc32(media, media_bytes); 477 if (bscan->check.freemap.icrc32 != cv) { 478 if (ForceOpt) 479 bscan->check.freemap.icrc32 = cv; 480 found = true; 481 } 482 break; 483 } 484 485 if (found) { 486 if (ForceOpt) { 487 if (write_media(fd, prev_bref, &bscan_media, bytes) 488 == -1) 489 return -1; 490 } 491 /* If !ForceOpt, only first bad blockref is printed. */ 492 printf("%s%2d %-8s blockref[%-3d] %016jx %02x %s\n", 493 ForceOpt ? "Modified " : "", 494 depth, hammer2_breftype_to_str(prev_bref->type), bi, 495 (uintmax_t)bscan->data_off, bscan->methods, 496 hammer2_breftype_to_str(bscan->type)); 497 } 498 499 return 0; 500 } 501 502 int 503 main(int argc, char **argv) 504 { 505 struct stat st; 506 int ch, fd; 507 const char *binpath = argv[0]; 508 const char *devpath; 509 510 while ((ch = getopt(argc, argv, "f")) != -1) { 511 switch(ch) { 512 case 'f': 513 ForceOpt = true; 514 break; 515 default: 516 break; 517 } 518 } 519 argc -= optind; 520 argv += optind; 521 522 if (argc < 1) { 523 fprintf(stderr, "%s [-f] special\n", binpath); 524 exit(1); 525 } 526 devpath = argv[0]; 527 528 fd = open(devpath, O_RDWR); 529 if (fd == -1) { 530 perror("open"); 531 exit(1); 532 } 533 534 if (fstat(fd, &st) == -1) { 535 perror("fstat"); 536 exit(1); 537 } 538 if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) { 539 fprintf(stderr, "Unsupported file type\n"); 540 exit(1); 541 } 542 543 printf("freemap\n"); 544 if (reconstruct_blockref(fd, HAMMER2_BREF_TYPE_FREEMAP) == -1) 545 exit(1); 546 printf("volume\n"); 547 if (reconstruct_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) 548 exit(1); 549 550 printf("volume header\n"); 551 if (reconstruct_volume_header(fd) == -1) 552 exit(1); 553 554 close(fd); 555 556 return 0; 557 } 558