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