xref: /dragonfly/sbin/fsck_hammer2/reconstruct.c (revision 73da1719)
12b7e4566STomohiro Kusumi /*
22b7e4566STomohiro Kusumi  * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
32b7e4566STomohiro Kusumi  * Copyright (c) 2019 The DragonFly Project
42b7e4566STomohiro Kusumi  * All rights reserved.
52b7e4566STomohiro Kusumi  *
62b7e4566STomohiro Kusumi  * This code is derived from software contributed to The DragonFly Project
72b7e4566STomohiro Kusumi  * by Matthew Dillon <dillon@dragonflybsd.org>
82b7e4566STomohiro Kusumi  *
92b7e4566STomohiro Kusumi  * Redistribution and use in source and binary forms, with or without
102b7e4566STomohiro Kusumi  * modification, are permitted provided that the following conditions
112b7e4566STomohiro Kusumi  * are met:
122b7e4566STomohiro Kusumi  *
132b7e4566STomohiro Kusumi  * 1. Redistributions of source code must retain the above copyright
142b7e4566STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer.
152b7e4566STomohiro Kusumi  * 2. Redistributions in binary form must reproduce the above copyright
162b7e4566STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer in
172b7e4566STomohiro Kusumi  *    the documentation and/or other materials provided with the
182b7e4566STomohiro Kusumi  *    distribution.
192b7e4566STomohiro Kusumi  * 3. Neither the name of The DragonFly Project nor the names of its
202b7e4566STomohiro Kusumi  *    contributors may be used to endorse or promote products derived
212b7e4566STomohiro Kusumi  *    from this software without specific, prior written permission.
222b7e4566STomohiro Kusumi  *
232b7e4566STomohiro Kusumi  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
242b7e4566STomohiro Kusumi  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
252b7e4566STomohiro Kusumi  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
262b7e4566STomohiro Kusumi  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
272b7e4566STomohiro Kusumi  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
282b7e4566STomohiro Kusumi  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
292b7e4566STomohiro Kusumi  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
302b7e4566STomohiro Kusumi  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
312b7e4566STomohiro Kusumi  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
322b7e4566STomohiro Kusumi  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
332b7e4566STomohiro Kusumi  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
342b7e4566STomohiro Kusumi  * SUCH DAMAGE.
352b7e4566STomohiro Kusumi  */
362b7e4566STomohiro Kusumi 
37*73da1719STomohiro Kusumi // # 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
382b7e4566STomohiro Kusumi 
392b7e4566STomohiro Kusumi #include <sys/types.h>
402b7e4566STomohiro Kusumi #include <sys/stat.h>
412b7e4566STomohiro Kusumi #include <unistd.h>
422b7e4566STomohiro Kusumi #include <fcntl.h>
432b7e4566STomohiro Kusumi #include <stdio.h>
442b7e4566STomohiro Kusumi #include <stdlib.h>
452b7e4566STomohiro Kusumi #include <stdbool.h>
462b7e4566STomohiro Kusumi #include <string.h>
472b7e4566STomohiro Kusumi #include <assert.h>
482b7e4566STomohiro Kusumi 
492b7e4566STomohiro Kusumi #include <openssl/sha.h>
502b7e4566STomohiro Kusumi 
512b7e4566STomohiro Kusumi #include <vfs/hammer2/hammer2_disk.h>
522b7e4566STomohiro Kusumi #include <vfs/hammer2/hammer2_xxhash.h>
532b7e4566STomohiro Kusumi 
542b7e4566STomohiro Kusumi #include "hammer2_subs.h"
552b7e4566STomohiro Kusumi 
560b738157STomohiro Kusumi static int modify_volume_header(hammer2_volume_data_t *,
572b7e4566STomohiro Kusumi     const hammer2_blockref_t *);
580b738157STomohiro Kusumi static int modify_blockref(const hammer2_volume_data_t *, int,
592b7e4566STomohiro Kusumi     hammer2_blockref_t *, hammer2_blockref_t *, int);
600b738157STomohiro Kusumi static int modify_check(int, hammer2_blockref_t *, const hammer2_blockref_t *,
610b738157STomohiro Kusumi     hammer2_media_data_t *, size_t, int);
622b7e4566STomohiro Kusumi 
632b7e4566STomohiro Kusumi static bool ForceOpt = false;
642b7e4566STomohiro Kusumi 
652b7e4566STomohiro Kusumi static int
reconstruct_volume_header(void)660b738157STomohiro Kusumi reconstruct_volume_header(void)
672b7e4566STomohiro Kusumi {
682b7e4566STomohiro Kusumi 	bool failed = false;
692b7e4566STomohiro Kusumi 	int i;
702b7e4566STomohiro Kusumi 
712b7e4566STomohiro Kusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
722b7e4566STomohiro Kusumi 		hammer2_volume_data_t voldata;
732b7e4566STomohiro Kusumi 		hammer2_blockref_t broot;
740b738157STomohiro Kusumi 		hammer2_off_t off;
752b7e4566STomohiro Kusumi 		ssize_t ret;
762b7e4566STomohiro Kusumi 
772b7e4566STomohiro Kusumi 		memset(&broot, 0, sizeof(broot));
782b7e4566STomohiro Kusumi 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
790b738157STomohiro Kusumi 		off = broot.data_off & ~HAMMER2_OFF_MASK_RADIX;
800b738157STomohiro Kusumi 		if (lseek(hammer2_get_root_volume_fd(),
810b738157STomohiro Kusumi 		    off - hammer2_get_root_volume_offset(), SEEK_SET) == -1) {
822b7e4566STomohiro Kusumi 			perror("lseek");
832b7e4566STomohiro Kusumi 			return -1;
842b7e4566STomohiro Kusumi 		}
852b7e4566STomohiro Kusumi 
860b738157STomohiro Kusumi 		ret = read(hammer2_get_root_volume_fd(), &voldata,
87*73da1719STomohiro Kusumi 		    HAMMER2_VOLUME_BYTES);
88*73da1719STomohiro Kusumi 		if (ret == HAMMER2_VOLUME_BYTES) {
892b7e4566STomohiro Kusumi 			fprintf(stdout, "zone.%d %016jx\n",
902b7e4566STomohiro Kusumi 			    i, (uintmax_t)broot.data_off);
910b738157STomohiro Kusumi 			if (modify_volume_header(&voldata, &broot) == -1)
922b7e4566STomohiro Kusumi 				failed = true;
932b7e4566STomohiro Kusumi 		} else if (ret == -1) {
942b7e4566STomohiro Kusumi 			perror("read");
952b7e4566STomohiro Kusumi 			return -1;
962b7e4566STomohiro Kusumi 		} else {
972b7e4566STomohiro Kusumi 			fprintf(stderr, "Failed to read volume header\n");
982b7e4566STomohiro Kusumi 			return -1;
992b7e4566STomohiro Kusumi 		}
1002b7e4566STomohiro Kusumi 	}
1012b7e4566STomohiro Kusumi 
1022b7e4566STomohiro Kusumi 	return failed ? -1 : 0;
1032b7e4566STomohiro Kusumi }
1042b7e4566STomohiro Kusumi 
1052b7e4566STomohiro Kusumi static int
reconstruct_blockref(uint8_t type)1060b738157STomohiro Kusumi reconstruct_blockref(uint8_t type)
1072b7e4566STomohiro Kusumi {
1082b7e4566STomohiro Kusumi 	bool failed = false;
1092b7e4566STomohiro Kusumi 	int i;
1102b7e4566STomohiro Kusumi 
1112b7e4566STomohiro Kusumi 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
1122b7e4566STomohiro Kusumi 		hammer2_volume_data_t voldata;
1132b7e4566STomohiro Kusumi 		hammer2_blockref_t broot;
1140b738157STomohiro Kusumi 		hammer2_off_t off;
1152b7e4566STomohiro Kusumi 		ssize_t ret;
1162b7e4566STomohiro Kusumi 
1172b7e4566STomohiro Kusumi 		memset(&broot, 0, sizeof(broot));
1182b7e4566STomohiro Kusumi 		broot.type = type;
1192b7e4566STomohiro Kusumi 		broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
1200b738157STomohiro Kusumi 		off = broot.data_off & ~HAMMER2_OFF_MASK_RADIX;
1210b738157STomohiro Kusumi 		if (lseek(hammer2_get_root_volume_fd(),
1220b738157STomohiro Kusumi 		    off - hammer2_get_root_volume_offset(), SEEK_SET) == -1) {
1232b7e4566STomohiro Kusumi 			perror("lseek");
1242b7e4566STomohiro Kusumi 			return -1;
1252b7e4566STomohiro Kusumi 		}
1262b7e4566STomohiro Kusumi 
1270b738157STomohiro Kusumi 		ret = read(hammer2_get_root_volume_fd(), &voldata,
128*73da1719STomohiro Kusumi 		    HAMMER2_VOLUME_BYTES);
129*73da1719STomohiro Kusumi 		if (ret == HAMMER2_VOLUME_BYTES) {
1302b7e4566STomohiro Kusumi 			fprintf(stdout, "zone.%d %016jx\n",
1312b7e4566STomohiro Kusumi 			    i, (uintmax_t)broot.data_off);
1320b738157STomohiro Kusumi 			if (modify_blockref(&voldata, -1, &broot, NULL, -1) ==
1330b738157STomohiro Kusumi 			    -1)
1342b7e4566STomohiro Kusumi 				failed = true;
1352b7e4566STomohiro Kusumi 		} else if (ret == -1) {
1362b7e4566STomohiro Kusumi 			perror("read");
1372b7e4566STomohiro Kusumi 			return -1;
1382b7e4566STomohiro Kusumi 		} else {
1392b7e4566STomohiro Kusumi 			fprintf(stderr, "Failed to read volume header\n");
1402b7e4566STomohiro Kusumi 			return -1;
1412b7e4566STomohiro Kusumi 		}
1422b7e4566STomohiro Kusumi 	}
1432b7e4566STomohiro Kusumi 
1442b7e4566STomohiro Kusumi 	return failed ? -1 : 0;
1452b7e4566STomohiro Kusumi }
1462b7e4566STomohiro Kusumi 
1472b7e4566STomohiro Kusumi static int
modify_volume_header(hammer2_volume_data_t * voldata,const hammer2_blockref_t * bref)1480b738157STomohiro Kusumi modify_volume_header(hammer2_volume_data_t *voldata,
1492b7e4566STomohiro Kusumi     const hammer2_blockref_t *bref)
1502b7e4566STomohiro Kusumi {
1512b7e4566STomohiro Kusumi 	hammer2_crc32_t crc0, crc1;
1522b7e4566STomohiro Kusumi 	const char *s = NULL;
1532b7e4566STomohiro Kusumi 	bool found = false;
1542b7e4566STomohiro Kusumi 
1552b7e4566STomohiro Kusumi 	if ((voldata->magic != HAMMER2_VOLUME_ID_HBO) &&
1562b7e4566STomohiro Kusumi 	    (voldata->magic != HAMMER2_VOLUME_ID_ABO)) {
1572b7e4566STomohiro Kusumi 		fprintf(stderr, "Bad magic %jX\n", voldata->magic);
1582b7e4566STomohiro Kusumi 		return -1;
1592b7e4566STomohiro Kusumi 	}
1602b7e4566STomohiro Kusumi 
1612b7e4566STomohiro Kusumi 	if (voldata->magic == HAMMER2_VOLUME_ID_ABO)
1622b7e4566STomohiro Kusumi 		fprintf(stderr, "Reverse endian\n");
1632b7e4566STomohiro Kusumi 
1642b7e4566STomohiro Kusumi 	/* Need to test HAMMER2_VOL_ICRC_SECT1 first. */
1652b7e4566STomohiro Kusumi 	crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
1662b7e4566STomohiro Kusumi 	crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRC1_OFF,
1672b7e4566STomohiro Kusumi 	    HAMMER2_VOLUME_ICRC1_SIZE);
1682b7e4566STomohiro Kusumi 	if (crc0 != crc1) {
1692b7e4566STomohiro Kusumi 		if (ForceOpt)
1702b7e4566STomohiro Kusumi 			voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT1] = crc1;
1712b7e4566STomohiro Kusumi 		found = true;
1722b7e4566STomohiro Kusumi 		s = "HAMMER2_VOL_ICRC_SECT1";
1732b7e4566STomohiro Kusumi 		printf("%s%016jx %s\n", ForceOpt ? "Modified " : "",
1742b7e4566STomohiro Kusumi 		    (uintmax_t)bref->data_off, s);
1752b7e4566STomohiro Kusumi 	}
1762b7e4566STomohiro Kusumi 
1772b7e4566STomohiro Kusumi 	crc0 = voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
1782b7e4566STomohiro Kusumi 	crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRC0_OFF,
1792b7e4566STomohiro Kusumi 	    HAMMER2_VOLUME_ICRC0_SIZE);
1802b7e4566STomohiro Kusumi 	if (crc0 != crc1) {
1812b7e4566STomohiro Kusumi 		if (ForceOpt)
1822b7e4566STomohiro Kusumi 			voldata->icrc_sects[HAMMER2_VOL_ICRC_SECT0] = crc1;
1832b7e4566STomohiro Kusumi 		found = true;
1842b7e4566STomohiro Kusumi 		s = "HAMMER2_VOL_ICRC_SECT0";
1852b7e4566STomohiro Kusumi 		printf("%s%016jx %s\n", ForceOpt ? "Modified " : "",
1862b7e4566STomohiro Kusumi 		    (uintmax_t)bref->data_off, s);
1872b7e4566STomohiro Kusumi 	}
1882b7e4566STomohiro Kusumi 
1892b7e4566STomohiro Kusumi 	crc0 = voldata->icrc_volheader;
1902b7e4566STomohiro Kusumi 	crc1 = hammer2_icrc32((char*)voldata + HAMMER2_VOLUME_ICRCVH_OFF,
1912b7e4566STomohiro Kusumi 	    HAMMER2_VOLUME_ICRCVH_SIZE);
1922b7e4566STomohiro Kusumi 	if (crc0 != crc1) {
1932b7e4566STomohiro Kusumi 		if (ForceOpt)
1942b7e4566STomohiro Kusumi 			voldata->icrc_volheader = crc1;
1952b7e4566STomohiro Kusumi 		found = true;
1962b7e4566STomohiro Kusumi 		s = "volume header CRC";
1972b7e4566STomohiro Kusumi 		printf("%s%016jx %s\n", ForceOpt ? "Modified " : "",
1982b7e4566STomohiro Kusumi 		    (uintmax_t)bref->data_off, s);
1992b7e4566STomohiro Kusumi 	}
2002b7e4566STomohiro Kusumi 
2012b7e4566STomohiro Kusumi 	if (found && ForceOpt) {
2022b7e4566STomohiro Kusumi 		ssize_t ret;
2030b738157STomohiro Kusumi 		int fd = hammer2_get_root_volume_fd();
2040b738157STomohiro Kusumi 		hammer2_off_t off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
2050b738157STomohiro Kusumi 		if (lseek(fd, off - hammer2_get_root_volume_offset(), SEEK_SET)
2060b738157STomohiro Kusumi 		    == -1) {
2072b7e4566STomohiro Kusumi 			perror("lseek");
2082b7e4566STomohiro Kusumi 			return -1;
2092b7e4566STomohiro Kusumi 		}
2102b7e4566STomohiro Kusumi 		ret = write(fd, voldata, HAMMER2_PBUFSIZE);
2112b7e4566STomohiro Kusumi 		if (ret == -1) {
2122b7e4566STomohiro Kusumi 			perror("write");
2132b7e4566STomohiro Kusumi 			return -1;
2142b7e4566STomohiro Kusumi 		} else if (ret != (ssize_t)HAMMER2_PBUFSIZE) {
2152b7e4566STomohiro Kusumi 			fprintf(stderr, "Failed to write volume header\n");
2162b7e4566STomohiro Kusumi 			return -1;
2172b7e4566STomohiro Kusumi 		}
2182b7e4566STomohiro Kusumi 		if (fsync(fd) == -1) {
2192b7e4566STomohiro Kusumi 			perror("fsync");
2202b7e4566STomohiro Kusumi 			return -1;
2212b7e4566STomohiro Kusumi 		}
2222b7e4566STomohiro Kusumi 	}
2232b7e4566STomohiro Kusumi 
2242b7e4566STomohiro Kusumi 	return 0;
2252b7e4566STomohiro Kusumi }
2262b7e4566STomohiro Kusumi 
2272b7e4566STomohiro Kusumi static int
read_media(const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t * media_bytes)2280b738157STomohiro Kusumi read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
2292b7e4566STomohiro Kusumi     size_t *media_bytes)
2302b7e4566STomohiro Kusumi {
2312b7e4566STomohiro Kusumi 	hammer2_off_t io_off, io_base;
2322b7e4566STomohiro Kusumi 	size_t bytes, io_bytes, boff;
2332b7e4566STomohiro Kusumi 	ssize_t ret;
2340b738157STomohiro Kusumi 	int fd;
2352b7e4566STomohiro Kusumi 
2362b7e4566STomohiro Kusumi 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
2372b7e4566STomohiro Kusumi 	if (bytes)
2382b7e4566STomohiro Kusumi 		bytes = (size_t)1 << bytes;
2392b7e4566STomohiro Kusumi 	if (media_bytes)
2402b7e4566STomohiro Kusumi 		*media_bytes = bytes;
2412b7e4566STomohiro Kusumi 
2422b7e4566STomohiro Kusumi 	if (!bytes)
2432b7e4566STomohiro Kusumi 		return 0;
2442b7e4566STomohiro Kusumi 
2452b7e4566STomohiro Kusumi 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
2465dade8cfSTomohiro Kusumi 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
2472b7e4566STomohiro Kusumi 	boff = io_off - io_base;
2482b7e4566STomohiro Kusumi 
2495dade8cfSTomohiro Kusumi 	io_bytes = HAMMER2_LBUFSIZE;
2502b7e4566STomohiro Kusumi 	while (io_bytes + boff < bytes)
2512b7e4566STomohiro Kusumi 		io_bytes <<= 1;
2522b7e4566STomohiro Kusumi 
2532b7e4566STomohiro Kusumi 	if (io_bytes > sizeof(*media)) {
2542b7e4566STomohiro Kusumi 		fprintf(stderr, "Bad I/O bytes\n");
2552b7e4566STomohiro Kusumi 		return -1;
2562b7e4566STomohiro Kusumi 	}
2570b738157STomohiro Kusumi 	fd = hammer2_get_volume_fd(io_off);
2580b738157STomohiro Kusumi 	if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
2590b738157STomohiro Kusumi 	    == -1) {
2602b7e4566STomohiro Kusumi 		perror("lseek");
2612b7e4566STomohiro Kusumi 		return -1;
2622b7e4566STomohiro Kusumi 	}
2632b7e4566STomohiro Kusumi 	ret = read(fd, media, io_bytes);
2642b7e4566STomohiro Kusumi 	if (ret == -1) {
2652b7e4566STomohiro Kusumi 		perror("read");
2662b7e4566STomohiro Kusumi 		return -1;
2672b7e4566STomohiro Kusumi 	} else if (ret != (ssize_t)io_bytes) {
2682b7e4566STomohiro Kusumi 		fprintf(stderr, "Failed to read media\n");
2692b7e4566STomohiro Kusumi 		return -1;
2702b7e4566STomohiro Kusumi 	}
2712b7e4566STomohiro Kusumi 	if (boff)
2722b7e4566STomohiro Kusumi 		memmove(media, (char *)media + boff, bytes);
2732b7e4566STomohiro Kusumi 
2742b7e4566STomohiro Kusumi 	return 0;
2752b7e4566STomohiro Kusumi }
2762b7e4566STomohiro Kusumi 
2772b7e4566STomohiro Kusumi static int
write_media(const hammer2_blockref_t * bref,const hammer2_media_data_t * media,size_t media_bytes)2780b738157STomohiro Kusumi write_media(const hammer2_blockref_t *bref, const hammer2_media_data_t *media,
2790b738157STomohiro Kusumi     size_t media_bytes)
2802b7e4566STomohiro Kusumi {
2812b7e4566STomohiro Kusumi 	hammer2_off_t io_off, io_base;
2822b7e4566STomohiro Kusumi 	char buf[HAMMER2_PBUFSIZE];
2832b7e4566STomohiro Kusumi 	size_t bytes, io_bytes, boff;
2842b7e4566STomohiro Kusumi 	ssize_t ret;
2850b738157STomohiro Kusumi 	int fd;
2862b7e4566STomohiro Kusumi 
2872b7e4566STomohiro Kusumi 	bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
2882b7e4566STomohiro Kusumi 	if (bytes)
2892b7e4566STomohiro Kusumi 		bytes = (size_t)1 << bytes;
2902b7e4566STomohiro Kusumi 	assert(bytes != 0);
2912b7e4566STomohiro Kusumi 	assert(bytes == media_bytes);
2922b7e4566STomohiro Kusumi 
2932b7e4566STomohiro Kusumi 	io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
2945dade8cfSTomohiro Kusumi 	io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
2952b7e4566STomohiro Kusumi 	boff = io_off - io_base;
2962b7e4566STomohiro Kusumi 
2975dade8cfSTomohiro Kusumi 	io_bytes = HAMMER2_LBUFSIZE;
2982b7e4566STomohiro Kusumi 	while (io_bytes + boff < bytes)
2992b7e4566STomohiro Kusumi 		io_bytes <<= 1;
3002b7e4566STomohiro Kusumi 
3012b7e4566STomohiro Kusumi 	if (io_bytes > sizeof(buf)) {
3022b7e4566STomohiro Kusumi 		fprintf(stderr, "Bad I/O bytes\n");
3032b7e4566STomohiro Kusumi 		return -1;
3042b7e4566STomohiro Kusumi 	}
3050b738157STomohiro Kusumi 	fd = hammer2_get_volume_fd(io_off);
3060b738157STomohiro Kusumi 	if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
3070b738157STomohiro Kusumi 	    == -1) {
3082b7e4566STomohiro Kusumi 		perror("lseek");
3092b7e4566STomohiro Kusumi 		return -1;
3102b7e4566STomohiro Kusumi 	}
3112b7e4566STomohiro Kusumi 	if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) {
3122b7e4566STomohiro Kusumi 		perror("read");
3132b7e4566STomohiro Kusumi 		return -1;
3142b7e4566STomohiro Kusumi 	}
3152b7e4566STomohiro Kusumi 
3162b7e4566STomohiro Kusumi 	memcpy(buf + boff, media, media_bytes);
3170b738157STomohiro Kusumi 	if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
3180b738157STomohiro Kusumi 	    == -1) {
3192b7e4566STomohiro Kusumi 		perror("lseek");
3202b7e4566STomohiro Kusumi 		return -1;
3212b7e4566STomohiro Kusumi 	}
3222b7e4566STomohiro Kusumi 	ret = write(fd, buf, io_bytes);
3232b7e4566STomohiro Kusumi 	if (ret == -1) {
3242b7e4566STomohiro Kusumi 		perror("write");
3252b7e4566STomohiro Kusumi 		return -1;
3262b7e4566STomohiro Kusumi 	} else if (ret != (ssize_t)io_bytes) {
3272b7e4566STomohiro Kusumi 		fprintf(stderr, "Failed to write media\n");
3282b7e4566STomohiro Kusumi 		return -1;
3292b7e4566STomohiro Kusumi 	}
3302b7e4566STomohiro Kusumi 	if (fsync(fd) == -1) {
3312b7e4566STomohiro Kusumi 		perror("fsync");
3322b7e4566STomohiro Kusumi 		return -1;
3332b7e4566STomohiro Kusumi 	}
3342b7e4566STomohiro Kusumi 
3352b7e4566STomohiro Kusumi 	return 0;
3362b7e4566STomohiro Kusumi }
3372b7e4566STomohiro Kusumi 
3382b7e4566STomohiro Kusumi static int
modify_blockref(const hammer2_volume_data_t * voldata,int bi,hammer2_blockref_t * bref,hammer2_blockref_t * prev_bref,int depth)3390b738157STomohiro Kusumi modify_blockref(const hammer2_volume_data_t *voldata, int bi,
3402b7e4566STomohiro Kusumi     hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref, int depth)
3412b7e4566STomohiro Kusumi {
3422b7e4566STomohiro Kusumi 	hammer2_media_data_t media;
3432b7e4566STomohiro Kusumi 	hammer2_blockref_t *bscan;
3442b7e4566STomohiro Kusumi 	int i, bcount;
3452b7e4566STomohiro Kusumi 	size_t bytes;
3462b7e4566STomohiro Kusumi 
3470b738157STomohiro Kusumi 	if (read_media(bref, &media, &bytes) == -1)
3482b7e4566STomohiro Kusumi 		return -1;
3492b7e4566STomohiro Kusumi 
3502b7e4566STomohiro Kusumi 	if (!bytes)
3512b7e4566STomohiro Kusumi 		return 0;
3522b7e4566STomohiro Kusumi 
3532b7e4566STomohiro Kusumi 	switch (bref->type) {
3542b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_INODE:
3552b7e4566STomohiro Kusumi 		if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
3562b7e4566STomohiro Kusumi 			bscan = &media.ipdata.u.blockset.blockref[0];
3572b7e4566STomohiro Kusumi 			bcount = HAMMER2_SET_COUNT;
3582b7e4566STomohiro Kusumi 		} else {
3592b7e4566STomohiro Kusumi 			bscan = NULL;
3602b7e4566STomohiro Kusumi 			bcount = 0;
3612b7e4566STomohiro Kusumi 		}
3622b7e4566STomohiro Kusumi 		break;
3632b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_INDIRECT:
3642b7e4566STomohiro Kusumi 		bscan = &media.npdata[0];
3652b7e4566STomohiro Kusumi 		bcount = bytes / sizeof(hammer2_blockref_t);
3662b7e4566STomohiro Kusumi 		break;
3672b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
3682b7e4566STomohiro Kusumi 		bscan = &media.npdata[0];
3692b7e4566STomohiro Kusumi 		bcount = bytes / sizeof(hammer2_blockref_t);
3702b7e4566STomohiro Kusumi 		break;
3712b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_VOLUME:
3722b7e4566STomohiro Kusumi 		bscan = &media.voldata.sroot_blockset.blockref[0];
3732b7e4566STomohiro Kusumi 		bcount = HAMMER2_SET_COUNT;
3742b7e4566STomohiro Kusumi 		break;
3752b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_FREEMAP:
3762b7e4566STomohiro Kusumi 		bscan = &media.voldata.freemap_blockset.blockref[0];
3772b7e4566STomohiro Kusumi 		bcount = HAMMER2_SET_COUNT;
3782b7e4566STomohiro Kusumi 		break;
3792b7e4566STomohiro Kusumi 	default:
3802b7e4566STomohiro Kusumi 		bscan = NULL;
3812b7e4566STomohiro Kusumi 		bcount = 0;
3822b7e4566STomohiro Kusumi 		break;
3832b7e4566STomohiro Kusumi 	}
3842b7e4566STomohiro Kusumi 
3852b7e4566STomohiro Kusumi 	for (i = 0; i < bcount; ++i)
3862b7e4566STomohiro Kusumi 		if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
3870b738157STomohiro Kusumi 			if (modify_blockref(voldata, i, &bscan[i], bref,
3882b7e4566STomohiro Kusumi 			    depth + 1) == -1)
3892b7e4566STomohiro Kusumi 				return -1;
3902b7e4566STomohiro Kusumi 
3912b7e4566STomohiro Kusumi 	if (ForceOpt)
3920b738157STomohiro Kusumi 		if (read_media(bref, &media, &bytes) == -1)
3932b7e4566STomohiro Kusumi 			return -1;
3940b738157STomohiro Kusumi 	if (modify_check(bi, prev_bref, bref, &media, bytes, depth) == -1)
3952b7e4566STomohiro Kusumi 		return -1;
3962b7e4566STomohiro Kusumi 
3972b7e4566STomohiro Kusumi 	return 0;
3982b7e4566STomohiro Kusumi }
3992b7e4566STomohiro Kusumi 
4002b7e4566STomohiro Kusumi 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)4010b738157STomohiro Kusumi modify_check(int bi, hammer2_blockref_t *prev_bref,
4022b7e4566STomohiro Kusumi     const hammer2_blockref_t *bref, hammer2_media_data_t *media,
4032b7e4566STomohiro Kusumi     size_t media_bytes, int depth)
4042b7e4566STomohiro Kusumi {
4052b7e4566STomohiro Kusumi 	hammer2_media_data_t bscan_media;
4062b7e4566STomohiro Kusumi 	hammer2_blockref_t *bscan;
4072b7e4566STomohiro Kusumi 	bool found = false;
4082b7e4566STomohiro Kusumi 	size_t bytes;
4092b7e4566STomohiro Kusumi 	uint32_t cv;
4102b7e4566STomohiro Kusumi 	uint64_t cv64;
4112b7e4566STomohiro Kusumi 
4122b7e4566STomohiro Kusumi 	//SHA256_CTX hash_ctx;
4132b7e4566STomohiro Kusumi 	union {
4142b7e4566STomohiro Kusumi 		uint8_t digest[SHA256_DIGEST_LENGTH];
4152b7e4566STomohiro Kusumi 		uint64_t digest64[SHA256_DIGEST_LENGTH/8];
4162b7e4566STomohiro Kusumi 	} u;
4172b7e4566STomohiro Kusumi 
4182b7e4566STomohiro Kusumi 
4192b7e4566STomohiro Kusumi 	if (!prev_bref)
4202b7e4566STomohiro Kusumi 		return 0;
4210b738157STomohiro Kusumi 	if (read_media(prev_bref, &bscan_media, &bytes) == -1)
4222b7e4566STomohiro Kusumi 		return -1;
4232b7e4566STomohiro Kusumi 	assert(bytes);
4242b7e4566STomohiro Kusumi 
4252b7e4566STomohiro Kusumi 	switch (prev_bref->type) {
4262b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_INODE:
4272b7e4566STomohiro Kusumi 		if (!(bscan_media.ipdata.meta.op_flags &
4282b7e4566STomohiro Kusumi 		    HAMMER2_OPFLAG_DIRECTDATA))
4292b7e4566STomohiro Kusumi 			bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
4302b7e4566STomohiro Kusumi 		else
4312b7e4566STomohiro Kusumi 			bscan = NULL;
4322b7e4566STomohiro Kusumi 		break;
4332b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_INDIRECT:
4342b7e4566STomohiro Kusumi 		bscan = &bscan_media.npdata[bi];
4352b7e4566STomohiro Kusumi 		break;
4362b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_VOLUME:
4372b7e4566STomohiro Kusumi 		bscan = &bscan_media.voldata.sroot_blockset.blockref[bi];
4382b7e4566STomohiro Kusumi 		break;
4392b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_FREEMAP:
4402b7e4566STomohiro Kusumi 		bscan = &bscan_media.voldata.freemap_blockset.blockref[bi];
4412b7e4566STomohiro Kusumi 		break;
4422b7e4566STomohiro Kusumi 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
4432b7e4566STomohiro Kusumi 		bscan = &bscan_media.npdata[bi];
4442b7e4566STomohiro Kusumi 		break;
4452b7e4566STomohiro Kusumi 	default:
4462b7e4566STomohiro Kusumi 		assert(0);
4472b7e4566STomohiro Kusumi 		break;
4482b7e4566STomohiro Kusumi 	}
4492b7e4566STomohiro Kusumi 
4502b7e4566STomohiro Kusumi 	if (memcmp(bref, bscan, sizeof(*bref))) {
4512b7e4566STomohiro Kusumi 		fprintf(stderr, "Blockref contents mismatch\n");
4522b7e4566STomohiro Kusumi 		return -1;
4532b7e4566STomohiro Kusumi 	}
4542b7e4566STomohiro Kusumi 
4552b7e4566STomohiro Kusumi 	switch (HAMMER2_DEC_CHECK(bscan->methods)) {
4562b7e4566STomohiro Kusumi 	case HAMMER2_CHECK_ISCSI32:
4572b7e4566STomohiro Kusumi 		cv = hammer2_icrc32(media, media_bytes);
4582b7e4566STomohiro Kusumi 		if (bscan->check.iscsi32.value != cv) {
4592b7e4566STomohiro Kusumi 			if (ForceOpt)
4602b7e4566STomohiro Kusumi 				bscan->check.iscsi32.value = cv;
4612b7e4566STomohiro Kusumi 			found = true;
4622b7e4566STomohiro Kusumi 		}
4632b7e4566STomohiro Kusumi 		break;
4642b7e4566STomohiro Kusumi 	case HAMMER2_CHECK_XXHASH64:
4652b7e4566STomohiro Kusumi 		cv64 = XXH64(media, media_bytes, XXH_HAMMER2_SEED);
4662b7e4566STomohiro Kusumi 		if (bscan->check.xxhash64.value != cv64) {
4672b7e4566STomohiro Kusumi 			if (ForceOpt)
4682b7e4566STomohiro Kusumi 				bscan->check.xxhash64.value = cv64;
4692b7e4566STomohiro Kusumi 			found = true;
4702b7e4566STomohiro Kusumi 		}
4712b7e4566STomohiro Kusumi 		break;
4722b7e4566STomohiro Kusumi 	case HAMMER2_CHECK_SHA192:
4732b7e4566STomohiro Kusumi #if 0
4742b7e4566STomohiro Kusumi 		SHA256_Init(&hash_ctx);
4752b7e4566STomohiro Kusumi 		SHA256_Update(&hash_ctx, &media, bytes);
4762b7e4566STomohiro Kusumi 		SHA256_Final(u.digest, &hash_ctx);
4772b7e4566STomohiro Kusumi #endif
4782b7e4566STomohiro Kusumi 		u.digest64[2] ^= u.digest64[3];
4792b7e4566STomohiro Kusumi 		if (memcmp(u.digest, bscan->check.sha192.data,
4802b7e4566STomohiro Kusumi 		    sizeof(bscan->check.sha192.data))) {
4812b7e4566STomohiro Kusumi 			if (ForceOpt)
4822b7e4566STomohiro Kusumi 				memcpy(&bscan->check.sha192.data, u.digest,
4832b7e4566STomohiro Kusumi 				    sizeof(bscan->check.sha192.data));
4842b7e4566STomohiro Kusumi 			found = true;
4852b7e4566STomohiro Kusumi 		}
4862b7e4566STomohiro Kusumi 		fprintf(stderr, "HAMMER2_CHECK_SHA192 unsupported\n");
4872b7e4566STomohiro Kusumi 		assert(0);
4882b7e4566STomohiro Kusumi 		break;
4892b7e4566STomohiro Kusumi 	case HAMMER2_CHECK_FREEMAP:
4902b7e4566STomohiro Kusumi 		cv = hammer2_icrc32(media, media_bytes);
4912b7e4566STomohiro Kusumi 		if (bscan->check.freemap.icrc32 != cv) {
4922b7e4566STomohiro Kusumi 			if (ForceOpt)
4932b7e4566STomohiro Kusumi 				bscan->check.freemap.icrc32 = cv;
4942b7e4566STomohiro Kusumi 			found = true;
4952b7e4566STomohiro Kusumi 		}
4962b7e4566STomohiro Kusumi 		break;
4972b7e4566STomohiro Kusumi 	}
4982b7e4566STomohiro Kusumi 
4992b7e4566STomohiro Kusumi 	if (found) {
5002b7e4566STomohiro Kusumi 		if (ForceOpt) {
5010b738157STomohiro Kusumi 			if (write_media(prev_bref, &bscan_media, bytes) == -1)
5022b7e4566STomohiro Kusumi 				return -1;
5032b7e4566STomohiro Kusumi 		}
5042b7e4566STomohiro Kusumi 		/* If !ForceOpt, only first bad blockref is printed. */
5052b7e4566STomohiro Kusumi 		printf("%s%2d %-8s blockref[%-3d] %016jx %02x %s\n",
5062b7e4566STomohiro Kusumi 		    ForceOpt ? "Modified " : "",
5072b7e4566STomohiro Kusumi 		    depth, hammer2_breftype_to_str(prev_bref->type), bi,
5082b7e4566STomohiro Kusumi 		    (uintmax_t)bscan->data_off, bscan->methods,
5092b7e4566STomohiro Kusumi 		    hammer2_breftype_to_str(bscan->type));
5102b7e4566STomohiro Kusumi 	}
5112b7e4566STomohiro Kusumi 
5122b7e4566STomohiro Kusumi 	return 0;
5132b7e4566STomohiro Kusumi }
5142b7e4566STomohiro Kusumi 
5152b7e4566STomohiro Kusumi int
main(int argc,char ** argv)5162b7e4566STomohiro Kusumi main(int argc, char **argv)
5172b7e4566STomohiro Kusumi {
5180b738157STomohiro Kusumi 	int ch;
5192b7e4566STomohiro Kusumi 	const char *binpath = argv[0];
5202b7e4566STomohiro Kusumi 	const char *devpath;
5212b7e4566STomohiro Kusumi 
5222b7e4566STomohiro Kusumi 	while ((ch = getopt(argc, argv, "f")) != -1) {
5232b7e4566STomohiro Kusumi 		switch(ch) {
5242b7e4566STomohiro Kusumi 		case 'f':
5252b7e4566STomohiro Kusumi 			ForceOpt = true;
5262b7e4566STomohiro Kusumi 			break;
5272b7e4566STomohiro Kusumi 		default:
5282b7e4566STomohiro Kusumi 			break;
5292b7e4566STomohiro Kusumi 		}
5302b7e4566STomohiro Kusumi 	}
5312b7e4566STomohiro Kusumi 	argc -= optind;
5322b7e4566STomohiro Kusumi 	argv += optind;
5332b7e4566STomohiro Kusumi 
5342b7e4566STomohiro Kusumi 	if (argc < 1) {
5352b7e4566STomohiro Kusumi 		fprintf(stderr, "%s [-f] special\n", binpath);
5362b7e4566STomohiro Kusumi 		exit(1);
5372b7e4566STomohiro Kusumi 	}
5382b7e4566STomohiro Kusumi 	devpath = argv[0];
5390b738157STomohiro Kusumi 	hammer2_init_volumes(devpath, 0);
5402b7e4566STomohiro Kusumi 
5412b7e4566STomohiro Kusumi 	printf("freemap\n");
5420b738157STomohiro Kusumi 	if (reconstruct_blockref(HAMMER2_BREF_TYPE_FREEMAP) == -1)
5432b7e4566STomohiro Kusumi 		exit(1);
5442b7e4566STomohiro Kusumi 	printf("volume\n");
5450b738157STomohiro Kusumi 	if (reconstruct_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1)
5462b7e4566STomohiro Kusumi 		exit(1);
5472b7e4566STomohiro Kusumi 
5482b7e4566STomohiro Kusumi 	printf("volume header\n");
5490b738157STomohiro Kusumi 	if (reconstruct_volume_header() == -1)
5502b7e4566STomohiro Kusumi 		exit(1);
5512b7e4566STomohiro Kusumi 
5520b738157STomohiro Kusumi 	hammer2_cleanup_volumes();
5532b7e4566STomohiro Kusumi 
5542b7e4566STomohiro Kusumi 	return 0;
5552b7e4566STomohiro Kusumi }
556