xref: /dragonfly/sbin/fsck_hammer2/reconstruct.c (revision 556932ec)
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
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
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
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