#include <libcbd.h>

#include <libdevmapper.h>

#include <cbdutil.h>

static uint64_t
device_logical_sectors(const char* dev)
{
    int fd;
    uint8_t buf[PBLK_SIZE];
    struct cbd_header header;
    uint64_t lblk_total;

    fd = open(dev, O_RDONLY);
    if (fd < 0) {
        error("Cannot open device\n");
    }
    if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
        error("Cannot read device\n");
    }
    close(fd);
    cbd_header_get(buf, &header);
    if (memcmp(header.magic, CBD_MAGIC, sizeof(header.magic)) != 0) {
        error("Bad magic\n");
    }
    lblk_total = header.params.lblk_per_zone * header.params.nr_zones;

    return lblk_total << (header.params.lblk_shift + (PBLK_SHIFT - SECTOR_SHIFT));
}

int
cbd_open(const char* dev,
         const char* name,
         uint64_t cache_pages, bool sync)
{
    int ret;
    struct stat st;
    uint64_t nr_logical_sectors;
    char optbuf[80];
    char params[256];
    struct dm_task* dmt;
    uint32_t cookie = 0;

    ret = stat(dev, &st);
    if (ret) {
        error("Failed to stat device\n");
    }
    if (!S_ISBLK(st.st_mode)) {
        error("Not a block device\n");
    }
    nr_logical_sectors = device_logical_sectors(dev);

    strcpy(params, dev);
    if (cache_pages) {
        sprintf(optbuf, " cache_pages=%lu", (unsigned long)cache_pages);
        strcat(params, optbuf);
    }
    if (sync) {
        strcat(params, " sync");
    }

    dmt = dm_task_create(DM_DEVICE_CREATE);
    if (!dmt) {
        error("dm_task_create failed\n");
    }
    ret = dm_task_set_name(dmt, name);
    if (ret == 0) {
        error("dm_task_set_name failed\n");
    }
    printf("%s: start_sector=0 num_sectors=%lu\n", __func__,
           (unsigned long)nr_logical_sectors);
    ret = dm_task_add_target(dmt,
                             0,
                             nr_logical_sectors,
                             "compress",
                             params);
    if (ret == 0) {
        error("dm_task_add_target failed\n");
    }
    ret = dm_task_set_cookie(dmt, &cookie, 0);
    if (ret == 0) {
        error("dm_task_set_cookie failed\n");
    }

    ret = dm_task_run(dmt);
    if (ret == 0) {
        error("dm_task_run failed\n");
    }
    dm_udev_wait(cookie);

    dm_task_destroy(dmt);

    return 0;
}