/*
 * verify_image.c - Read each HAMMER2 volume header copy and confirm
 * the three CRC32C values match what the kernel expects. Used to
 * sanity-check patch_image.c's CRC implementation BEFORE mounting.
 *
 * Build:  cc -O2 -o verify_image verify_image.c
 * Run:    ./verify_image <image-file>
 * Exit:   0 if all present copies verify, 1 if any mismatch.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <inttypes.h>

static uint32_t crc32c_table[256];
static void init_crc32c(void) {
    for (uint32_t i = 0; i < 256; i++) {
        uint32_t crc = i;
        for (int j = 0; j < 8; j++)
            crc = (crc >> 1) ^ (0x82F63B78u & (uint32_t)(-(int32_t)(crc & 1)));
        crc32c_table[i] = crc;
    }
}
static uint32_t crc32c(const uint8_t *buf, size_t len) {
    uint32_t crc = 0xFFFFFFFFu;
    for (size_t i = 0; i < len; i++)
        crc = (crc >> 8) ^ crc32c_table[(crc ^ buf[i]) & 0xFF];
    return ~crc;
}

#define H2_VOLUME_BYTES       65536
#define H2_ZONE_BYTES64       (2LL * 1024 * 1024 * 1024)
#define H2_NUM_VOLHDRS        4
#define H2_VOLUME_ID_HBO      0x48414d3205172011ULL
#define DATA_OFF_OFF          0x220

int main(int argc, char **argv) {
    if (argc < 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); return 2; }
    init_crc32c();
    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) { perror("open"); return 1; }
    uint8_t *buf = malloc(H2_VOLUME_BYTES);
    int ok = 0, bad = 0;
    for (int i = 0; i < H2_NUM_VOLHDRS; i++) {
        off_t off = (off_t)i * H2_ZONE_BYTES64;
        ssize_t r = pread(fd, buf, H2_VOLUME_BYTES, off);
        if (r != H2_VOLUME_BYTES) continue;
        uint64_t magic; memcpy(&magic, buf, 8);
        if (magic != H2_VOLUME_ID_HBO) continue;

        uint32_t e0, c0, e1, c1, evh, cvh;
        memcpy(&e0, buf + 0x1E0 + 7*4, 4);
        memcpy(&e1, buf + 0x1E0 + 6*4, 4);
        memcpy(&evh, buf + 0xFFFC, 4);
        c0  = crc32c(buf + 0, 508);
        c1  = crc32c(buf + 512, 512);
        cvh = crc32c(buf + 0, 65532);
        uint64_t data_off; memcpy(&data_off, buf + DATA_OFF_OFF, 8);
        int good = (e0==c0 && e1==c1 && evh==cvh);
        printf("copy %d: data_off=0x%016" PRIx64
               " ICRC0 exp=%08x got=%08x %s | ICRC1 exp=%08x got=%08x %s | ICRCVH exp=%08x got=%08x %s\n",
               i, data_off,
               e0, c0, e0==c0?"OK":"MISMATCH",
               e1, c1, e1==c1?"OK":"MISMATCH",
               evh, cvh, evh==cvh?"OK":"MISMATCH");
        if (good) ok++; else bad++;
    }
    free(buf); close(fd);
    fprintf(stderr, "%d copies OK, %d BAD\n", ok, bad);
    return bad ? 1 : 0;
}
