Commit 65b31cc2 authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'kwolf/for-anthony' into staging

* kwolf/for-anthony:
  AHCI: Masking of IRQs actually masks them
  sheepdog: fix co_recv coroutine context
  AHCI: Fix port reset race
  rewrite QEMU_BUILD_BUG_ON
  qcow2: Keep unknown header extension when rewriting header
  qcow2: Update whole header at once
  vpc: Round up image size during fixed image creation
  vpc: Add support for Fixed Disk type
  iSCSI: add configuration variables for iSCSI
  qemu-io: add write -z option for bdrv_co_write_zeroes
  qed: add .bdrv_co_write_zeroes() support
  qed: replace is_write with flags field
  block: perform zero-detection during copy-on-read
  block: add .bdrv_co_write_zeroes() interface
  cutils: extract buffer_is_zero() from qemu-img.c
parents b2d4b3f7 b8676728
Loading
Loading
Loading
Loading
+58 −9
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@

typedef enum {
    BDRV_REQ_COPY_ON_READ = 0x1,
    BDRV_REQ_ZERO_WRITE   = 0x2,
} BdrvRequestFlags;

static void bdrv_dev_change_media_cb(BlockDriverState *bs, bool load);
@@ -69,7 +70,8 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
    int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
    BdrvRequestFlags flags);
static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
    int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
    int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
    BdrvRequestFlags flags);
static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
                                               int64_t sector_num,
                                               QEMUIOVector *qiov,
@@ -1300,7 +1302,7 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
                                     rwco->nb_sectors, rwco->qiov, 0);
    } else {
        rwco->ret = bdrv_co_do_writev(rwco->bs, rwco->sector_num,
                                      rwco->nb_sectors, rwco->qiov);
                                      rwco->nb_sectors, rwco->qiov, 0);
    }
}

@@ -1515,6 +1517,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
     */
    void *bounce_buffer;

    BlockDriver *drv = bs->drv;
    struct iovec iov;
    QEMUIOVector bounce_qiov;
    int64_t cluster_sector_num;
@@ -1535,14 +1538,21 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
    iov.iov_base = bounce_buffer = qemu_blockalign(bs, iov.iov_len);
    qemu_iovec_init_external(&bounce_qiov, &iov, 1);

    ret = bs->drv->bdrv_co_readv(bs, cluster_sector_num, cluster_nb_sectors,
    ret = drv->bdrv_co_readv(bs, cluster_sector_num, cluster_nb_sectors,
                             &bounce_qiov);
    if (ret < 0) {
        goto err;
    }

    ret = bs->drv->bdrv_co_writev(bs, cluster_sector_num, cluster_nb_sectors,
    if (drv->bdrv_co_write_zeroes &&
        buffer_is_zero(bounce_buffer, iov.iov_len)) {
        ret = drv->bdrv_co_write_zeroes(bs, cluster_sector_num,
                                        cluster_nb_sectors);
    } else {
        ret = drv->bdrv_co_writev(bs, cluster_sector_num, cluster_nb_sectors,
                                  &bounce_qiov);
    }

    if (ret < 0) {
        /* It might be okay to ignore write errors for guest requests.  If this
         * is a deliberate copy-on-read then we don't want to ignore the error.
@@ -1639,11 +1649,37 @@ int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs,
                            BDRV_REQ_COPY_ON_READ);
}

static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
    int64_t sector_num, int nb_sectors)
{
    BlockDriver *drv = bs->drv;
    QEMUIOVector qiov;
    struct iovec iov;
    int ret;

    /* First try the efficient write zeroes operation */
    if (drv->bdrv_co_write_zeroes) {
        return drv->bdrv_co_write_zeroes(bs, sector_num, nb_sectors);
    }

    /* Fall back to bounce buffer if write zeroes is unsupported */
    iov.iov_len  = nb_sectors * BDRV_SECTOR_SIZE;
    iov.iov_base = qemu_blockalign(bs, iov.iov_len);
    memset(iov.iov_base, 0, iov.iov_len);
    qemu_iovec_init_external(&qiov, &iov, 1);

    ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, &qiov);

    qemu_vfree(iov.iov_base);
    return ret;
}

/*
 * Handle a write request in coroutine context
 */
static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
    int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
    int64_t sector_num, int nb_sectors, QEMUIOVector *qiov,
    BdrvRequestFlags flags)
{
    BlockDriver *drv = bs->drv;
    BdrvTrackedRequest req;
@@ -1670,7 +1706,11 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,

    tracked_request_begin(&req, bs, sector_num, nb_sectors, true);

    if (flags & BDRV_REQ_ZERO_WRITE) {
        ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors);
    } else {
        ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov);
    }

    if (bs->dirty_bitmap) {
        set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
@@ -1690,7 +1730,16 @@ int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
{
    trace_bdrv_co_writev(bs, sector_num, nb_sectors);

    return bdrv_co_do_writev(bs, sector_num, nb_sectors, qiov);
    return bdrv_co_do_writev(bs, sector_num, nb_sectors, qiov, 0);
}

int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs,
                                      int64_t sector_num, int nb_sectors)
{
    trace_bdrv_co_write_zeroes(bs, sector_num, nb_sectors);

    return bdrv_co_do_writev(bs, sector_num, nb_sectors, NULL,
                             BDRV_REQ_ZERO_WRITE);
}

/**
@@ -3192,7 +3241,7 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque)
            acb->req.nb_sectors, acb->req.qiov, 0);
    } else {
        acb->req.error = bdrv_co_do_writev(bs, acb->req.sector,
            acb->req.nb_sectors, acb->req.qiov);
            acb->req.nb_sectors, acb->req.qiov, 0);
    }

    acb->bh = qemu_bh_new(bdrv_co_em_bh, acb);
+8 −0
Original line number Diff line number Diff line
@@ -146,6 +146,14 @@ int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs,
    int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
    int nb_sectors, QEMUIOVector *qiov);
/*
 * Efficiently zero a region of the disk image.  Note that this is a regular
 * I/O request like read or write and should have a reasonable size.  This
 * function is not suitable for zeroing the entire image in a single request
 * because it may allocate memory for the entire region.
 */
int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
    int nb_sectors);
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num,
    int nb_sectors, int *pnum);
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
+129 −10
Original line number Diff line number Diff line
@@ -455,6 +455,109 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
    }
}

static int parse_chap(struct iscsi_context *iscsi, const char *target)
{
    QemuOptsList *list;
    QemuOpts *opts;
    const char *user = NULL;
    const char *password = NULL;

    list = qemu_find_opts("iscsi");
    if (!list) {
        return 0;
    }

    opts = qemu_opts_find(list, target);
    if (opts == NULL) {
        opts = QTAILQ_FIRST(&list->head);
        if (!opts) {
            return 0;
        }
    }

    user = qemu_opt_get(opts, "user");
    if (!user) {
        return 0;
    }

    password = qemu_opt_get(opts, "password");
    if (!password) {
        error_report("CHAP username specified but no password was given");
        return -1;
    }

    if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
        error_report("Failed to set initiator username and password");
        return -1;
    }

    return 0;
}

static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
{
    QemuOptsList *list;
    QemuOpts *opts;
    const char *digest = NULL;

    list = qemu_find_opts("iscsi");
    if (!list) {
        return;
    }

    opts = qemu_opts_find(list, target);
    if (opts == NULL) {
        opts = QTAILQ_FIRST(&list->head);
        if (!opts) {
            return;
        }
    }

    digest = qemu_opt_get(opts, "header-digest");
    if (!digest) {
        return;
    }

    if (!strcmp(digest, "CRC32C")) {
        iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
    } else if (!strcmp(digest, "NONE")) {
        iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
    } else if (!strcmp(digest, "CRC32C-NONE")) {
        iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
    } else if (!strcmp(digest, "NONE-CRC32C")) {
        iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
    } else {
        error_report("Invalid header-digest setting : %s", digest);
    }
}

static char *parse_initiator_name(const char *target)
{
    QemuOptsList *list;
    QemuOpts *opts;
    const char *name = NULL;

    list = qemu_find_opts("iscsi");
    if (!list) {
        return g_strdup("iqn.2008-11.org.linux-kvm");
    }

    opts = qemu_opts_find(list, target);
    if (opts == NULL) {
        opts = QTAILQ_FIRST(&list->head);
        if (!opts) {
            return g_strdup("iqn.2008-11.org.linux-kvm");
        }
    }

    name = qemu_opt_get(opts, "initiator-name");
    if (!name) {
        return g_strdup("iqn.2008-11.org.linux-kvm");
    }

    return g_strdup(name);
}

/*
 * We support iscsi url's on the form
 * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@ -465,6 +568,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
    struct iscsi_context *iscsi = NULL;
    struct iscsi_url *iscsi_url = NULL;
    struct IscsiTask task;
    char *initiator_name = NULL;
    int ret;

    if ((BDRV_SECTOR_SIZE % 512) != 0) {
@@ -474,16 +578,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
        return -EINVAL;
    }

    memset(iscsilun, 0, sizeof(IscsiLun));

    /* Should really append the KVM name after the ':' here */
    iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
    if (iscsi == NULL) {
        error_report("iSCSI: Failed to create iSCSI context.");
        ret = -ENOMEM;
        goto failed;
    }

    iscsi_url = iscsi_parse_full_url(iscsi, filename);
    if (iscsi_url == NULL) {
        error_report("Failed to parse URL : %s %s", filename,
@@ -492,6 +586,17 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
        goto failed;
    }

    memset(iscsilun, 0, sizeof(IscsiLun));

    initiator_name = parse_initiator_name(iscsi_url->target);

    iscsi = iscsi_create_context(initiator_name);
    if (iscsi == NULL) {
        error_report("iSCSI: Failed to create iSCSI context.");
        ret = -ENOMEM;
        goto failed;
    }

    if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
        error_report("iSCSI: Failed to set target name.");
        ret = -EINVAL;
@@ -507,6 +612,14 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
            goto failed;
        }
    }

    /* check if we got CHAP username/password via the options */
    if (parse_chap(iscsi, iscsi_url->target) != 0) {
        error_report("iSCSI: Failed to set CHAP user/password");
        ret = -EINVAL;
        goto failed;
    }

    if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
        error_report("iSCSI: Failed to set session type to normal.");
        ret = -EINVAL;
@@ -515,6 +628,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)

    iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);

    /* check if we got HEADER_DIGEST via the options */
    parse_header_digest(iscsi, iscsi_url->target);

    task.iscsilun = iscsilun;
    task.status = 0;
    task.complete = 0;
@@ -548,6 +664,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
    return 0;

failed:
    if (initiator_name != NULL) {
        g_free(initiator_name);
    }
    if (iscsi_url != NULL) {
        iscsi_destroy_url(iscsi_url);
    }
+134 −60
Original line number Diff line number Diff line
@@ -77,8 +77,10 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
                                 uint64_t end_offset)
{
    BDRVQcowState *s = bs->opaque;
    QCowExtension ext;
    uint64_t offset;
    int ret;

#ifdef DEBUG_EXT
    printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset);
@@ -129,8 +131,22 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
            break;

        default:
            /* unknown magic -- just skip it */
            /* unknown magic - save it in case we need to rewrite the header */
            {
                Qcow2UnknownHeaderExtension *uext;

                uext = g_malloc0(sizeof(*uext)  + ext.len);
                uext->magic = ext.magic;
                uext->len = ext.len;
                QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next);

                ret = bdrv_pread(bs->file, offset , uext->data, uext->len);
                if (ret < 0) {
                    return ret;
                }

                offset = ((offset + ext.len + 7) & ~7);
            }
            break;
        }
    }
@@ -138,6 +154,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
    return 0;
}

static void cleanup_unknown_header_ext(BlockDriverState *bs)
{
    BDRVQcowState *s = bs->opaque;
    Qcow2UnknownHeaderExtension *uext, *next;

    QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) {
        QLIST_REMOVE(uext, next);
        g_free(uext);
    }
}

static int qcow2_open(BlockDriverState *bs, int flags)
{
@@ -291,6 +317,7 @@ static int qcow2_open(BlockDriverState *bs, int flags)
    return ret;

 fail:
    cleanup_unknown_header_ext(bs);
    qcow2_free_snapshots(bs);
    qcow2_refcount_close(bs);
    g_free(s->l1_table);
@@ -632,6 +659,7 @@ static void qcow2_close(BlockDriverState *bs)
    qcow2_cache_destroy(bs, s->l2_table_cache);
    qcow2_cache_destroy(bs, s->refcount_block_cache);

    cleanup_unknown_header_ext(bs);
    g_free(s->cluster_cache);
    qemu_vfree(s->cluster_data);
    qcow2_refcount_close(bs);
@@ -669,103 +697,149 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
    }
}

static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
    size_t len, size_t buflen)
{
    QCowExtension *ext_backing_fmt = (QCowExtension*) buf;
    size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7);

    if (buflen < ext_len) {
        return -ENOSPC;
    }

    *ext_backing_fmt = (QCowExtension) {
        .magic  = cpu_to_be32(magic),
        .len    = cpu_to_be32(len),
    };
    memcpy(buf + sizeof(QCowExtension), s, len);

    return ext_len;
}

/*
 * Updates the variable length parts of the qcow2 header, i.e. the backing file
 * name and all extensions. qcow2 was not designed to allow such changes, so if
 * we run out of space (we can only use the first cluster) this function may
 * fail.
 * Updates the qcow2 header, including the variable length parts of it, i.e.
 * the backing file name and all extensions. qcow2 was not designed to allow
 * such changes, so if we run out of space (we can only use the first cluster)
 * this function may fail.
 *
 * Returns 0 on success, -errno in error cases.
 */
static int qcow2_update_ext_header(BlockDriverState *bs,
    const char *backing_file, const char *backing_fmt)
int qcow2_update_header(BlockDriverState *bs)
{
    size_t backing_file_len = 0;
    size_t backing_fmt_len = 0;
    BDRVQcowState *s = bs->opaque;
    QCowExtension ext_backing_fmt = {0, 0};
    QCowHeader *header;
    char *buf;
    size_t buflen = s->cluster_size;
    int ret;
    uint64_t total_size;
    uint32_t refcount_table_clusters;
    Qcow2UnknownHeaderExtension *uext;

    /* Backing file format doesn't make sense without a backing file */
    if (backing_fmt && !backing_file) {
        return -EINVAL;
    }
    buf = qemu_blockalign(bs, buflen);
    memset(buf, 0, s->cluster_size);

    /* Prepare the backing file format extension if needed */
    if (backing_fmt) {
        ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt));
        ext_backing_fmt.magic = cpu_to_be32(QCOW2_EXT_MAGIC_BACKING_FORMAT);
        backing_fmt_len = ((sizeof(ext_backing_fmt)
            + strlen(backing_fmt) + 7) & ~7);
    }
    /* Header structure */
    header = (QCowHeader*) buf;

    /* Check if we can fit the new header into the first cluster */
    if (backing_file) {
        backing_file_len = strlen(backing_file);
    }

    size_t header_size = sizeof(QCowHeader) + backing_file_len
        + backing_fmt_len;

    if (header_size > s->cluster_size) {
        return -ENOSPC;
    if (buflen < sizeof(*header)) {
        ret = -ENOSPC;
        goto fail;
    }

    /* Rewrite backing file name and qcow2 extensions */
    size_t ext_size = header_size - sizeof(QCowHeader);
    uint8_t buf[ext_size];
    size_t offset = 0;
    size_t backing_file_offset = 0;
    total_size = bs->total_sectors * BDRV_SECTOR_SIZE;
    refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);

    *header = (QCowHeader) {
        .magic                  = cpu_to_be32(QCOW_MAGIC),
        .version                = cpu_to_be32(QCOW_VERSION),
        .backing_file_offset    = 0,
        .backing_file_size      = 0,
        .cluster_bits           = cpu_to_be32(s->cluster_bits),
        .size                   = cpu_to_be64(total_size),
        .crypt_method           = cpu_to_be32(s->crypt_method_header),
        .l1_size                = cpu_to_be32(s->l1_size),
        .l1_table_offset        = cpu_to_be64(s->l1_table_offset),
        .refcount_table_offset  = cpu_to_be64(s->refcount_table_offset),
        .refcount_table_clusters = cpu_to_be32(refcount_table_clusters),
        .nb_snapshots           = cpu_to_be32(s->nb_snapshots),
        .snapshots_offset       = cpu_to_be64(s->snapshots_offset),
    };

    if (backing_file) {
        if (backing_fmt) {
            int padding = backing_fmt_len -
                (sizeof(ext_backing_fmt) + strlen(backing_fmt));
    buf += sizeof(*header);
    buflen -= sizeof(*header);

            memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt));
            offset += sizeof(ext_backing_fmt);
    /* Backing file format header extension */
    if (*bs->backing_format) {
        ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT,
                             bs->backing_format, strlen(bs->backing_format),
                             buflen);
        if (ret < 0) {
            goto fail;
        }

            memcpy(buf + offset, backing_fmt, strlen(backing_fmt));
            offset += strlen(backing_fmt);
        buf += ret;
        buflen -= ret;
    }

            memset(buf + offset, 0, padding);
            offset += padding;
    /* Keep unknown header extensions */
    QLIST_FOREACH(uext, &s->unknown_header_ext, next) {
        ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen);
        if (ret < 0) {
            goto fail;
        }

        memcpy(buf + offset, backing_file, backing_file_len);
        backing_file_offset = sizeof(QCowHeader) + offset;
        buf += ret;
        buflen -= ret;
    }

    ret = bdrv_pwrite_sync(bs->file, sizeof(QCowHeader), buf, ext_size);
    /* End of header extensions */
    ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen);
    if (ret < 0) {
        goto fail;
    }

    /* Update header fields */
    uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset);
    uint32_t be_backing_file_size = cpu_to_be32(backing_file_len);
    buf += ret;
    buflen -= ret;

    ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_offset),
        &be_backing_file_offset, sizeof(uint64_t));
    if (ret < 0) {
    /* Backing file name */
    if (*bs->backing_file) {
        size_t backing_file_len = strlen(bs->backing_file);

        if (buflen < backing_file_len) {
            ret = -ENOSPC;
            goto fail;
        }

    ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_size),
        &be_backing_file_size, sizeof(uint32_t));
        strncpy(buf, bs->backing_file, buflen);

        header->backing_file_offset = cpu_to_be64(buf - ((char*) header));
        header->backing_file_size   = cpu_to_be32(backing_file_len);
    }

    /* Write the new header */
    ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size);
    if (ret < 0) {
        goto fail;
    }

    ret = 0;
fail:
    qemu_vfree(header);
    return ret;
}

static int qcow2_change_backing_file(BlockDriverState *bs,
    const char *backing_file, const char *backing_fmt)
{
    return qcow2_update_ext_header(bs, backing_file, backing_fmt);
    /* Backing file format doesn't make sense without a backing file */
    if (backing_fmt && !backing_file) {
        return -EINVAL;
    }

    pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
    pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");

    return qcow2_update_header(bs);
}

static int preallocate(BlockDriverState *bs)
+9 −0
Original line number Diff line number Diff line
@@ -87,6 +87,13 @@ typedef struct QCowSnapshot {
struct Qcow2Cache;
typedef struct Qcow2Cache Qcow2Cache;

typedef struct Qcow2UnknownHeaderExtension {
    uint32_t magic;
    uint32_t len;
    QLIST_ENTRY(Qcow2UnknownHeaderExtension) next;
    uint8_t data[];
} Qcow2UnknownHeaderExtension;

typedef struct BDRVQcowState {
    int cluster_bits;
    int cluster_size;
@@ -127,6 +134,7 @@ typedef struct BDRVQcowState {
    QCowSnapshot *snapshots;

    int flags;
    QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext;
} BDRVQcowState;

/* XXX: use std qcow open function ? */
@@ -178,6 +186,7 @@ static inline int64_t align_offset(int64_t offset, int n)
/* qcow2.c functions */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
                  int64_t sector_num, int nb_sectors);
int qcow2_update_header(BlockDriverState *bs);

/* qcow2-refcount.c functions */
int qcow2_refcount_init(BlockDriverState *bs);
Loading