/*
 * DF-2831 image generator: 8 MiB MBR disk, one DFLYBSD slice s1 at LBA 128
 * (8000 sectors) containing a VALID disklabel64 with partition 'a'
 * (p_boffset 64 KiB, p_bsize 2 MiB, fstype FS_SWAP) so /dev/vnXs1a exists
 * and swapon(2) exercises dssize() -> op_getpartbounds() label walk.
 *
 * On-disk layout per sys/sys/disklabel64.h:
 *   [512 reserved][magic I][crc I][align I][nparts I][stor_uuid 16]
 *   [total_size Q][bbase Q][pbase Q][pstop Q][abase Q]
 *   [packname 64][reserved 64][partition64 x 16 (64 bytes each)]
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define SECT 512
#define NSLICE1 128            /* s1 start LBA */
#define SSLICE1 8000           /* s1 size in sectors */
#define NPARTS 16

static uint32_t crc32_buf(const unsigned char *b, size_t n)
{
    /* reflected crc32, poly 0xedb88320, init/final xor 0xffffffff */
    static uint32_t tab[256];
    static int init;
    uint32_t c;
    size_t i;
    int k;

    if (!init) {
        for (i = 0; i < 256; i++) {
            c = i;
            for (k = 0; k < 8; k++)
                c = (c & 1) ? 0xedb88320U ^ (c >> 1) : c >> 1;
            tab[i] = c;
        }
        init = 1;
    }
    c = 0xffffffffU;
    for (i = 0; i < n; i++)
        c = tab[(c ^ b[i]) & 0xff] ^ (c >> 8);
    return c ^ 0xffffffffU;
}

int main(int argc, char **argv)
{
    const char *path = argc > 1 ? argv[1] : "/root/df2831.img";
    uint64_t psize_a = argc > 2 ? strtoull(argv[2], NULL, 0)
                                : 2UL * 1024 * 1024;
    size_t nsec = 16384;                    /* 8 MiB */
    size_t sl_bytes = SSLICE1 * SECT;
    unsigned char *img;
    unsigned char *lab;
    size_t off_magic, off_end, base;
    uint32_t c;
    FILE *f;

    img = calloc(1, nsec * SECT);

    /* protective MBR: one DFLYBSD (0x6C) slice */
    img[446 + 4] = 0x6C;
    img[446 + 8]  = NSLICE1 & 0xff;
    img[446 + 9]  = (NSLICE1 >> 8) & 0xff;
    img[446 + 10] = (NSLICE1 >> 16) & 0xff;
    img[446 + 11] = (NSLICE1 >> 24) & 0xff;
    img[446 + 12] = SSLICE1 & 0xff;
    img[446 + 13] = (SSLICE1 >> 8) & 0xff;
    img[446 + 14] = (SSLICE1 >> 16) & 0xff;
    img[446 + 15] = (SSLICE1 >> 24) & 0xff;
    img[510] = 0x55;
    img[511] = 0xAA;

    lab = calloc(1, 512 + 16 + 16 + 40 + 64 + 64 + NPARTS * 64);

    off_magic = 512;
    /* d_magic */
    *(uint32_t *)(lab + off_magic) = 0xc4464c59U;
    /* d_align */
    *(uint32_t *)(lab + off_magic + 8) = 512;
    /* d_npartitions */
    *(uint32_t *)(lab + off_magic + 12) = NPARTS;
    /* d_stor_uuid */
    memset(lab + off_magic + 16, 0x33, 16);
    /* d_total_size, d_bbase, d_pbase, d_pstop, d_abase */
    *(uint64_t *)(lab + off_magic + 32)      = sl_bytes;
    *(uint64_t *)(lab + off_magic + 32 + 8)  = 4096;            /* bbase  */
    *(uint64_t *)(lab + off_magic + 32 + 16) = 65536;           /* pbase  */
    *(uint64_t *)(lab + off_magic + 32 + 24) = sl_bytes - 4096; /* pstop  */
    *(uint64_t *)(lab + off_magic + 32 + 32) = 0;               /* abase  */
    /* d_packname at 512+16+16+40 */
    strcpy((char *)lab + 512 + 16 + 16 + 40, "df2831");
    /* partitions at 512+16+16+40+64+64 */
    base = 512 + 16 + 16 + 40 + 64 + 64;
    /* partition 'a' (0): fstype FS_SWAP(1), size from argv */
    *(uint64_t *)(lab + base + 0 * 64)      = 65536;              /* p_boffset */
    *(uint64_t *)(lab + base + 0 * 64 + 8)  = psize_a;            /* p_bsize */
    lab[base + 0 * 64 + 16] = 1;                                  /* p_fstype */
    /* partition 'c' (2): whole slice, unused */
    *(uint64_t *)(lab + base + 2 * 64)     = 0;
    *(uint64_t *)(lab + base + 2 * 64 + 8) = sl_bytes;
    lab[base + 2 * 64 + 16] = 0;

    /* crc over d_magic .. d_partitions[NPARTS) */
    off_end = base + NPARTS * 64;
    c = crc32_buf(lab + off_magic, off_end - off_magic);
    *(uint32_t *)(lab + off_magic + 4) = c;

    memcpy(img + NSLICE1 * SECT, lab, off_end);
    free(lab);

    f = fopen(path, "wb");
    fwrite(img, 1, nsec * SECT, f);
    fclose(f);
    free(img);
    printf("df2831.img: 8MiB, s1=%u/%u sectors, label64 crc=%08x\n",
           NSLICE1, SSLICE1, c);
    return 0;
}
