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 -I../../crypto/libressl/include ../../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
reconstruct_volume_header(void)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_VOLUME_BYTES);
88 if (ret == HAMMER2_VOLUME_BYTES) {
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
reconstruct_blockref(uint8_t type)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_VOLUME_BYTES);
129 if (ret == HAMMER2_VOLUME_BYTES) {
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
modify_volume_header(hammer2_volume_data_t * voldata,const hammer2_blockref_t * bref)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
read_media(const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t * media_bytes)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
write_media(const hammer2_blockref_t * bref,const hammer2_media_data_t * media,size_t media_bytes)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
modify_blockref(const hammer2_volume_data_t * voldata,int bi,hammer2_blockref_t * bref,hammer2_blockref_t * prev_bref,int depth)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
modify_check(int bi,hammer2_blockref_t * prev_bref,const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t media_bytes,int depth)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
main(int argc,char ** argv)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