static int qcow2_image_open(struct bdev *bdev, int dirfd, const char *pathname, int flags)
{
	struct qcow2_header buf;
	struct qcow2_header header;
	struct qcow_state *s;
	uint64_t l1_size;
	unsigned int shift;
	ssize_t read;

	s = calloc(1, sizeof(struct qcow_state));
	if (!s)
		return -1;
	bdev->private = s;

	bdev->fd = openat(dirfd, pathname, flags);
	s->fd = bdev->fd;
	if (bdev->fd == -1) {
		tcmu_err("Failed to open file: %s\n", pathname);
		goto fail_nofd;
	}

	if (pread(bdev->fd, &buf, sizeof(buf), 0) != sizeof(buf)) {
		tcmu_err("Failed to read file: %s\n", pathname);
		goto fail;
	}

	qcow2_header_bswap(&buf, &header);
	if (qcow2_validate_header(&header) < 0)
		goto fail;

	if (bdev->size != header.size) {
		tcmu_err("size misconfigured, TCMU says %" PRId64
				" but image says %" PRId64 "\n",
				bdev->size, header.size);
		goto fail;
	}
	s->size = bdev->size;
	if (bdev->block_size != 512) {
		tcmu_err("block_size misconfigured, TCMU says %" PRId32
				" but qcow only supports 512\n",
				bdev->block_size);
		goto fail;
	}

	s->cluster_bits = header.cluster_bits;
	s->cluster_size = 1 << s->cluster_bits;
	s->cluster_sectors = 1 << (s->cluster_bits - 9);
	s->l2_bits = s->cluster_bits - 3;	// L2 table is always 1 cluster in size (8 (2^3) byte entries)
	s->l2_size = 1 << s->l2_bits;
	s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1;

	shift = s->cluster_bits + s->l2_bits;
	if (header.size > UINT64_MAX - (1LL << shift)) {
		tcmu_err("Image size too big\n");
		goto fail;
	}
	l1_size = (header.size + (1LL << shift) - 1) >> shift;
	if (l1_size > INT_MAX / sizeof(uint64_t)) {
		tcmu_err("Image size too big\n");
		goto fail;
	}
	s->l1_size = l1_size;
	// why did they add this to qcow2 ?
	if (header.l1_size != s->l1_size) {
		tcmu_err("L1 size is incorrect\n");
		goto fail;
	}
	s->l1_table_offset = header.l1_table_offset;

	s->l1_table = calloc(s->l1_size, sizeof(uint64_t));
	if (!s->l1_table) {
		tcmu_err("Failed to allocate L1 table\n");
		goto fail;
	}
	read = pread(bdev->fd, s->l1_table, s->l1_size * sizeof(uint64_t), s->l1_table_offset);
	if (read != s->l1_size * sizeof(uint64_t)) {
		tcmu_err("Failed to read L1 table\n");
		goto fail;
	}

	s->l2_cache = calloc(L2_CACHE_SIZE, s->l2_size * sizeof(uint64_t));
	if (s->l2_cache == NULL) {
		tcmu_err("Failed to allocate L2 cache\n");
		goto fail;
	}
	tcmu_dbg("s->l2_cache = %p\n", s->l2_cache);

	/* cluster decompression cache */
	s->cluster_cache = calloc(1, s->cluster_size);
	s->cluster_data = calloc(1, s->cluster_size);
	s->cluster_cache_offset = -1;
	if (!s->cluster_cache || !s->cluster_data) {
		tcmu_err("Failed to allocate cluster decompression space\n");
		goto fail;
	}
	tcmu_dbg("s->cluster_cache = %p\n", s->cluster_cache);

	/* refcount table */
	s->refcount_table_offset = header.refcount_table_offset;
	s->refcount_table_size = header.refcount_table_clusters << (s->cluster_bits - 3);

	s->refcount_table = calloc(s->refcount_table_size, sizeof(uint64_t));
	if (!s->refcount_table) {
		tcmu_err("Failed to allocate refcount table\n");
		goto fail;
	}
	read = pread(bdev->fd, s->refcount_table, s->refcount_table_size * sizeof(uint64_t), s->refcount_table_offset);
	if (read != s->refcount_table_size * sizeof(uint64_t)) {
		tcmu_err("Failed to read refcount table\n");
		goto fail;
	}

	s->refcount_order = header.refcount_order;
	s->rc_cache = calloc(RC_CACHE_SIZE, s->cluster_size);
	if (s->rc_cache == NULL) {
		tcmu_err("Failed to allocate refcount cache\n");
		goto fail;
	}
	tcmu_dbg("s->rc_cache = %p\n", s->rc_cache);

	if (qcow2_setup_backing_file(bdev, &header) == -1)
		goto fail;

	s->cluster_compressed = QCOW2_OFLAG_COMPRESSED;
	s->cluster_copied =  QCOW2_OFLAG_COPIED;
	s->cluster_mask = ~(QCOW_OFLAG_COMPRESSED | QCOW2_OFLAG_COPIED | QCOW2_OFLAG_ZERO);

	s->block_alloc = qcow2_block_alloc;
	s->set_refcount = qcow2_set_refcount;
	tcmu_dbg("%d: %s\n", bdev->fd, pathname);
	return 0;
fail:
	close(bdev->fd);
	free(s->cluster_cache);
	free(s->cluster_data);
	free(s->rc_cache);
	free(s->refcount_table);
	free(s->l2_cache);
	free(s->l1_table);
fail_nofd:
	free(s);
	return -1;
}