/*
 * DF-2963 PoC image generator.
 *
 * Builds a GPT-labeled disk image that exposes the slice-index-128 minor
 * truncation bug:
 *
 *   - protective MBR (entry 0 type 0xEE)
 *   - GPT header @ LBA1: 128 entries, entsz 128, table @ LBA2 (32 sectors)
 *   - entry   0 -> LBA 34..63   (data pattern ENTRY0ZONE)   -> slice s0 (compat)
 *   - entry 126 -> LBA 160      (1 sector, pattern E126)    -> slice s126 (index 127, OK)
 *   - entry 127 -> LBA 1000..1099 (pattern ENTRY127ZONE)    -> slice s127 (index 128, BUG)
 *
 * The kernel creates /dev/vn0s127 for entry 127, but dkmakeminor() masks the
 * slice field to 7 bits, so its minor equals slice 0's minor.  Expected:
 * st_rdev(vn0s127) == st_rdev(vn0s0), and reads via vn0s127 return entry 0's
 * data, not entry 127's.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define SECSZ 512
#define NBLK  131072			/* 64MB */
#define NENT  128
#define TBL_LBA 2
#define TBL_BLOCKS (NENT * 128 / SECSZ)	/* 32 */

static uint32_t crc_tab[256];
static void
crc_init(void)
{
	for (uint32_t n = 0; n < 256; n++) {
		uint32_t c = n;
		for (int k = 0; k < 8; k++)
			c = (c & 1) ? 0xEDB88320U ^ (c >> 1) : c >> 1;
		crc_tab[n] = c;
	}
}
static uint32_t
crc32buf(const void *buf, size_t len)
{
	const uint8_t *p = buf;
	uint32_t c = 0xFFFFFFFFU;
	for (size_t i = 0; i < len; i++)
		c = crc_tab[(c ^ p[i]) & 0xFF] ^ (c >> 8);
	return c ^ 0xFFFFFFFFU;
}

static void
putle16(uint8_t *p, uint16_t v) { p[0]=v; p[1]=v>>8; }
static void
putle32(uint8_t *p, uint32_t v) { p[0]=v;p[1]=v>>8;p[2]=v>>16;p[3]=v>>24; }
static void
putle64(uint8_t *p, uint64_t v) { putle32(p,(uint32_t)v); putle32(p+4,(uint32_t)(v>>32)); }

/* little-endian on-disk GUID: first 3 fields LE, rest raw */
static void
putguid(uint8_t *p, uint32_t d1, uint16_t d2, uint16_t d3,
	const uint8_t d4[8])
{
	putle32(p, d1); putle16(p+4, d2); putle16(p+6, d3);
	memcpy(p+8, d4, 8);
}

static uint8_t img[(uint64_t)NBLK * SECSZ];

static void
fill_pattern(uint64_t lba, const char *tag)
{
	for (int s = 0; s < 64; s++) {
		uint8_t *p = img + lba*SECSZ; (void)s;
		break;
	}
	/* tag the first sector of the region, each 64th byte shows the tag */
	uint8_t *p = img + lba*SECSZ;
	memset(p, 0, SECSZ);
	for (int i = 0; i < SECSZ; i += 64) {
		memcpy(p + i, tag, strlen(tag) < 63 ? strlen(tag) : 63);
	}
}

static void
set_ent(int idx, uint32_t d1, uint64_t start, uint64_t end, const char *name16)
{
	uint8_t *e = img + (TBL_LBA*SECSZ) + (uint64_t)idx*128;
	uint8_t d4[8] = {1,2,3,4,5,6,7,8};
	memset(e, 0, 128);
	putguid(e,    d1, 0x1ca5, 0x11dc, d4);	/* type  */
	putguid(e+16, 0xAA000000u+(uint32_t)idx, 1, 2, d4); /* unique */
	putle64(e+32, start);
	putle64(e+40, end);
	putle64(e+48, 0);			/* attrs */
	if (name16)
		for (int i = 0; name16[i]; i++)
			putle16(e+72+2*i, (uint16_t)name16[i]);
}

int
main(int argc, char **argv)
{
	const char *out = argc > 1 ? argv[1] : "/tmp/df2963.img";
	crc_init();

	/* ---- protective MBR @ LBA0 ---- */
	uint8_t *mbr = img;
	memset(mbr, 0, SECSZ);
	mbr[0x1be + 4] = 0xEE;			/* part 0 type = GPT PMBR */
	putle32(mbr + 0x1be + 8, 1);		/* start LBA 1 */
	putle32(mbr + 0x1be + 12, 0xFFFFFFFF);	/* size */
	mbr[0x1FE] = 0x55; mbr[0x1FF] = 0xAA;

	/* ---- partition entries @ LBA2..33 ---- */
	/* entries 1..125: nil type (unused) */
	/* entry 0: EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 MS basic data */
	set_ent(0, 0xEBD0A0A2u, 34, 63, "slice0-entry0");
	/* entries 1..125 nil */
	for (int i = 1; i <= 125; i++)
		set_ent(i, 0, 0, 0, NULL);
	/* entry 126: MS basic data, LBA 160..161 */
	set_ent(126, 0xEBD0A0A2u, 160, 161, "slice126");
	/* entry 127: MS basic data, LBA 1000..1099 */
	set_ent(127, 0xEBD0A0A2u, 1000, 1099, "slice127-ALIAS");

	/* ---- GPT header @ LBA1 ---- */
	uint8_t *h = img + SECSZ;
	memset(h, 0, SECSZ);
	memcpy(h, "EFI PART", 8);
	putle32(h+8,  0x00010000);		/* revision */
	putle32(h+12, 92);			/* hdr_size */
	putle32(h+16, 0);			/* crc_self (fill below) */
	putle32(h+20, 0);			/* reserved */
	putle64(h+24, 1);			/* lba_self */
	putle64(h+32, NBLK-1);			/* lba_alt */
	putle64(h+40, 34);			/* usable start */
	putle64(h+48, NBLK-34);			/* usable end */
	putguid(h+56, 0x29632963u, 0x2963, 0x2963, (const uint8_t[]){"DF2963!"});
	putle64(h+72, TBL_LBA);		/* lba_table */
	putle32(h+80, NENT);			/* entries */
	putle32(h+84, 128);			/* entsz */
	putle32(h+88, crc32buf(img + TBL_LBA*SECSZ, NENT*128)); /* crc_table */
	putle32(h+16, crc32buf(h, 92));		/* crc_self over first 92 bytes */

	/* ---- data patterns ---- */
	fill_pattern(34,   "ENTRY0ZONE_compat_slice0");
	fill_pattern(160,  "ENTRY126ZONE");
	fill_pattern(1000, "ENTRY127ZONE_real_s127_data");

	FILE *f = fopen(out, "w");
	if (!f) { perror("fopen"); return 1; }
	if (fwrite(img, 1, sizeof(img), f) != sizeof(img)) { perror("write"); return 1; }
	fclose(f);
	printf("wrote %s (%d sectors)\n", out, NBLK);
	return 0;
}
