diff mbox

[V2,2/3,RFC] libqblock-API design

Message ID 1344507137-11173-1-git-send-email-xiawenc@linux.vnet.ibm.com
State New
Headers show

Commit Message

Wayne Xia Aug. 9, 2012, 10:12 a.m. UTC
This patch is the API design.

Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
---
 libqblock.c |  670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libqblock.h |  447 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 1117 insertions(+), 0 deletions(-)
 create mode 100644 libqblock.c
 create mode 100644 libqblock.h

Comments

Blue Swirl Aug. 9, 2012, 5:36 p.m. UTC | #1
On Thu, Aug 9, 2012 at 10:12 AM, Wenchao Xia <xiawenc@linux.vnet.ibm.com> wrote:
>   This patch is the API design.
>
> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> ---
>  libqblock.c |  670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  libqblock.h |  447 +++++++++++++++++++++++++++++++++++++++
>  2 files changed, 1117 insertions(+), 0 deletions(-)
>  create mode 100644 libqblock.c
>  create mode 100644 libqblock.h
>
> diff --git a/libqblock.c b/libqblock.c
> new file mode 100644
> index 0000000..52e46dc
> --- /dev/null
> +++ b/libqblock.c
> @@ -0,0 +1,670 @@
> +/*
> + * Copyright IBM Corp. 2012
> + *
> + * Authors:
> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

Current address of FSF is:

51 Franklin Street, Fifth Floor
Boston, MA 02110-1301
USA

However, please use the recommended web version like other files.

> + */
> +
> +#include "libqblock.h"
> +
> +#include <unistd.h>
> +#include "block.h"
> +#include "block_int.h"
> +
> +#define FUNC_FREE free
> +#define FUNC_MALLOC malloc
> +#define FUNC_CALLOC calloc
> +
> +#define CLEAN_FREE(p) { \
> +        FUNC_FREE(p); \
> +        (p) = NULL; \
> +}

I don't think this is enough since block functions may use glib memory
allocation. Probably the block files have to be compiled with wrappers
for g_malloc and friends.

> +
> +/* details should be hidden to user */
> +struct QBlockState {
> +    BlockDriverState *bdrvs;
> +    char *filename;
> +    const char *err_msg;
> +    int err_no;
> +};
> +
> +static void set_err_nomem(struct QBlockState *qbs)
> +{
> +    qbs->err_msg = NULL;
> +    qbs->err_no = 0;
> +}
> +
> +void libqblock_init(void)
> +{
> +    bdrv_init();
> +    return;

Useless return.

> +}
> +
> +int qb_state_new(struct QBlockState **qbs)
> +{
> +    *qbs = FUNC_CALLOC(1, sizeof(struct QBlockState));
> +    if (*qbs == NULL) {
> +        return QB_ERR_MEM_ERR;
> +    }
> +    (*qbs)->bdrvs = bdrv_new("hda");
> +    if ((*qbs)->bdrvs == NULL) {
> +        CLEAN_FREE(*qbs);
> +        return QB_ERR_INTERNAL_ERR;
> +    }
> +    return 0;
> +}
> +
> +void qb_state_free(struct QBlockState **qbs)
> +{
> +    CLEAN_FREE((*qbs)->filename);
> +    if ((*qbs)->bdrvs == NULL) {
> +        bdrv_delete((*qbs)->bdrvs);
> +        (*qbs)->bdrvs = NULL;
> +    }
> +    CLEAN_FREE(*qbs);
> +    return;

Remove.

> +}
> +
> +static const char *fmt2str(enum QBlockFormat fmt)
> +{
> +    const char *ret = NULL;
> +    switch (fmt) {
> +    case QB_FMT_COW:
> +        ret = "cow";
> +        break;
> +    case QB_FMT_QED:
> +        ret = "qed";
> +        break;
> +    case QB_FMT_QCOW:
> +        ret = "qcow";
> +        break;
> +    case QB_FMT_QCOW2:
> +        ret = "qcow2";
> +        break;
> +    case QB_FMT_RAW:
> +        ret = "raw";
> +        break;
> +    case QB_FMT_RBD:
> +        ret = "rbd";
> +        break;
> +    case QB_FMT_SHEEPDOG:
> +        ret = "sheepdog";
> +        break;
> +    case QB_FMT_VDI:
> +        ret = "vdi";
> +        break;
> +    case QB_FMT_VMDK:
> +        ret = "vmdk";
> +        break;
> +    case QB_FMT_VPC:
> +        ret = "vpc";
> +        break;
> +    default:
> +        break;
> +    }
> +    return ret;
> +}
> +
> +static enum QBlockFormat str2fmt(const char *fmt)
> +{
> +    enum QBlockFormat ret = QB_FMT_NONE;
> +    if (0 == strcmp(fmt, "cow")) {

Please use natural order:
strcmp(fmt, "cow") == 0

> +        ret = QB_FMT_COW;
> +    } else if (0 == strcmp(fmt, "qed")) {
> +        ret = QB_FMT_QED;
> +    } else if (0 == strcmp(fmt, "qcow")) {
> +        ret = QB_FMT_QCOW;
> +    } else if (0 == strcmp(fmt, "qcow2")) {
> +        ret = QB_FMT_QCOW2;
> +    } else if (0 == strcmp(fmt, "raw")) {
> +        ret = QB_FMT_RAW;
> +    } else if (0 == strcmp(fmt, "rbd")) {
> +        ret = QB_FMT_RBD;
> +    } else if (0 == strcmp(fmt, "sheepdog")) {
> +        ret = QB_FMT_SHEEPDOG;
> +    } else if (0 == strcmp(fmt, "vdi")) {
> +        ret = QB_FMT_VDI;
> +    } else if (0 == strcmp(fmt, "vmdk")) {
> +        ret = QB_FMT_VMDK;
> +    } else if (0 == strcmp(fmt, "vpc")) {
> +        ret = QB_FMT_VPC;
> +    }
> +    return ret;
> +}
> +
> +/* copy information and take care the member difference in differect version.
> +   Assuming all new member are added in the tail, struct size is the first
> +   member, this is old to new version, src have its struct_size set. */
> +static void qboo_adjust_o2n(struct QBlockOptionOpen *dest,
> +                            struct QBlockOptionOpen *src)
> +{
> +    /* for simple it does memcpy now, need to take care of embbed structure */
> +    memcpy(dest, src, src->struct_size);

Since later there will be a field by field copy, I'd use:
*dest = *src;

> +}
> +
> +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op)
> +{
> +    int ret = 0, bd_ret;
> +    BlockDriverState *bs;
> +    struct QBlockOptionOpen qboo;
> +    BlockDriver *bd;
> +    const char *fmt;
> +
> +    /* take care of user settings */
> +    qboo_adjust_o2n(&qboo, op);
> +
> +    /* check parameters */
> +    if (qboo.o_loc.filename == NULL) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "filename was not set.";
> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +
> +    if (path_has_protocol(qboo.o_loc.filename) > 0) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "filename have protocol.";

had

> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +
> +    if (qboo.o_loc.protocol >= QB_PROTO_MAX) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "protocol was not supported.";
> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +
> +    bd = NULL;
> +    fmt = fmt2str(qboo.o_fmt_type);
> +    if (fmt != NULL) {
> +        bd = bdrv_find_format(fmt);
> +        assert(bd != NULL);
> +    }

What should happen in the NULL fmt case, goto out?

> +
> +
> +    /* do real openning */
> +    bs = qbs->bdrvs;
> +    bd_ret = bdrv_open(bs, qboo.o_loc.filename, qboo.o_flag, bd);
> +    if (bd_ret < 0) {
> +        ret = QB_ERR_INTERNAL_ERR;
> +        qbs->err_msg = "failed to open the file.";
> +        qbs->err_no = -errno;
> +        goto out;
> +    }
> +
> +    if (qbs->filename != NULL) {
> +        FUNC_FREE(qbs->filename);
> +    }
> +    qbs->filename = strdup(qboo.o_loc.filename);
> +    if (qbs->filename == NULL) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        set_err_nomem(qbs);
> +        goto out;
> +    }
> +
> + out:
> +    return ret;
> +}
> +
> +void qb_close(struct QBlockState *qbs)
> +{
> +    BlockDriverState *bs;
> +
> +    bs = qbs->bdrvs;
> +    bdrv_close(bs);
> +    return;

Delete return

> +}
> +
> +/* copy information and take care the member difference in differect version.
> +   Assuming all new member are added in the tail, struct size is the first
> +   member, this is old to new version, src have its struct_size set. */
> +static void qboc_adjust_o2n(struct QBlockOptionCreate *dest,
> +                            struct QBlockOptionCreate *src)
> +{
> +    /* for simple it does memcpy now, need to take care of embbed structure */
> +    memcpy(dest, src, src->struct_size);
> +}
> +
> +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op)
> +{
> +    int ret = 0, bd_ret;
> +    BlockDriverState *bs = NULL;
> +    BlockDriver *drv = NULL, *backing_drv = NULL;
> +    struct QBlockOptionCreate qbco;
> +    const char *fmt = NULL, *fmt_backing_str = NULL, *tmp;
> +    QEMUOptionParameter *param = NULL, *create_options = NULL;
> +    QEMUOptionParameter *backing_fmt, *backing_file, *size;
> +    struct QBlockOption_cow *o_cow = NULL;
> +    struct QBlockOption_qed *o_qed = NULL;
> +    struct QBlockOption_qcow *o_qcow = NULL;
> +    struct QBlockOption_qcow2 *o_qcow2 = NULL;
> +    struct QBlockOption_raw *o_raw = NULL;
> +    struct QBlockOption_rbd *o_rbd = NULL;
> +    struct QBlockOption_sheepdog *o_sd = NULL;
> +    struct QBlockOption_vdi *o_vdi = NULL;
> +    struct QBlockOption_vmdk *o_vmdk = NULL;
> +    struct QBlockOption_vpc *o_vpc = NULL;
> +    int backing_flag = 0;
> +
> +    /* take care of user settings */
> +    qboc_adjust_o2n(&qbco, op);
> +
> +    /* check parameters */
> +    if (qbco.o_loc.filename == NULL) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "filename was not set.";
> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +
> +    if (path_has_protocol(qbco.o_loc.filename) > 0) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "filename have protocol.";
> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +
> +    if (qbco.o_loc.protocol >= QB_PROTO_MAX) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "protocol was not supported.";
> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +
> +    /* set parameters */
> +    fmt = fmt2str(qbco.o_fmt.fmt_type);
> +    if (fmt == NULL) {
> +        ret = QB_ERR_INVALID_PARAM;
> +        qbs->err_msg = "format is not valid.";
> +        qbs->err_no = 0;
> +        goto out;
> +    }
> +    drv = bdrv_find_format(fmt);
> +    assert(drv != NULL);
> +
> +    create_options = append_option_parameters(create_options,
> +                                              drv->create_options);
> +    param = parse_option_parameters("", create_options, param);
> +
> +    switch (qbco.o_fmt.fmt_type) {
> +    case QB_FMT_COW:
> +        o_cow = &qbco.o_fmt.fmt_op.o_cow;
> +        assert(0 == set_option_parameter_int(param,
> +                                BLOCK_OPT_SIZE, o_cow->virt_size));

expr == 0, also below

> +        if (o_cow->backing_file) {
> +            assert(0 == set_option_parameter(param,
> +                                BLOCK_OPT_BACKING_FILE, o_cow->backing_file));
> +        }
> +        backing_flag = o_cow->backing_flag;
> +        break;
> +    case QB_FMT_QED:
> +        o_qed = &qbco.o_fmt.fmt_op.o_qed;
> +        assert(0 == set_option_parameter_int(param,
> +                                BLOCK_OPT_SIZE, o_qed->virt_size));
> +        if (o_qed->backing_file) {
> +            assert(0 == set_option_parameter(param,
> +                                BLOCK_OPT_BACKING_FILE, o_qed->backing_file));
> +        }
> +        fmt_backing_str = fmt2str(o_qed->backing_fmt);
> +        if (fmt_backing_str) {
> +            assert(0 == set_option_parameter(param,
> +                                BLOCK_OPT_BACKING_FMT, fmt_backing_str));
> +        }
> +        assert(0 == set_option_parameter_int(param,
> +                                BLOCK_OPT_CLUSTER_SIZE, o_qed->cluster_size));
> +        assert(0 == set_option_parameter_int(param,
> +                                BLOCK_OPT_TABLE_SIZE, o_qed->table_size));
> +        backing_flag = o_qed->backing_flag;
> +        break;
> +    case QB_FMT_QCOW:
> +        o_qcow = &qbco.o_fmt.fmt_op.o_qcow;
> +        assert(0 == set_option_parameter_int(param,
> +                                BLOCK_OPT_SIZE, o_qcow->virt_size));
> +        if (o_qcow->backing_file) {
> +            assert(0 == set_option_parameter(param,
> +                                BLOCK_OPT_BACKING_FILE, o_qcow->backing_file));
> +        }
> +        tmp = o_qcow->encrypt ? "on" : "off";
> +        assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp));
> +        backing_flag = o_qcow->backing_flag;
> +        break;
> +    case QB_FMT_QCOW2:
> +        o_qcow2 = &qbco.o_fmt.fmt_op.o_qcow2;
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_SIZE, o_qcow2->virt_size));
> +        if (o_qcow2->backing_file) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_BACKING_FILE, o_qcow2->backing_file));
> +        }
> +        fmt_backing_str = fmt2str(o_qcow2->backing_fmt);
> +        if (fmt_backing_str) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_BACKING_FMT, fmt_backing_str));
> +        }
> +        if (o_qcow2->compat_level) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_COMPAT_LEVEL, o_qcow2->compat_level));
> +        }
> +        tmp = o_qcow2->encrypt ? "on" : "off";
> +        assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp));
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_CLUSTER_SIZE, o_qcow2->cluster_size));
> +        if (o_qcow2->prealloc_mode) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_PREALLOC, o_qcow2->prealloc_mode));
> +        }
> +        backing_flag = o_qcow2->backing_flag;
> +        break;
> +    case QB_FMT_RAW:
> +        o_raw = &qbco.o_fmt.fmt_op.o_raw;
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_SIZE, o_raw->virt_size));
> +        break;
> +    case QB_FMT_RBD:
> +        o_rbd = &qbco.o_fmt.fmt_op.o_rbd;
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_SIZE, o_rbd->virt_size));
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_CLUSTER_SIZE, o_rbd->cluster_size));
> +        break;
> +    case QB_FMT_SHEEPDOG:
> +        o_sd = &qbco.o_fmt.fmt_op.o_sheepdog;
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_SIZE, o_sd->virt_size));
> +        if (o_sd->backing_file) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_BACKING_FILE, o_sd->backing_file));
> +        }
> +        if (o_sd->prealloc_mode) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_PREALLOC, o_sd->prealloc_mode));
> +        }
> +        backing_flag = o_sd->backing_flag;
> +        break;
> +    case QB_FMT_VDI:
> +        o_vdi = &qbco.o_fmt.fmt_op.o_vdi;
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_SIZE, o_vdi->virt_size));
> +        /* following option is not always valid depends on configuration */
> +        set_option_parameter_int(param,
> +                              BLOCK_OPT_CLUSTER_SIZE, o_vdi->cluster_size);
> +        set_option_parameter_int(param, "static", o_vdi->prealloc_mode);
> +        break;
> +    case QB_FMT_VMDK:
> +        o_vmdk = &qbco.o_fmt.fmt_op.o_vmdk;
> +        assert(0 == set_option_parameter_int(param,
> +                              BLOCK_OPT_SIZE, o_vmdk->virt_size));
> +        if (o_vmdk->backing_file) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_BACKING_FILE, o_vmdk->backing_file));
> +        }
> +        tmp = o_vmdk->compat_version6 ? "on" : "off";
> +        assert(0 == set_option_parameter(param, BLOCK_OPT_COMPAT6, tmp));
> +        if (o_vmdk->subfmt) {
> +            assert(0 == set_option_parameter(param,
> +                              BLOCK_OPT_SUBFMT, o_vmdk->subfmt));
> +        }
> +        backing_flag = o_vmdk->backing_flag;
> +        break;
> +    case QB_FMT_VPC:
> +        o_vpc = &qbco.o_fmt.fmt_op.o_vpc;
> +        assert(0 == set_option_parameter_int(param,
> +                               BLOCK_OPT_SIZE, o_vpc->virt_size));
> +        if (o_vpc->subfmt) {
> +            assert(0 == set_option_parameter(param,
> +                               BLOCK_OPT_SUBFMT, o_vpc->subfmt));
> +        }
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
> +    if (backing_file && backing_file->value.s) {
> +        if (!strcmp(op->o_loc.filename, backing_file->value.s)) {
> +            ret = QB_ERR_INVALID_PARAM;
> +            qbs->err_msg = "backing file is the same with new file.";

same as the new file

> +            qbs->err_no = 0;
> +            goto out;
> +        }
> +    }
> +
> +    backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
> +    if (backing_fmt && backing_fmt->value.s) {
> +        backing_drv = bdrv_find_format(backing_fmt->value.s);
> +        assert(backing_drv != NULL);
> +    }
> +
> +    size = get_option_parameter(param, BLOCK_OPT_SIZE);
> +    if (size && size->value.n <= 0) {
> +        if (backing_file && backing_file->value.s) {
> +            uint64_t size;
> +            char buf[32];
> +            int back_flags;
> +
> +            /* backing files always opened read-only */
> +            back_flags =
> +                backing_flag &
> +                ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
> +
> +            bs = bdrv_new("");
> +
> +            ret = bdrv_open(bs, backing_file->value.s,
> +                                back_flags, backing_drv);
> +            if (ret < 0) {
> +                ret = QB_ERR_INTERNAL_ERR;
> +                qbs->err_msg = "failed to open the backing file.";
> +                qbs->err_no = -errno;
> +                goto out;
> +            }
> +            bdrv_get_geometry(bs, &size);
> +            size *= 512;

Where's the #define?

> +
> +            snprintf(buf, sizeof(buf), "%" PRId64, size);
> +            set_option_parameter(param, BLOCK_OPT_SIZE, buf);
> +        } else {
> +            ret = QB_ERR_INVALID_PARAM;
> +            qbs->err_msg = "neither size or backing file was not set.";
> +            qbs->err_no = 0;
> +            goto out;
> +        }
> +    }
> +
> +    bd_ret = bdrv_create(drv, op->o_loc.filename, param);
> +
> +    if (bd_ret < 0) {
> +        ret = QB_ERR_INTERNAL_ERR;
> +        qbs->err_no = bd_ret;
> +        if (bd_ret == -ENOTSUP) {
> +            qbs->err_msg = "formatting option not supported.";

s/f/F/

> +        } else if (bd_ret == -EFBIG) {
> +            qbs->err_msg = "The image size is too large.";
> +        } else {
> +            qbs->err_msg = "Error in creating the image.";
> +        }
> +        goto out;
> +    }
> +
> +out:
> +    free_option_parameters(create_options);
> +    free_option_parameters(param);
> +
> +    if (bs) {
> +        bdrv_delete(bs);
> +    }
> +
> +    return ret;
> +}
> +
> +/* ignore case that len is not alligned to 512 now. */

aligned

> +int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
> +                                                      off_t offset)
> +{
> +    int ret = 0, bd_ret;
> +    BlockDriverState *bs;
> +
> +    bs = qbs->bdrvs;
> +
> +    assert((len & 0x01ff) == 0);
> +    assert((offset & 0x01ff) == 0);

Also here use the #defined constant.

> +    bd_ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, len >> 9);
> +    if (bd_ret < 0) {
> +        ret = QB_ERR_INTERNAL_ERR;
> +        qbs->err_no = bd_ret;
> +        if (bd_ret == -EIO) {
> +            qbs->err_msg = "Generic I/O error.";
> +        } else if (bd_ret == -ENOMEDIUM) {
> +            qbs->err_msg = "No meida inserted.";

media

> +        } else if (bd_ret == -EINVAL) {
> +            qbs->err_msg = "Sector was not correct.";
> +        } else {
> +            qbs->err_msg = "Internal error.";
> +        }
> +    }
> +    return ret;
> +}
> +
> +/* ignore case that len is not alligned to 512 now. */
> +int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
> +                                                       off_t offset)
> +{
> +    int ret = 0, bd_ret;
> +    BlockDriverState *bs;
> +
> +    bs = qbs->bdrvs;
> +
> +    assert((len & 0x01ff) == 0);
> +    assert((offset & 0x01ff) == 0);
> +    bd_ret = bdrv_write(bs, offset >> 9, buf, len >> 9);
> +    if (bd_ret < 0) {
> +        ret = QB_ERR_INTERNAL_ERR;
> +        qbs->err_no = bd_ret;
> +        if (bd_ret == -EIO) {
> +            qbs->err_msg = "Generic I/O error.";
> +        } else if (bd_ret == -ENOMEDIUM) {
> +            qbs->err_msg = "No meida inserted.";
> +        } else if (bd_ret == -EINVAL) {
> +            qbs->err_msg = "Sector was not correct.";
> +        } else if (bd_ret == -EACCES) {
> +            qbs->err_msg = "Ready only device.";
> +        } else {
> +            qbs->err_msg = "Internal error.";
> +        }
> +    }
> +    return ret;
> +}
> +
> +int qb_flush(struct QBlockState *qbs)
> +{
> +    int ret = 0, bd_ret;
> +    BlockDriverState *bs;
> +
> +    bs = qbs->bdrvs;
> +    bd_ret = bdrv_flush(bs);
> +    if (bd_ret < 0) {
> +        ret = QB_ERR_INTERNAL_ERR;
> +        qbs->err_no = bd_ret;
> +        qbs->err_msg = NULL;
> +    }
> +    return ret;
> +}
> +
> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info)
> +{
> +    int ret = 0;
> +    BlockDriverState *bs;
> +    const char *tmp;
> +    uint64_t total_sectors;
> +    char backing_filename[1024];
> +
> +    struct QBlockInfoImage *ifimg = FUNC_CALLOC(1,
> +                                           sizeof(struct QBlockInfoImage));
> +    if (ifimg == NULL) {
> +        ret = QB_ERR_MEM_ERR;
> +        set_err_nomem(qbs);
> +        goto out;
> +    }
> +
> +    bs = qbs->bdrvs;
> +    ifimg->filename = strdup(qbs->filename);
> +    if (ifimg->filename == NULL) {
> +        ret = QB_ERR_MEM_ERR;
> +        set_err_nomem(qbs);
> +        goto err;
> +    }
> +    tmp = bdrv_get_format_name(bs);
> +    ifimg->format = str2fmt(tmp);
> +    /* support only file now */
> +    ifimg->protocol = QB_PROTO_FILE;
> +
> +    bdrv_get_geometry(bs, &total_sectors);
> +    ifimg->virt_size = total_sectors * 512;
> +    ifimg->allocated_size = bdrv_get_allocated_file_size(bs);
> +    ifimg->encrypt = bdrv_is_encrypted(bs);
> +    bdrv_get_full_backing_filename(bs, backing_filename,
> +                                   sizeof(backing_filename));
> +    if (backing_filename[0] != '\0') {
> +        ifimg->backing_filename = strdup(backing_filename);
> +        if (ifimg->backing_filename == NULL) {
> +            ret = QB_ERR_MEM_ERR;
> +            set_err_nomem(qbs);
> +            goto err;
> +        }
> +    }
> +
> +    *info = ifimg;
> + out:
> +    return ret;
> + err:
> +    qb_infoimage_free(&ifimg);
> +    return ret;
> +}
> +
> +void qb_infoimage_free(struct QBlockInfoImage **info)
> +{
> +    if (*info == NULL) {
> +        return;
> +    }
> +    FUNC_FREE((*info)->filename);
> +    FUNC_FREE((*info)->backing_filename);
> +    FUNC_FREE(*info);
> +    *info = NULL;
> +    return;
> +}
> +
> +bool qb_supports_format(enum QBlockFormat fmt)
> +{
> +    if (fmt < QB_FMT_MAX) {
> +        return true;
> +    }
> +    return false;
> +}
> +
> +bool qb_supports_protocol(enum QBlockProtocol proto)
> +{
> +    if (proto < QB_PROTO_MAX) {
> +        return true;
> +    }
> +    return false;
> +}
> +
> +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num)
> +{
> +    *err_num = qbs->err_no;
> +    return qbs->err_msg;
> +}
> diff --git a/libqblock.h b/libqblock.h
> new file mode 100644
> index 0000000..d2e9502
> --- /dev/null
> +++ b/libqblock.h
> @@ -0,0 +1,447 @@
> +/*
> + * Copyright IBM Corp. 2012
> + *
> + * Authors:
> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

web version

> + */
> +
> +#ifndef LIBQBLOCK_H
> +#define LIBQBLOCK_H
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#define bool _Bool

#include <stdbool.h>

> +
> +#define QB_ERR_MEM_ERR (-1)
> +#define QB_ERR_INTERNAL_ERR (-2)
> +#define QB_ERR_INVALID_PARAM (-3)
> +
> +/* this library is designed around this core struct. */
> +struct QBlockState;
> +
> +/*
> +   libarary init

library

> +   This function get the library ready to use.
> + */

Please use markup for the comments, see for example memory.h.

> +void libqblock_init(void);
> +
> +/*
> +   create a new qbs object
> +   params:
> +     qbs: out, pointer that will receive created obj.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +int qb_state_new(struct QBlockState **qbs);
> +
> +/*
> +   delete a qbs object
> +   params:
> +     qbs: in, pointer that will be freed. *qbs will be set to NULL.
> +   return:
> +     void.
> + */
> +void qb_state_free(struct QBlockState **qbs);
> +
> +
> +/* flag used in open and create */
> +#define LIBQBLOCK_O_RDWR        0x0002
> +/* open the file read only and save writes in a snapshot */
> +#define LIBQBLOCK_O_SNAPSHOT    0x0008
> +/* do not use the host page cache */
> +#define LIBQBLOCK_O_NOCACHE     0x0020
> +/* use write-back caching */
> +#define LIBQBLOCK_O_CACHE_WB    0x0040
> +/* use native AIO instead of the thread pool */
> +#define LIBQBLOCK_O_NATIVE_AIO  0x0080
> +/* don't open the backing file */
> +#define LIBQBLOCK_O_NO_BACKING  0x0100
> +/* disable flushing on this disk */
> +#define LIBQBLOCK_O_NO_FLUSH    0x0200
> +/* copy read backing sectors into image */
> +#define LIBQBLOCK_O_COPY_ON_READ 0x0400
> +/* consistency hint for incoming migration */
> +#define LIBQBLOCK_O_INCOMING    0x0800
> +
> +#define LIBQBLOCK_O_CACHE_MASK \
> +   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
> +
> +enum QBlockProtocol {
> +    QB_PROTO_FILE = 0,
> +    QB_PROTO_MAX
> +};
> +
> +enum QBlockFormat {
> +    QB_FMT_NONE = 0,
> +    QB_FMT_COW,
> +    QB_FMT_QED,
> +    QB_FMT_QCOW,
> +    QB_FMT_QCOW2,
> +    QB_FMT_RAW,
> +    QB_FMT_RBD,
> +    QB_FMT_SHEEPDOG,
> +    QB_FMT_VDI,
> +    QB_FMT_VMDK,
> +    QB_FMT_VPC,
> +    QB_FMT_MAX
> +};
> +
> +/* block target location info, it include all information about how to find
> +   the image */
> +struct QBlockLocInfo {
> +    int struct_size;
> +    const char *filename;
> +    enum QBlockProtocol protocol;
> +};
> +
> +/* how to open the image */
> +struct QBlockOptionOpen {
> +    int struct_size;
> +    struct QBlockLocInfo o_loc; /* how to find */
> +    enum QBlockFormat o_fmt_type; /* how to extract */
> +    int o_flag; /* how to control */
> +};
> +
> +/*
> +   create a new QBlockOptionOpen structure.
> +   params:
> +     op: out, pointer that will receive created structure.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +static inline int qb_oo_new(struct QBlockOptionOpen **op)
> +{
> +    *op = calloc(1, sizeof(struct QBlockOptionOpen));

calloc() is not wrapped by your macros.

> +    if (*op == NULL) {
> +        return QB_ERR_MEM_ERR;
> +    }
> +    (*op)->struct_size = sizeof(struct QBlockOptionOpen);
> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
> +    return 0;
> +}
> +
> +/*
> +   free QBlockOptionOpen structure.
> +   params:
> +     op: in, *op will be set as NULL after called.
> +   return:
> +     void
> + */
> +static inline void qb_oo_free(struct QBlockOptionOpen **op)
> +{
> +    free(*op);

Also free() here.

> +    *op = NULL;
> +}
> +
> +/*
> +   open a block object.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     op: in, options for open.
> +   return:
> +     0 on success, negative on fail.
> + */
> +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op);
> +
> +/*
> +   close a block object.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +   return:
> +     void.
> + */
> +void qb_close(struct QBlockState *qbs);
> +
> +/* format related options, struct_size must be set. */
> +struct QBlockOption_cow {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +};
> +
> +struct QBlockOption_qed {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    enum QBlockFormat backing_fmt;
> +    int backing_flag;
> +    size_t cluster_size; /* unit is bytes */
> +    size_t table_size; /* unit is clusters */
> +};
> +
> +struct QBlockOption_qcow {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    bool encrypt;
> +};
> +
> +struct QBlockOption_qcow2 {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *compat_level; /* "Compatibility level (0.10 or 1.1)" */
> +    const char *backing_file;
> +    enum QBlockFormat backing_fmt;
> +    int backing_flag;
> +    bool encrypt;
> +    size_t cluster_size; /* unit is bytes */
> +    const char *prealloc_mode; /* off or metadata */
> +};
> +
> +struct QBlockOption_raw {
> +    int struct_size;
> +    size_t virt_size;
> +};
> +
> +struct QBlockOption_rbd {
> +    int struct_size;
> +    size_t virt_size;
> +    size_t cluster_size;
> +};
> +
> +struct QBlockOption_sheepdog {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    const char *prealloc_mode; /* off or full */
> +};
> +
> +struct QBlockOption_vdi {
> +    int struct_size;
> +    size_t virt_size;
> +    size_t cluster_size;
> +    bool prealloc_mode;
> +};
> +
> +struct QBlockOption_vmdk {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    bool compat_version6;
> +    const char *subfmt;
> +    /* vmdk flat extent format, values:
> +   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
> +    twoGbMaxExtentFlat | streamOptimized} */
> +};
> +
> +struct QBlockOption_vpc {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *subfmt; /* "{dynamic (default) | fixed} " */
> +};

I'd rather have different open functions than variant structures.

> +
> +union QBlockOption_fmt {
> +    struct QBlockOption_cow       o_cow;
> +    struct QBlockOption_qed       o_qed;
> +    struct QBlockOption_qcow      o_qcow;
> +    struct QBlockOption_qcow2     o_qcow2;
> +    struct QBlockOption_raw       o_raw;
> +    struct QBlockOption_rbd       o_rbd;
> +    struct QBlockOption_sheepdog  o_sheepdog;
> +    struct QBlockOption_vdi       o_vdi;
> +    struct QBlockOption_vmdk      o_vmdk;
> +    struct QBlockOption_vpc       o_vpc;
> +};
> +
> +struct QBlockOptionFormat {
> +    int struct_size;
> +    enum QBlockFormat fmt_type;
> +    union QBlockOption_fmt fmt_op;
> +};
> +
> +/* struct_size in o_loc and o_fmt must set. To make this structure extensible,
> +   all new member must be added in the tail of each structure. */
> +struct QBlockOptionCreate {
> +    int struct_size;
> +    struct QBlockLocInfo o_loc;
> +    struct QBlockOptionFormat o_fmt;
> +};
> +
> +/*
> +   create a new QBlockOptionCreate structure.
> +   params:
> +     op: out, pointer that will receive created structure.
> +     fmt: format want to use.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +static inline int qb_oc_new(struct QBlockOptionCreate **op,
> +                            enum QBlockFormat fmt)
> +{
> +    *op = calloc(1, sizeof(struct QBlockOptionCreate));
> +    if (*op == NULL) {
> +        return QB_ERR_MEM_ERR;
> +    }
> +    (*op)->struct_size = sizeof(struct QBlockOptionCreate);
> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
> +    (*op)->o_fmt.struct_size = sizeof(struct QBlockOptionFormat);
> +
> +    switch (fmt) {
> +    case QB_FMT_COW:
> +        (*op)->o_fmt.fmt_op.o_cow.struct_size =
> +                                          sizeof(struct QBlockOption_cow);
> +        break;
> +    case QB_FMT_QED:
> +        (*op)->o_fmt.fmt_op.o_qed.struct_size =
> +                                          sizeof(struct QBlockOption_qed);
> +        break;
> +    case QB_FMT_QCOW:
> +        (*op)->o_fmt.fmt_op.o_qcow.struct_size =
> +                                          sizeof(struct QBlockOption_qcow);
> +        break;
> +    case QB_FMT_QCOW2:
> +        (*op)->o_fmt.fmt_op.o_qcow2.struct_size =
> +                                          sizeof(struct QBlockOption_qcow2);
> +        break;
> +    case QB_FMT_RAW:
> +        (*op)->o_fmt.fmt_op.o_raw.struct_size =
> +                                          sizeof(struct QBlockOption_raw);
> +        break;
> +    case QB_FMT_RBD:
> +        (*op)->o_fmt.fmt_op.o_rbd.struct_size =
> +                                          sizeof(struct QBlockOption_rbd);
> +        break;
> +    case QB_FMT_SHEEPDOG:
> +        (*op)->o_fmt.fmt_op.o_sheepdog.struct_size =
> +                                          sizeof(struct QBlockOption_sheepdog);
> +        break;
> +    case QB_FMT_VDI:
> +        (*op)->o_fmt.fmt_op.o_vdi.struct_size =
> +                                          sizeof(struct QBlockOption_vdi);
> +        break;
> +    case QB_FMT_VMDK:
> +        (*op)->o_fmt.fmt_op.o_vmdk.struct_size =
> +                                          sizeof(struct QBlockOption_vmdk);
> +        break;
> +    case QB_FMT_VPC:
> +        (*op)->o_fmt.fmt_op.o_vpc.struct_size =
> +                                          sizeof(struct QBlockOption_vpc);
> +        break;
> +    default:
> +        break;
> +    }
> +    (*op)->o_fmt.fmt_type = fmt;
> +    return 0;
> +}
> +
> +/*
> +   free QBlockOptionCreate structure.
> +   params:
> +     op: in, *op will be set as NULL after called.
> +   return:
> +     void
> + */
> +static inline void qb_oc_free(struct QBlockOptionCreate **op)
> +{
> +    free(*op);
> +    *op = NULL;
> +}
> +/*
> +   create a block file.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     op: in, create option.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op);
> +
> +
> +/* sync access */
> +/*
> +   block sync read.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     buf: out, buffer that receive the content.
> +     len: in, length to read.
> +     offset: in, offset in the block data.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
> +                                                      off_t offset);
> +
> +/*
> +   block sync read.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     buf: in, buffer that would be wrote.
> +     len: in, length to write.
> +     offset: in, offset in the block data.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
> +                                                       off_t offset);
> +
> +/*
> +   block sync flush.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_flush(struct QBlockState *qbs);
> +
> +/* information */
> +/* image related info, static information, from user perspective. */
> +/* now it is a plain structure, wonder if it could be foldered into embbed one
> +   to reflect that format related information better. */
> +struct QBlockInfoImage {
> +    int struct_size;
> +    char *filename;
> +    enum QBlockProtocol protocol;
> +    enum QBlockFormat format;
> +    size_t virt_size;
> +    /* advance info */
> +    size_t allocated_size;
> +    bool encrypt;
> +    char *backing_filename;
> +};
> +
> +/* image info */
> +/*
> +   get image info.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     info, out, pointer that would receive the information.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);
> +
> +/*
> +   free image info.
> +   params:
> +     info, in, pointer, *info would be set to NULL after function called.
> +   return:
> +     void.
> + */
> +void qb_infoimage_free(struct QBlockInfoImage **info);
> +
> +/* misc */
> +bool qb_supports_format(enum QBlockFormat fmt);
> +bool qb_supports_protocol(enum QBlockProtocol proto);
> +
>

A comment here stating that the string may not be free()d.

 +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);
> +#endif
> --
> 1.7.1
>
>
>
Wayne Xia Aug. 10, 2012, 8:35 a.m. UTC | #2
于 2012-8-10 1:36, Blue Swirl 写道:
> On Thu, Aug 9, 2012 at 10:12 AM, Wenchao Xia <xiawenc@linux.vnet.ibm.com> wrote:
>>    This patch is the API design.
>>
>> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> ---
>>   libqblock.c |  670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   libqblock.h |  447 +++++++++++++++++++++++++++++++++++++++
>>   2 files changed, 1117 insertions(+), 0 deletions(-)
>>   create mode 100644 libqblock.c
>>   create mode 100644 libqblock.h
>>
>> diff --git a/libqblock.c b/libqblock.c
>> new file mode 100644
>> index 0000000..52e46dc
>> --- /dev/null
>> +++ b/libqblock.c
>> @@ -0,0 +1,670 @@
>> +/*
>> + * Copyright IBM Corp. 2012
>> + *
>> + * Authors:
>> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
>
> Current address of FSF is:
>
> 51 Franklin Street, Fifth Floor
> Boston, MA 02110-1301
> USA
>
> However, please use the recommended web version like other files.
>
OK.

>> + */
>> +
>> +#include "libqblock.h"
>> +
>> +#include <unistd.h>
>> +#include "block.h"
>> +#include "block_int.h"
>> +
>> +#define FUNC_FREE free
>> +#define FUNC_MALLOC malloc
>> +#define FUNC_CALLOC calloc
>> +
>> +#define CLEAN_FREE(p) { \
>> +        FUNC_FREE(p); \
>> +        (p) = NULL; \
>> +}
>
> I don't think this is enough since block functions may use glib memory
> allocation. Probably the block files have to be compiled with wrappers
> for g_malloc and friends.
>
   yes, I plan to patch qemu block code that used glib. I think qemu
block code did not consider OOM, so patches should go there to make it
known of that, and then at higher level qemu exit when it found block
layer reports OOM.

>> +
>> +/* details should be hidden to user */
>> +struct QBlockState {
>> +    BlockDriverState *bdrvs;
>> +    char *filename;
>> +    const char *err_msg;
>> +    int err_no;
>> +};
>> +
>> +static void set_err_nomem(struct QBlockState *qbs)
>> +{
>> +    qbs->err_msg = NULL;
>> +    qbs->err_no = 0;
>> +}
>> +
>> +void libqblock_init(void)
>> +{
>> +    bdrv_init();
>> +    return;
>
> Useless return.
>
done.

>> +}
>> +
>> +int qb_state_new(struct QBlockState **qbs)
>> +{
>> +    *qbs = FUNC_CALLOC(1, sizeof(struct QBlockState));
>> +    if (*qbs == NULL) {
>> +        return QB_ERR_MEM_ERR;
>> +    }
>> +    (*qbs)->bdrvs = bdrv_new("hda");
>> +    if ((*qbs)->bdrvs == NULL) {
>> +        CLEAN_FREE(*qbs);
>> +        return QB_ERR_INTERNAL_ERR;
>> +    }
>> +    return 0;
>> +}
>> +
>> +void qb_state_free(struct QBlockState **qbs)
>> +{
>> +    CLEAN_FREE((*qbs)->filename);
>> +    if ((*qbs)->bdrvs == NULL) {
>> +        bdrv_delete((*qbs)->bdrvs);
>> +        (*qbs)->bdrvs = NULL;
>> +    }
>> +    CLEAN_FREE(*qbs);
>> +    return;
>
> Remove.
>
>> +}
>> +
>> +static const char *fmt2str(enum QBlockFormat fmt)
>> +{
>> +    const char *ret = NULL;
>> +    switch (fmt) {
>> +    case QB_FMT_COW:
>> +        ret = "cow";
>> +        break;
>> +    case QB_FMT_QED:
>> +        ret = "qed";
>> +        break;
>> +    case QB_FMT_QCOW:
>> +        ret = "qcow";
>> +        break;
>> +    case QB_FMT_QCOW2:
>> +        ret = "qcow2";
>> +        break;
>> +    case QB_FMT_RAW:
>> +        ret = "raw";
>> +        break;
>> +    case QB_FMT_RBD:
>> +        ret = "rbd";
>> +        break;
>> +    case QB_FMT_SHEEPDOG:
>> +        ret = "sheepdog";
>> +        break;
>> +    case QB_FMT_VDI:
>> +        ret = "vdi";
>> +        break;
>> +    case QB_FMT_VMDK:
>> +        ret = "vmdk";
>> +        break;
>> +    case QB_FMT_VPC:
>> +        ret = "vpc";
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +    return ret;
>> +}
>> +
>> +static enum QBlockFormat str2fmt(const char *fmt)
>> +{
>> +    enum QBlockFormat ret = QB_FMT_NONE;
>> +    if (0 == strcmp(fmt, "cow")) {
>
> Please use natural order:
> strcmp(fmt, "cow") == 0
>
>> +        ret = QB_FMT_COW;
>> +    } else if (0 == strcmp(fmt, "qed")) {
>> +        ret = QB_FMT_QED;
>> +    } else if (0 == strcmp(fmt, "qcow")) {
>> +        ret = QB_FMT_QCOW;
>> +    } else if (0 == strcmp(fmt, "qcow2")) {
>> +        ret = QB_FMT_QCOW2;
>> +    } else if (0 == strcmp(fmt, "raw")) {
>> +        ret = QB_FMT_RAW;
>> +    } else if (0 == strcmp(fmt, "rbd")) {
>> +        ret = QB_FMT_RBD;
>> +    } else if (0 == strcmp(fmt, "sheepdog")) {
>> +        ret = QB_FMT_SHEEPDOG;
>> +    } else if (0 == strcmp(fmt, "vdi")) {
>> +        ret = QB_FMT_VDI;
>> +    } else if (0 == strcmp(fmt, "vmdk")) {
>> +        ret = QB_FMT_VMDK;
>> +    } else if (0 == strcmp(fmt, "vpc")) {
>> +        ret = QB_FMT_VPC;
>> +    }
>> +    return ret;
>> +}
>> +
>> +/* copy information and take care the member difference in differect version.
>> +   Assuming all new member are added in the tail, struct size is the first
>> +   member, this is old to new version, src have its struct_size set. */
>> +static void qboo_adjust_o2n(struct QBlockOptionOpen *dest,
>> +                            struct QBlockOptionOpen *src)
>> +{
>> +    /* for simple it does memcpy now, need to take care of embbed structure */
>> +    memcpy(dest, src, src->struct_size);
>
> Since later there will be a field by field copy, I'd use:
> *dest = *src;
>
>> +}
>> +
>> +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op)
>> +{
>> +    int ret = 0, bd_ret;
>> +    BlockDriverState *bs;
>> +    struct QBlockOptionOpen qboo;
>> +    BlockDriver *bd;
>> +    const char *fmt;
>> +
>> +    /* take care of user settings */
>> +    qboo_adjust_o2n(&qboo, op);
>> +
>> +    /* check parameters */
>> +    if (qboo.o_loc.filename == NULL) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "filename was not set.";
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +
>> +    if (path_has_protocol(qboo.o_loc.filename) > 0) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "filename have protocol.";
>
> had
>
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +
>> +    if (qboo.o_loc.protocol >= QB_PROTO_MAX) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "protocol was not supported.";
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +
>> +    bd = NULL;
>> +    fmt = fmt2str(qboo.o_fmt_type);
>> +    if (fmt != NULL) {
>> +        bd = bdrv_find_format(fmt);
>> +        assert(bd != NULL);
>> +    }
>
> What should happen in the NULL fmt case, goto out?
>

This should never happen so I used assert, because fmt was translated
in libqblock, it should return a known format string unless there is
a bug.


>> +
>> +
>> +    /* do real openning */
>> +    bs = qbs->bdrvs;
>> +    bd_ret = bdrv_open(bs, qboo.o_loc.filename, qboo.o_flag, bd);
>> +    if (bd_ret < 0) {
>> +        ret = QB_ERR_INTERNAL_ERR;
>> +        qbs->err_msg = "failed to open the file.";
>> +        qbs->err_no = -errno;
>> +        goto out;
>> +    }
>> +
>> +    if (qbs->filename != NULL) {
>> +        FUNC_FREE(qbs->filename);
>> +    }
>> +    qbs->filename = strdup(qboo.o_loc.filename);
>> +    if (qbs->filename == NULL) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        set_err_nomem(qbs);
>> +        goto out;
>> +    }
>> +
>> + out:
>> +    return ret;
>> +}
>> +
>> +void qb_close(struct QBlockState *qbs)
>> +{
>> +    BlockDriverState *bs;
>> +
>> +    bs = qbs->bdrvs;
>> +    bdrv_close(bs);
>> +    return;
>
> Delete return
>
>> +}
>> +
>> +/* copy information and take care the member difference in differect version.
>> +   Assuming all new member are added in the tail, struct size is the first
>> +   member, this is old to new version, src have its struct_size set. */
>> +static void qboc_adjust_o2n(struct QBlockOptionCreate *dest,
>> +                            struct QBlockOptionCreate *src)
>> +{
>> +    /* for simple it does memcpy now, need to take care of embbed structure */
>> +    memcpy(dest, src, src->struct_size);
>> +}
>> +
>> +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op)
>> +{
>> +    int ret = 0, bd_ret;
>> +    BlockDriverState *bs = NULL;
>> +    BlockDriver *drv = NULL, *backing_drv = NULL;
>> +    struct QBlockOptionCreate qbco;
>> +    const char *fmt = NULL, *fmt_backing_str = NULL, *tmp;
>> +    QEMUOptionParameter *param = NULL, *create_options = NULL;
>> +    QEMUOptionParameter *backing_fmt, *backing_file, *size;
>> +    struct QBlockOption_cow *o_cow = NULL;
>> +    struct QBlockOption_qed *o_qed = NULL;
>> +    struct QBlockOption_qcow *o_qcow = NULL;
>> +    struct QBlockOption_qcow2 *o_qcow2 = NULL;
>> +    struct QBlockOption_raw *o_raw = NULL;
>> +    struct QBlockOption_rbd *o_rbd = NULL;
>> +    struct QBlockOption_sheepdog *o_sd = NULL;
>> +    struct QBlockOption_vdi *o_vdi = NULL;
>> +    struct QBlockOption_vmdk *o_vmdk = NULL;
>> +    struct QBlockOption_vpc *o_vpc = NULL;
>> +    int backing_flag = 0;
>> +
>> +    /* take care of user settings */
>> +    qboc_adjust_o2n(&qbco, op);
>> +
>> +    /* check parameters */
>> +    if (qbco.o_loc.filename == NULL) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "filename was not set.";
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +
>> +    if (path_has_protocol(qbco.o_loc.filename) > 0) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "filename have protocol.";
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +
>> +    if (qbco.o_loc.protocol >= QB_PROTO_MAX) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "protocol was not supported.";
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +
>> +    /* set parameters */
>> +    fmt = fmt2str(qbco.o_fmt.fmt_type);
>> +    if (fmt == NULL) {
>> +        ret = QB_ERR_INVALID_PARAM;
>> +        qbs->err_msg = "format is not valid.";
>> +        qbs->err_no = 0;
>> +        goto out;
>> +    }
>> +    drv = bdrv_find_format(fmt);
>> +    assert(drv != NULL);
>> +
>> +    create_options = append_option_parameters(create_options,
>> +                                              drv->create_options);
>> +    param = parse_option_parameters("", create_options, param);
>> +
>> +    switch (qbco.o_fmt.fmt_type) {
>> +    case QB_FMT_COW:
>> +        o_cow = &qbco.o_fmt.fmt_op.o_cow;
>> +        assert(0 == set_option_parameter_int(param,
>> +                                BLOCK_OPT_SIZE, o_cow->virt_size));
>
> expr == 0, also below
>
>> +        if (o_cow->backing_file) {
>> +            assert(0 == set_option_parameter(param,
>> +                                BLOCK_OPT_BACKING_FILE, o_cow->backing_file));
>> +        }
>> +        backing_flag = o_cow->backing_flag;
>> +        break;
>> +    case QB_FMT_QED:
>> +        o_qed = &qbco.o_fmt.fmt_op.o_qed;
>> +        assert(0 == set_option_parameter_int(param,
>> +                                BLOCK_OPT_SIZE, o_qed->virt_size));
>> +        if (o_qed->backing_file) {
>> +            assert(0 == set_option_parameter(param,
>> +                                BLOCK_OPT_BACKING_FILE, o_qed->backing_file));
>> +        }
>> +        fmt_backing_str = fmt2str(o_qed->backing_fmt);
>> +        if (fmt_backing_str) {
>> +            assert(0 == set_option_parameter(param,
>> +                                BLOCK_OPT_BACKING_FMT, fmt_backing_str));
>> +        }
>> +        assert(0 == set_option_parameter_int(param,
>> +                                BLOCK_OPT_CLUSTER_SIZE, o_qed->cluster_size));
>> +        assert(0 == set_option_parameter_int(param,
>> +                                BLOCK_OPT_TABLE_SIZE, o_qed->table_size));
>> +        backing_flag = o_qed->backing_flag;
>> +        break;
>> +    case QB_FMT_QCOW:
>> +        o_qcow = &qbco.o_fmt.fmt_op.o_qcow;
>> +        assert(0 == set_option_parameter_int(param,
>> +                                BLOCK_OPT_SIZE, o_qcow->virt_size));
>> +        if (o_qcow->backing_file) {
>> +            assert(0 == set_option_parameter(param,
>> +                                BLOCK_OPT_BACKING_FILE, o_qcow->backing_file));
>> +        }
>> +        tmp = o_qcow->encrypt ? "on" : "off";
>> +        assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp));
>> +        backing_flag = o_qcow->backing_flag;
>> +        break;
>> +    case QB_FMT_QCOW2:
>> +        o_qcow2 = &qbco.o_fmt.fmt_op.o_qcow2;
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_SIZE, o_qcow2->virt_size));
>> +        if (o_qcow2->backing_file) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_BACKING_FILE, o_qcow2->backing_file));
>> +        }
>> +        fmt_backing_str = fmt2str(o_qcow2->backing_fmt);
>> +        if (fmt_backing_str) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_BACKING_FMT, fmt_backing_str));
>> +        }
>> +        if (o_qcow2->compat_level) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_COMPAT_LEVEL, o_qcow2->compat_level));
>> +        }
>> +        tmp = o_qcow2->encrypt ? "on" : "off";
>> +        assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp));
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_CLUSTER_SIZE, o_qcow2->cluster_size));
>> +        if (o_qcow2->prealloc_mode) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_PREALLOC, o_qcow2->prealloc_mode));
>> +        }
>> +        backing_flag = o_qcow2->backing_flag;
>> +        break;
>> +    case QB_FMT_RAW:
>> +        o_raw = &qbco.o_fmt.fmt_op.o_raw;
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_SIZE, o_raw->virt_size));
>> +        break;
>> +    case QB_FMT_RBD:
>> +        o_rbd = &qbco.o_fmt.fmt_op.o_rbd;
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_SIZE, o_rbd->virt_size));
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_CLUSTER_SIZE, o_rbd->cluster_size));
>> +        break;
>> +    case QB_FMT_SHEEPDOG:
>> +        o_sd = &qbco.o_fmt.fmt_op.o_sheepdog;
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_SIZE, o_sd->virt_size));
>> +        if (o_sd->backing_file) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_BACKING_FILE, o_sd->backing_file));
>> +        }
>> +        if (o_sd->prealloc_mode) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_PREALLOC, o_sd->prealloc_mode));
>> +        }
>> +        backing_flag = o_sd->backing_flag;
>> +        break;
>> +    case QB_FMT_VDI:
>> +        o_vdi = &qbco.o_fmt.fmt_op.o_vdi;
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_SIZE, o_vdi->virt_size));
>> +        /* following option is not always valid depends on configuration */
>> +        set_option_parameter_int(param,
>> +                              BLOCK_OPT_CLUSTER_SIZE, o_vdi->cluster_size);
>> +        set_option_parameter_int(param, "static", o_vdi->prealloc_mode);
>> +        break;
>> +    case QB_FMT_VMDK:
>> +        o_vmdk = &qbco.o_fmt.fmt_op.o_vmdk;
>> +        assert(0 == set_option_parameter_int(param,
>> +                              BLOCK_OPT_SIZE, o_vmdk->virt_size));
>> +        if (o_vmdk->backing_file) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_BACKING_FILE, o_vmdk->backing_file));
>> +        }
>> +        tmp = o_vmdk->compat_version6 ? "on" : "off";
>> +        assert(0 == set_option_parameter(param, BLOCK_OPT_COMPAT6, tmp));
>> +        if (o_vmdk->subfmt) {
>> +            assert(0 == set_option_parameter(param,
>> +                              BLOCK_OPT_SUBFMT, o_vmdk->subfmt));
>> +        }
>> +        backing_flag = o_vmdk->backing_flag;
>> +        break;
>> +    case QB_FMT_VPC:
>> +        o_vpc = &qbco.o_fmt.fmt_op.o_vpc;
>> +        assert(0 == set_option_parameter_int(param,
>> +                               BLOCK_OPT_SIZE, o_vpc->virt_size));
>> +        if (o_vpc->subfmt) {
>> +            assert(0 == set_option_parameter(param,
>> +                               BLOCK_OPT_SUBFMT, o_vpc->subfmt));
>> +        }
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +
>> +    backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
>> +    if (backing_file && backing_file->value.s) {
>> +        if (!strcmp(op->o_loc.filename, backing_file->value.s)) {
>> +            ret = QB_ERR_INVALID_PARAM;
>> +            qbs->err_msg = "backing file is the same with new file.";
>
> same as the new file
>
>> +            qbs->err_no = 0;
>> +            goto out;
>> +        }
>> +    }
>> +
>> +    backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
>> +    if (backing_fmt && backing_fmt->value.s) {
>> +        backing_drv = bdrv_find_format(backing_fmt->value.s);
>> +        assert(backing_drv != NULL);
>> +    }
>> +
>> +    size = get_option_parameter(param, BLOCK_OPT_SIZE);
>> +    if (size && size->value.n <= 0) {
>> +        if (backing_file && backing_file->value.s) {
>> +            uint64_t size;
>> +            char buf[32];
>> +            int back_flags;
>> +
>> +            /* backing files always opened read-only */
>> +            back_flags =
>> +                backing_flag &
>> +                ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
>> +
>> +            bs = bdrv_new("");
>> +
>> +            ret = bdrv_open(bs, backing_file->value.s,
>> +                                back_flags, backing_drv);
>> +            if (ret < 0) {
>> +                ret = QB_ERR_INTERNAL_ERR;
>> +                qbs->err_msg = "failed to open the backing file.";
>> +                qbs->err_no = -errno;
>> +                goto out;
>> +            }
>> +            bdrv_get_geometry(bs, &size);
>> +            size *= 512;
>
> Where's the #define?
>
>> +
>> +            snprintf(buf, sizeof(buf), "%" PRId64, size);
>> +            set_option_parameter(param, BLOCK_OPT_SIZE, buf);
>> +        } else {
>> +            ret = QB_ERR_INVALID_PARAM;
>> +            qbs->err_msg = "neither size or backing file was not set.";
>> +            qbs->err_no = 0;
>> +            goto out;
>> +        }
>> +    }
>> +
>> +    bd_ret = bdrv_create(drv, op->o_loc.filename, param);
>> +
>> +    if (bd_ret < 0) {
>> +        ret = QB_ERR_INTERNAL_ERR;
>> +        qbs->err_no = bd_ret;
>> +        if (bd_ret == -ENOTSUP) {
>> +            qbs->err_msg = "formatting option not supported.";
>
> s/f/F/
>
>> +        } else if (bd_ret == -EFBIG) {
>> +            qbs->err_msg = "The image size is too large.";
>> +        } else {
>> +            qbs->err_msg = "Error in creating the image.";
>> +        }
>> +        goto out;
>> +    }
>> +
>> +out:
>> +    free_option_parameters(create_options);
>> +    free_option_parameters(param);
>> +
>> +    if (bs) {
>> +        bdrv_delete(bs);
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +/* ignore case that len is not alligned to 512 now. */
>
> aligned
>
>> +int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
>> +                                                      off_t offset)
>> +{
>> +    int ret = 0, bd_ret;
>> +    BlockDriverState *bs;
>> +
>> +    bs = qbs->bdrvs;
>> +
>> +    assert((len & 0x01ff) == 0);
>> +    assert((offset & 0x01ff) == 0);
>
> Also here use the #defined constant.
>
>> +    bd_ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, len >> 9);
>> +    if (bd_ret < 0) {
>> +        ret = QB_ERR_INTERNAL_ERR;
>> +        qbs->err_no = bd_ret;
>> +        if (bd_ret == -EIO) {
>> +            qbs->err_msg = "Generic I/O error.";
>> +        } else if (bd_ret == -ENOMEDIUM) {
>> +            qbs->err_msg = "No meida inserted.";
>
> media
>
>> +        } else if (bd_ret == -EINVAL) {
>> +            qbs->err_msg = "Sector was not correct.";
>> +        } else {
>> +            qbs->err_msg = "Internal error.";
>> +        }
>> +    }
>> +    return ret;
>> +}
>> +
>> +/* ignore case that len is not alligned to 512 now. */
>> +int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
>> +                                                       off_t offset)
>> +{
>> +    int ret = 0, bd_ret;
>> +    BlockDriverState *bs;
>> +
>> +    bs = qbs->bdrvs;
>> +
>> +    assert((len & 0x01ff) == 0);
>> +    assert((offset & 0x01ff) == 0);
>> +    bd_ret = bdrv_write(bs, offset >> 9, buf, len >> 9);
>> +    if (bd_ret < 0) {
>> +        ret = QB_ERR_INTERNAL_ERR;
>> +        qbs->err_no = bd_ret;
>> +        if (bd_ret == -EIO) {
>> +            qbs->err_msg = "Generic I/O error.";
>> +        } else if (bd_ret == -ENOMEDIUM) {
>> +            qbs->err_msg = "No meida inserted.";
>> +        } else if (bd_ret == -EINVAL) {
>> +            qbs->err_msg = "Sector was not correct.";
>> +        } else if (bd_ret == -EACCES) {
>> +            qbs->err_msg = "Ready only device.";
>> +        } else {
>> +            qbs->err_msg = "Internal error.";
>> +        }
>> +    }
>> +    return ret;
>> +}
>> +
>> +int qb_flush(struct QBlockState *qbs)
>> +{
>> +    int ret = 0, bd_ret;
>> +    BlockDriverState *bs;
>> +
>> +    bs = qbs->bdrvs;
>> +    bd_ret = bdrv_flush(bs);
>> +    if (bd_ret < 0) {
>> +        ret = QB_ERR_INTERNAL_ERR;
>> +        qbs->err_no = bd_ret;
>> +        qbs->err_msg = NULL;
>> +    }
>> +    return ret;
>> +}
>> +
>> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info)
>> +{
>> +    int ret = 0;
>> +    BlockDriverState *bs;
>> +    const char *tmp;
>> +    uint64_t total_sectors;
>> +    char backing_filename[1024];
>> +
>> +    struct QBlockInfoImage *ifimg = FUNC_CALLOC(1,
>> +                                           sizeof(struct QBlockInfoImage));
>> +    if (ifimg == NULL) {
>> +        ret = QB_ERR_MEM_ERR;
>> +        set_err_nomem(qbs);
>> +        goto out;
>> +    }
>> +
>> +    bs = qbs->bdrvs;
>> +    ifimg->filename = strdup(qbs->filename);
>> +    if (ifimg->filename == NULL) {
>> +        ret = QB_ERR_MEM_ERR;
>> +        set_err_nomem(qbs);
>> +        goto err;
>> +    }
>> +    tmp = bdrv_get_format_name(bs);
>> +    ifimg->format = str2fmt(tmp);
>> +    /* support only file now */
>> +    ifimg->protocol = QB_PROTO_FILE;
>> +
>> +    bdrv_get_geometry(bs, &total_sectors);
>> +    ifimg->virt_size = total_sectors * 512;
>> +    ifimg->allocated_size = bdrv_get_allocated_file_size(bs);
>> +    ifimg->encrypt = bdrv_is_encrypted(bs);
>> +    bdrv_get_full_backing_filename(bs, backing_filename,
>> +                                   sizeof(backing_filename));
>> +    if (backing_filename[0] != '\0') {
>> +        ifimg->backing_filename = strdup(backing_filename);
>> +        if (ifimg->backing_filename == NULL) {
>> +            ret = QB_ERR_MEM_ERR;
>> +            set_err_nomem(qbs);
>> +            goto err;
>> +        }
>> +    }
>> +
>> +    *info = ifimg;
>> + out:
>> +    return ret;
>> + err:
>> +    qb_infoimage_free(&ifimg);
>> +    return ret;
>> +}
>> +
>> +void qb_infoimage_free(struct QBlockInfoImage **info)
>> +{
>> +    if (*info == NULL) {
>> +        return;
>> +    }
>> +    FUNC_FREE((*info)->filename);
>> +    FUNC_FREE((*info)->backing_filename);
>> +    FUNC_FREE(*info);
>> +    *info = NULL;
>> +    return;
>> +}
>> +
>> +bool qb_supports_format(enum QBlockFormat fmt)
>> +{
>> +    if (fmt < QB_FMT_MAX) {
>> +        return true;
>> +    }
>> +    return false;
>> +}
>> +
>> +bool qb_supports_protocol(enum QBlockProtocol proto)
>> +{
>> +    if (proto < QB_PROTO_MAX) {
>> +        return true;
>> +    }
>> +    return false;
>> +}
>> +
>> +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num)
>> +{
>> +    *err_num = qbs->err_no;
>> +    return qbs->err_msg;
>> +}
>> diff --git a/libqblock.h b/libqblock.h
>> new file mode 100644
>> index 0000000..d2e9502
>> --- /dev/null
>> +++ b/libqblock.h
>> @@ -0,0 +1,447 @@
>> +/*
>> + * Copyright IBM Corp. 2012
>> + *
>> + * Authors:
>> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
>
> web version
>
>> + */
>> +
>> +#ifndef LIBQBLOCK_H
>> +#define LIBQBLOCK_H
>> +
>> +#include <stdio.h>
>> +#include <stdint.h>
>> +#include <stdlib.h>
>> +
>> +#define bool _Bool
>
> #include <stdbool.h>
>
>> +
>> +#define QB_ERR_MEM_ERR (-1)
>> +#define QB_ERR_INTERNAL_ERR (-2)
>> +#define QB_ERR_INVALID_PARAM (-3)
>> +
>> +/* this library is designed around this core struct. */
>> +struct QBlockState;
>> +
>> +/*
>> +   libarary init
>
> library
>
>> +   This function get the library ready to use.
>> + */
>
> Please use markup for the comments, see for example memory.h.
>

OK.

>> +void libqblock_init(void);
>> +
>> +/*
>> +   create a new qbs object
>> +   params:
>> +     qbs: out, pointer that will receive created obj.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +int qb_state_new(struct QBlockState **qbs);
>> +
>> +/*
>> +   delete a qbs object
>> +   params:
>> +     qbs: in, pointer that will be freed. *qbs will be set to NULL.
>> +   return:
>> +     void.
>> + */
>> +void qb_state_free(struct QBlockState **qbs);
>> +
>> +
>> +/* flag used in open and create */
>> +#define LIBQBLOCK_O_RDWR        0x0002
>> +/* open the file read only and save writes in a snapshot */
>> +#define LIBQBLOCK_O_SNAPSHOT    0x0008
>> +/* do not use the host page cache */
>> +#define LIBQBLOCK_O_NOCACHE     0x0020
>> +/* use write-back caching */
>> +#define LIBQBLOCK_O_CACHE_WB    0x0040
>> +/* use native AIO instead of the thread pool */
>> +#define LIBQBLOCK_O_NATIVE_AIO  0x0080
>> +/* don't open the backing file */
>> +#define LIBQBLOCK_O_NO_BACKING  0x0100
>> +/* disable flushing on this disk */
>> +#define LIBQBLOCK_O_NO_FLUSH    0x0200
>> +/* copy read backing sectors into image */
>> +#define LIBQBLOCK_O_COPY_ON_READ 0x0400
>> +/* consistency hint for incoming migration */
>> +#define LIBQBLOCK_O_INCOMING    0x0800
>> +
>> +#define LIBQBLOCK_O_CACHE_MASK \
>> +   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
>> +
>> +enum QBlockProtocol {
>> +    QB_PROTO_FILE = 0,
>> +    QB_PROTO_MAX
>> +};
>> +
>> +enum QBlockFormat {
>> +    QB_FMT_NONE = 0,
>> +    QB_FMT_COW,
>> +    QB_FMT_QED,
>> +    QB_FMT_QCOW,
>> +    QB_FMT_QCOW2,
>> +    QB_FMT_RAW,
>> +    QB_FMT_RBD,
>> +    QB_FMT_SHEEPDOG,
>> +    QB_FMT_VDI,
>> +    QB_FMT_VMDK,
>> +    QB_FMT_VPC,
>> +    QB_FMT_MAX
>> +};
>> +
>> +/* block target location info, it include all information about how to find
>> +   the image */
>> +struct QBlockLocInfo {
>> +    int struct_size;
>> +    const char *filename;
>> +    enum QBlockProtocol protocol;
>> +};
>> +
>> +/* how to open the image */
>> +struct QBlockOptionOpen {
>> +    int struct_size;
>> +    struct QBlockLocInfo o_loc; /* how to find */
>> +    enum QBlockFormat o_fmt_type; /* how to extract */
>> +    int o_flag; /* how to control */
>> +};
>> +
>> +/*
>> +   create a new QBlockOptionOpen structure.
>> +   params:
>> +     op: out, pointer that will receive created structure.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +static inline int qb_oo_new(struct QBlockOptionOpen **op)
>> +{
>> +    *op = calloc(1, sizeof(struct QBlockOptionOpen));
>
> calloc() is not wrapped by your macros.
>

   Macro is defined at .c file, I am not sure if I should bring
it to header file, because user should not care about it.

>> +    if (*op == NULL) {
>> +        return QB_ERR_MEM_ERR;
>> +    }
>> +    (*op)->struct_size = sizeof(struct QBlockOptionOpen);
>> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
>> +    return 0;
>> +}
>> +
>> +/*
>> +   free QBlockOptionOpen structure.
>> +   params:
>> +     op: in, *op will be set as NULL after called.
>> +   return:
>> +     void
>> + */
>> +static inline void qb_oo_free(struct QBlockOptionOpen **op)
>> +{
>> +    free(*op);
>
> Also free() here.
>
>> +    *op = NULL;
>> +}
>> +
>> +/*
>> +   open a block object.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     op: in, options for open.
>> +   return:
>> +     0 on success, negative on fail.
>> + */
>> +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op);
>> +
>> +/*
>> +   close a block object.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +   return:
>> +     void.
>> + */
>> +void qb_close(struct QBlockState *qbs);
>> +
>> +/* format related options, struct_size must be set. */
>> +struct QBlockOption_cow {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +};
>> +
>> +struct QBlockOption_qed {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    enum QBlockFormat backing_fmt;
>> +    int backing_flag;
>> +    size_t cluster_size; /* unit is bytes */
>> +    size_t table_size; /* unit is clusters */
>> +};
>> +
>> +struct QBlockOption_qcow {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    bool encrypt;
>> +};
>> +
>> +struct QBlockOption_qcow2 {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *compat_level; /* "Compatibility level (0.10 or 1.1)" */
>> +    const char *backing_file;
>> +    enum QBlockFormat backing_fmt;
>> +    int backing_flag;
>> +    bool encrypt;
>> +    size_t cluster_size; /* unit is bytes */
>> +    const char *prealloc_mode; /* off or metadata */
>> +};
>> +
>> +struct QBlockOption_raw {
>> +    int struct_size;
>> +    size_t virt_size;
>> +};
>> +
>> +struct QBlockOption_rbd {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    size_t cluster_size;
>> +};
>> +
>> +struct QBlockOption_sheepdog {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    const char *prealloc_mode; /* off or full */
>> +};
>> +
>> +struct QBlockOption_vdi {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    size_t cluster_size;
>> +    bool prealloc_mode;
>> +};
>> +
>> +struct QBlockOption_vmdk {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    bool compat_version6;
>> +    const char *subfmt;
>> +    /* vmdk flat extent format, values:
>> +   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
>> +    twoGbMaxExtentFlat | streamOptimized} */
>> +};
>> +
>> +struct QBlockOption_vpc {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *subfmt; /* "{dynamic (default) | fixed} " */
>> +};
>
> I'd rather have different open functions than variant structures.
>

   It have about 10 formats, API for each one seems not friendly API,
and hard to adding the comibination case of block protocol and formats
later.

>> +
>> +union QBlockOption_fmt {
>> +    struct QBlockOption_cow       o_cow;
>> +    struct QBlockOption_qed       o_qed;
>> +    struct QBlockOption_qcow      o_qcow;
>> +    struct QBlockOption_qcow2     o_qcow2;
>> +    struct QBlockOption_raw       o_raw;
>> +    struct QBlockOption_rbd       o_rbd;
>> +    struct QBlockOption_sheepdog  o_sheepdog;
>> +    struct QBlockOption_vdi       o_vdi;
>> +    struct QBlockOption_vmdk      o_vmdk;
>> +    struct QBlockOption_vpc       o_vpc;
>> +};
>> +
>> +struct QBlockOptionFormat {
>> +    int struct_size;
>> +    enum QBlockFormat fmt_type;
>> +    union QBlockOption_fmt fmt_op;
>> +};
>> +
>> +/* struct_size in o_loc and o_fmt must set. To make this structure extensible,
>> +   all new member must be added in the tail of each structure. */
>> +struct QBlockOptionCreate {
>> +    int struct_size;
>> +    struct QBlockLocInfo o_loc;
>> +    struct QBlockOptionFormat o_fmt;
>> +};
>> +
>> +/*
>> +   create a new QBlockOptionCreate structure.
>> +   params:
>> +     op: out, pointer that will receive created structure.
>> +     fmt: format want to use.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +static inline int qb_oc_new(struct QBlockOptionCreate **op,
>> +                            enum QBlockFormat fmt)
>> +{
>> +    *op = calloc(1, sizeof(struct QBlockOptionCreate));
>> +    if (*op == NULL) {
>> +        return QB_ERR_MEM_ERR;
>> +    }
>> +    (*op)->struct_size = sizeof(struct QBlockOptionCreate);
>> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
>> +    (*op)->o_fmt.struct_size = sizeof(struct QBlockOptionFormat);
>> +
>> +    switch (fmt) {
>> +    case QB_FMT_COW:
>> +        (*op)->o_fmt.fmt_op.o_cow.struct_size =
>> +                                          sizeof(struct QBlockOption_cow);
>> +        break;
>> +    case QB_FMT_QED:
>> +        (*op)->o_fmt.fmt_op.o_qed.struct_size =
>> +                                          sizeof(struct QBlockOption_qed);
>> +        break;
>> +    case QB_FMT_QCOW:
>> +        (*op)->o_fmt.fmt_op.o_qcow.struct_size =
>> +                                          sizeof(struct QBlockOption_qcow);
>> +        break;
>> +    case QB_FMT_QCOW2:
>> +        (*op)->o_fmt.fmt_op.o_qcow2.struct_size =
>> +                                          sizeof(struct QBlockOption_qcow2);
>> +        break;
>> +    case QB_FMT_RAW:
>> +        (*op)->o_fmt.fmt_op.o_raw.struct_size =
>> +                                          sizeof(struct QBlockOption_raw);
>> +        break;
>> +    case QB_FMT_RBD:
>> +        (*op)->o_fmt.fmt_op.o_rbd.struct_size =
>> +                                          sizeof(struct QBlockOption_rbd);
>> +        break;
>> +    case QB_FMT_SHEEPDOG:
>> +        (*op)->o_fmt.fmt_op.o_sheepdog.struct_size =
>> +                                          sizeof(struct QBlockOption_sheepdog);
>> +        break;
>> +    case QB_FMT_VDI:
>> +        (*op)->o_fmt.fmt_op.o_vdi.struct_size =
>> +                                          sizeof(struct QBlockOption_vdi);
>> +        break;
>> +    case QB_FMT_VMDK:
>> +        (*op)->o_fmt.fmt_op.o_vmdk.struct_size =
>> +                                          sizeof(struct QBlockOption_vmdk);
>> +        break;
>> +    case QB_FMT_VPC:
>> +        (*op)->o_fmt.fmt_op.o_vpc.struct_size =
>> +                                          sizeof(struct QBlockOption_vpc);
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +    (*op)->o_fmt.fmt_type = fmt;
>> +    return 0;
>> +}
>> +
>> +/*
>> +   free QBlockOptionCreate structure.
>> +   params:
>> +     op: in, *op will be set as NULL after called.
>> +   return:
>> +     void
>> + */
>> +static inline void qb_oc_free(struct QBlockOptionCreate **op)
>> +{
>> +    free(*op);
>> +    *op = NULL;
>> +}
>> +/*
>> +   create a block file.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     op: in, create option.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op);
>> +
>> +
>> +/* sync access */
>> +/*
>> +   block sync read.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     buf: out, buffer that receive the content.
>> +     len: in, length to read.
>> +     offset: in, offset in the block data.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
>> +                                                      off_t offset);
>> +
>> +/*
>> +   block sync read.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     buf: in, buffer that would be wrote.
>> +     len: in, length to write.
>> +     offset: in, offset in the block data.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
>> +                                                       off_t offset);
>> +
>> +/*
>> +   block sync flush.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_flush(struct QBlockState *qbs);
>> +
>> +/* information */
>> +/* image related info, static information, from user perspective. */
>> +/* now it is a plain structure, wonder if it could be foldered into embbed one
>> +   to reflect that format related information better. */
>> +struct QBlockInfoImage {
>> +    int struct_size;
>> +    char *filename;
>> +    enum QBlockProtocol protocol;
>> +    enum QBlockFormat format;
>> +    size_t virt_size;
>> +    /* advance info */
>> +    size_t allocated_size;
>> +    bool encrypt;
>> +    char *backing_filename;
>> +};
>> +
>> +/* image info */
>> +/*
>> +   get image info.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     info, out, pointer that would receive the information.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);
>> +
>> +/*
>> +   free image info.
>> +   params:
>> +     info, in, pointer, *info would be set to NULL after function called.
>> +   return:
>> +     void.
>> + */
>> +void qb_infoimage_free(struct QBlockInfoImage **info);
>> +
>> +/* misc */
>> +bool qb_supports_format(enum QBlockFormat fmt);
>> +bool qb_supports_protocol(enum QBlockProtocol proto);
>> +
>>
>
> A comment here stating that the string may not be free()d.
>

ok.

>   +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);
>> +#endif
>> --
>> 1.7.1
>>
>>
>>
>
Paolo Bonzini Aug. 10, 2012, 10:48 a.m. UTC | #3
Il 09/08/2012 12:12, Wenchao Xia ha scritto:
> +/* copy information and take care the member difference in differect version.
> +   Assuming all new member are added in the tail, struct size is the first
> +   member, this is old to new version, src have its struct_size set. */
> +static void qboc_adjust_o2n(struct QBlockOptionCreate *dest,
> +                            struct QBlockOptionCreate *src)
> +{
> +    /* for simple it does memcpy now, need to take care of embbed structure */
> +    memcpy(dest, src, src->struct_size);

You need an assertion that src->struct_size < sizeof(*dest).

However, the structure size perhaps wasn't my brightest idea ever.  But
still thanks for preparing this prototype!  Now that we have a more
complete API to discuss, we can iterate to something better.


> +        assert(0 == set_option_parameter_int(param,
> +                                BLOCK_OPT_SIZE, o_cow->virt_size));

Assertions should not have side effects.


> diff --git a/libqblock.h b/libqblock.h
> new file mode 100644
> index 0000000..d2e9502
> --- /dev/null
> +++ b/libqblock.h
> @@ -0,0 +1,447 @@
> +/*
> + * Copyright IBM Corp. 2012
> + *
> + * Authors:
> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
> + */
> +
> +#ifndef LIBQBLOCK_H
> +#define LIBQBLOCK_H
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#define bool _Bool
> +
> +#define QB_ERR_MEM_ERR (-1)
> +#define QB_ERR_INTERNAL_ERR (-2)
> +#define QB_ERR_INVALID_PARAM (-3)
> +
> +/* this library is designed around this core struct. */
> +struct QBlockState;
> +
> +/*
> +   libarary init
> +   This function get the library ready to use.
> + */
> +void libqblock_init(void);
> +
> +/*
> +   create a new qbs object
> +   params:
> +     qbs: out, pointer that will receive created obj.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +int qb_state_new(struct QBlockState **qbs);
> +
> +/*
> +   delete a qbs object
> +   params:
> +     qbs: in, pointer that will be freed. *qbs will be set to NULL.
> +   return:
> +     void.
> + */
> +void qb_state_free(struct QBlockState **qbs);
> +
> +
> +/* flag used in open and create */
> +#define LIBQBLOCK_O_RDWR        0x0002
> +/* open the file read only and save writes in a snapshot */
> +#define LIBQBLOCK_O_SNAPSHOT    0x0008

I'd rather avoid exposing this for now.

> +/* do not use the host page cache */
> +#define LIBQBLOCK_O_NOCACHE     0x0020
> +/* use write-back caching */
> +#define LIBQBLOCK_O_CACHE_WB    0x0040
> +/* use native AIO instead of the thread pool */
> +#define LIBQBLOCK_O_NATIVE_AIO  0x0080

NATIVE_AIO should be an option for the file protocol.  But it's mostly
for performance and since we only support synchronous I/O we can drop it
for now.


> +/* don't open the backing file */
> +#define LIBQBLOCK_O_NO_BACKING  0x0100
> +/* disable flushing on this disk */
> +#define LIBQBLOCK_O_NO_FLUSH    0x0200
> +/* copy read backing sectors into image */
> +#define LIBQBLOCK_O_COPY_ON_READ 0x0400

I'd rather avoid exposing this for now too.

> +/* consistency hint for incoming migration */
> +#define LIBQBLOCK_O_INCOMING    0x0800

This is internal to QEMU.

Please add a mask of valid bits and an assertion that unknown bits are zero.

> +
> +#define LIBQBLOCK_O_CACHE_MASK \
> +   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
> +
> +enum QBlockProtocol {
> +    QB_PROTO_FILE = 0,
> +    QB_PROTO_MAX
> +};
> +
> +enum QBlockFormat {
> +    QB_FMT_NONE = 0,
> +    QB_FMT_COW,
> +    QB_FMT_QED,
> +    QB_FMT_QCOW,
> +    QB_FMT_QCOW2,
> +    QB_FMT_RAW,
> +    QB_FMT_RBD,
> +    QB_FMT_SHEEPDOG,
> +    QB_FMT_VDI,
> +    QB_FMT_VMDK,
> +    QB_FMT_VPC,
> +    QB_FMT_MAX
> +};
> +
> +/* block target location info, it include all information about how to find
> +   the image */
> +struct QBlockLocInfo {
> +    int struct_size;
> +    const char *filename;
> +    enum QBlockProtocol protocol;
> +};
> +
> +/* how to open the image */
> +struct QBlockOptionOpen {
> +    int struct_size;
> +    struct QBlockLocInfo o_loc; /* how to find */
> +    enum QBlockFormat o_fmt_type; /* how to extract */
> +    int o_flag; /* how to control */
> +};

You are right that the embedded structs are very complicated.  I think
we need to find the right balance between structs for extensibility and
function arguments for ease of use.

For example, we could have

int qb_open(struct QBlockState *qbs,
            struct QBlockLocation *loc,
            struct QBlockFormatOption *op,
            int o_flag);

where:

- QBlockLocation is basically your QBlockLocInfo, but restructured to
use unions for the protocol-specific fields.

- QBlockFormatOption is basically your QBlockOptionFormat struct.  Thus
we can plan for specifying format-specific options at open time rather
than just at create time (this can be useful, for example, for things
such as lazy_refcounts).  Until support is there in the block layer, we
can simply check that all format-specific options are zero.

Since both QBlockLocation and QBlockFormatOption are basically a
discriminated record (enum + union of structs) we can add a large char
member to the union (e.g. char padding[512]) to keep the ABI stable.
Users must zero out all fields, and future additions must ensure that
the default value is represented by all-zeros.

> +    const char *prealloc_mode; /* off or metadata */

Make this an enum.

> +    bool prealloc_mode;

Here too.

> +};
> +
> +struct QBlockOption_vmdk {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    bool compat_version6;
> +    const char *subfmt;
> +    /* vmdk flat extent format, values:
> +   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
> +    twoGbMaxExtentFlat | streamOptimized} */

Here too.

> +/* information */
> +/* image related info, static information, from user perspective. */
> +/* now it is a plain structure, wonder if it could be foldered into embbed one
> +   to reflect that format related information better. */
> +struct QBlockInfoImage {

QBlockImageInfo

> +    int struct_size;

Here the struct size makes more sense, because it's a returned struct.

> +    char *filename;
> +    enum QBlockProtocol protocol;

Can this just be a struct QBlockLocation * (using a pointer avoids
problems with embedded structs)?

> +    enum QBlockFormat format;

Can this just be a struct QBlockFormatOption * (same as the above)?

> +    size_t virt_size;
> +    /* advance info */
> +    size_t allocated_size;
> +    bool encrypt;
> +    char *backing_filename;
> +};
> +
> +/* image info */
> +/*
> +   get image info.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     info, out, pointer that would receive the information.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);

qb_get_image_info

> +
> +/*
> +   free image info.
> +   params:
> +     info, in, pointer, *info would be set to NULL after function called.
> +   return:
> +     void.
> + */
> +void qb_infoimage_free(struct QBlockInfoImage **info);

qb_free_image_info

Paolo

> +
> +/* misc */
> +bool qb_supports_format(enum QBlockFormat fmt);
> +bool qb_supports_protocol(enum QBlockProtocol proto);
> +
> +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);
> +#endif
>
Kevin Wolf Aug. 10, 2012, 11:02 a.m. UTC | #4
Am 09.08.2012 12:12, schrieb Wenchao Xia:
>   This patch is the API design.
> 
> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> ---
>  libqblock.c |  670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  libqblock.h |  447 +++++++++++++++++++++++++++++++++++++++
>  2 files changed, 1117 insertions(+), 0 deletions(-)
>  create mode 100644 libqblock.c
>  create mode 100644 libqblock.h

Ignoring the implementation for now as the design should be visible in
the header file.

> diff --git a/libqblock.h b/libqblock.h
> new file mode 100644
> index 0000000..d2e9502
> --- /dev/null
> +++ b/libqblock.h
> @@ -0,0 +1,447 @@
> +/*
> + * Copyright IBM Corp. 2012
> + *
> + * Authors:
> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
> + */
> +
> +#ifndef LIBQBLOCK_H
> +#define LIBQBLOCK_H
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +#define bool _Bool

Why not use stdbool.h?

> +
> +#define QB_ERR_MEM_ERR (-1)
> +#define QB_ERR_INTERNAL_ERR (-2)
> +#define QB_ERR_INVALID_PARAM (-3)

qemu uses errno values internally, I think they would make sense in the
library interface as well.

> +
> +/* this library is designed around this core struct. */
> +struct QBlockState;
> +
> +/*
> +   libarary init
> +   This function get the library ready to use.
> + */
> +void libqblock_init(void);
> +
> +/*
> +   create a new qbs object
> +   params:
> +     qbs: out, pointer that will receive created obj.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +int qb_state_new(struct QBlockState **qbs);
> +
> +/*
> +   delete a qbs object
> +   params:
> +     qbs: in, pointer that will be freed. *qbs will be set to NULL.
> +   return:
> +     void.
> + */
> +void qb_state_free(struct QBlockState **qbs);

What happens if the qbs is open? Will it be flushed and closed? If so,
can it fail and we need to allow an error return?

> +
> +/* flag used in open and create */
> +#define LIBQBLOCK_O_RDWR        0x0002
> +/* open the file read only and save writes in a snapshot */
> +#define LIBQBLOCK_O_SNAPSHOT    0x0008
> +/* do not use the host page cache */
> +#define LIBQBLOCK_O_NOCACHE     0x0020
> +/* use write-back caching */
> +#define LIBQBLOCK_O_CACHE_WB    0x0040
> +/* use native AIO instead of the thread pool */
> +#define LIBQBLOCK_O_NATIVE_AIO  0x0080
> +/* don't open the backing file */
> +#define LIBQBLOCK_O_NO_BACKING  0x0100
> +/* disable flushing on this disk */
> +#define LIBQBLOCK_O_NO_FLUSH    0x0200
> +/* copy read backing sectors into image */
> +#define LIBQBLOCK_O_COPY_ON_READ 0x0400
> +/* consistency hint for incoming migration */
> +#define LIBQBLOCK_O_INCOMING    0x0800
> +
> +#define LIBQBLOCK_O_CACHE_MASK \
> +   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
> +
> +enum QBlockProtocol {
> +    QB_PROTO_FILE = 0,
> +    QB_PROTO_MAX
> +};
> +
> +enum QBlockFormat {
> +    QB_FMT_NONE = 0,
> +    QB_FMT_COW,
> +    QB_FMT_QED,
> +    QB_FMT_QCOW,
> +    QB_FMT_QCOW2,
> +    QB_FMT_RAW,
> +    QB_FMT_RBD,
> +    QB_FMT_SHEEPDOG,
> +    QB_FMT_VDI,
> +    QB_FMT_VMDK,
> +    QB_FMT_VPC,
> +    QB_FMT_MAX
> +};

Not sure if this is a good idea with respect to extensibility. Today you
only need to create a new block/foo.c and add it to the Makefile in
order to add a new format and protocol. It would be better if the
library could make use of it without changing these enums, e.g. by
referring to formats by string (possibly getting a struct referring to a
format by something like qblk_get_format("raw"))

> +
> +/* block target location info, it include all information about how to find
> +   the image */
> +struct QBlockLocInfo {
> +    int struct_size;
> +    const char *filename;
> +    enum QBlockProtocol protocol;
> +};

This is a relatively simplistic view of the world. It works okay for
local image files (which is probably the most common use case for the
library), but you get into trouble as soon as you want to build some
less trivial structures, like something including blkdebug.

Eventually, this leads straight into -blockdev discussions, so maybe we
should indeed go with a very simplistic approach for the start, but
always be aware that a more general solution is to be expected and
should be possible to fit in naturally.

> +
> +/* how to open the image */
> +struct QBlockOptionOpen {
> +    int struct_size;
> +    struct QBlockLocInfo o_loc; /* how to find */
> +    enum QBlockFormat o_fmt_type; /* how to extract */
> +    int o_flag; /* how to control */
> +};
> +
> +/*
> +   create a new QBlockOptionOpen structure.
> +   params:
> +     op: out, pointer that will receive created structure.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +static inline int qb_oo_new(struct QBlockOptionOpen **op)
> +{
> +    *op = calloc(1, sizeof(struct QBlockOptionOpen));
> +    if (*op == NULL) {
> +        return QB_ERR_MEM_ERR;
> +    }
> +    (*op)->struct_size = sizeof(struct QBlockOptionOpen);
> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
> +    return 0;
> +}
> +
> +/*
> +   free QBlockOptionOpen structure.
> +   params:
> +     op: in, *op will be set as NULL after called.
> +   return:
> +     void
> + */
> +static inline void qb_oo_free(struct QBlockOptionOpen **op)
> +{
> +    free(*op);
> +    *op = NULL;
> +}
> +
> +/*
> +   open a block object.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     op: in, options for open.
> +   return:
> +     0 on success, negative on fail.
> + */
> +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op);

No further explanation on what negative values there are allowed? In
practice I guess -errno, but will it be officially undefined?

Will there be a function that allows getting an error message if it has
failed?

> +
> +/*
> +   close a block object.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +   return:
> +     void.
> + */
> +void qb_close(struct QBlockState *qbs);

Does it imply a flush? Do we need an error return then?

> +
> +/* format related options, struct_size must be set. */
> +struct QBlockOption_cow {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +};
> +
> +struct QBlockOption_qed {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    enum QBlockFormat backing_fmt;
> +    int backing_flag;
> +    size_t cluster_size; /* unit is bytes */
> +    size_t table_size; /* unit is clusters */
> +};
> +
> +struct QBlockOption_qcow {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    bool encrypt;
> +};
> +
> +struct QBlockOption_qcow2 {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *compat_level; /* "Compatibility level (0.10 or 1.1)" */
> +    const char *backing_file;
> +    enum QBlockFormat backing_fmt;
> +    int backing_flag;
> +    bool encrypt;
> +    size_t cluster_size; /* unit is bytes */
> +    const char *prealloc_mode; /* off or metadata */
> +};
> +
> +struct QBlockOption_raw {
> +    int struct_size;
> +    size_t virt_size;
> +};
> +
> +struct QBlockOption_rbd {
> +    int struct_size;
> +    size_t virt_size;
> +    size_t cluster_size;
> +};
> +
> +struct QBlockOption_sheepdog {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    const char *prealloc_mode; /* off or full */
> +};
> +
> +struct QBlockOption_vdi {
> +    int struct_size;
> +    size_t virt_size;
> +    size_t cluster_size;
> +    bool prealloc_mode;
> +};
> +
> +struct QBlockOption_vmdk {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *backing_file;
> +    int backing_flag;
> +    bool compat_version6;
> +    const char *subfmt;
> +    /* vmdk flat extent format, values:
> +   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
> +    twoGbMaxExtentFlat | streamOptimized} */
> +};
> +
> +struct QBlockOption_vpc {
> +    int struct_size;
> +    size_t virt_size;
> +    const char *subfmt; /* "{dynamic (default) | fixed} " */
> +};

Ouch. :-)

But maybe it's the best we can do...

> +
> +union QBlockOption_fmt {
> +    struct QBlockOption_cow       o_cow;
> +    struct QBlockOption_qed       o_qed;
> +    struct QBlockOption_qcow      o_qcow;
> +    struct QBlockOption_qcow2     o_qcow2;
> +    struct QBlockOption_raw       o_raw;
> +    struct QBlockOption_rbd       o_rbd;
> +    struct QBlockOption_sheepdog  o_sheepdog;
> +    struct QBlockOption_vdi       o_vdi;
> +    struct QBlockOption_vmdk      o_vmdk;
> +    struct QBlockOption_vpc       o_vpc;
> +};
> +
> +struct QBlockOptionFormat {
> +    int struct_size;
> +    enum QBlockFormat fmt_type;
> +    union QBlockOption_fmt fmt_op;
> +};
> +
> +/* struct_size in o_loc and o_fmt must set. To make this structure extensible,
> +   all new member must be added in the tail of each structure. */
> +struct QBlockOptionCreate {
> +    int struct_size;

size_t?

> +    struct QBlockLocInfo o_loc;
> +    struct QBlockOptionFormat o_fmt;
> +};
> +
> +/*
> +   create a new QBlockOptionCreate structure.
> +   params:
> +     op: out, pointer that will receive created structure.
> +     fmt: format want to use.
> +   return:
> +     0 on succeed, negative on failure.
> + */
> +static inline int qb_oc_new(struct QBlockOptionCreate **op,
> +                            enum QBlockFormat fmt)
> +{
> +    *op = calloc(1, sizeof(struct QBlockOptionCreate));
> +    if (*op == NULL) {
> +        return QB_ERR_MEM_ERR;
> +    }
> +    (*op)->struct_size = sizeof(struct QBlockOptionCreate);
> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
> +    (*op)->o_fmt.struct_size = sizeof(struct QBlockOptionFormat);
> +
> +    switch (fmt) {
> +    case QB_FMT_COW:
> +        (*op)->o_fmt.fmt_op.o_cow.struct_size =
> +                                          sizeof(struct QBlockOption_cow);
> +        break;
> +    case QB_FMT_QED:
> +        (*op)->o_fmt.fmt_op.o_qed.struct_size =
> +                                          sizeof(struct QBlockOption_qed);
> +        break;
> +    case QB_FMT_QCOW:
> +        (*op)->o_fmt.fmt_op.o_qcow.struct_size =
> +                                          sizeof(struct QBlockOption_qcow);
> +        break;
> +    case QB_FMT_QCOW2:
> +        (*op)->o_fmt.fmt_op.o_qcow2.struct_size =
> +                                          sizeof(struct QBlockOption_qcow2);
> +        break;
> +    case QB_FMT_RAW:
> +        (*op)->o_fmt.fmt_op.o_raw.struct_size =
> +                                          sizeof(struct QBlockOption_raw);
> +        break;
> +    case QB_FMT_RBD:
> +        (*op)->o_fmt.fmt_op.o_rbd.struct_size =
> +                                          sizeof(struct QBlockOption_rbd);
> +        break;
> +    case QB_FMT_SHEEPDOG:
> +        (*op)->o_fmt.fmt_op.o_sheepdog.struct_size =
> +                                          sizeof(struct QBlockOption_sheepdog);
> +        break;
> +    case QB_FMT_VDI:
> +        (*op)->o_fmt.fmt_op.o_vdi.struct_size =
> +                                          sizeof(struct QBlockOption_vdi);
> +        break;
> +    case QB_FMT_VMDK:
> +        (*op)->o_fmt.fmt_op.o_vmdk.struct_size =
> +                                          sizeof(struct QBlockOption_vmdk);
> +        break;
> +    case QB_FMT_VPC:
> +        (*op)->o_fmt.fmt_op.o_vpc.struct_size =
> +                                          sizeof(struct QBlockOption_vpc);
> +        break;
> +    default:
> +        break;
> +    }
> +    (*op)->o_fmt.fmt_type = fmt;
> +    return 0;
> +}
> +
> +/*
> +   free QBlockOptionCreate structure.
> +   params:
> +     op: in, *op will be set as NULL after called.
> +   return:
> +     void
> + */
> +static inline void qb_oc_free(struct QBlockOptionCreate **op)
> +{
> +    free(*op);
> +    *op = NULL;
> +}
> +/*
> +   create a block file.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     op: in, create option.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op);
> +
> +
> +/* sync access */
> +/*
> +   block sync read.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     buf: out, buffer that receive the content.
> +     len: in, length to read.
> +     offset: in, offset in the block data.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
> +                                                      off_t offset);
> +
> +/*
> +   block sync read.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     buf: in, buffer that would be wrote.
> +     len: in, length to write.
> +     offset: in, offset in the block data.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
> +                                                       off_t offset);
> +
> +/*
> +   block sync flush.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_flush(struct QBlockState *qbs);
> +
> +/* information */
> +/* image related info, static information, from user perspective. */
> +/* now it is a plain structure, wonder if it could be foldered into embbed one
> +   to reflect that format related information better. */
> +struct QBlockInfoImage {
> +    int struct_size;
> +    char *filename;
> +    enum QBlockProtocol protocol;
> +    enum QBlockFormat format;
> +    size_t virt_size;
> +    /* advance info */
> +    size_t allocated_size;
> +    bool encrypt;
> +    char *backing_filename;
> +};

What about things like cluster size, dirty state etc. that only some
image formats have?

> +
> +/* image info */
> +/*
> +   get image info.
> +   params:
> +     qbs: in, pointer to QBlockState.
> +     info, out, pointer that would receive the information.
> +   return:
> +     negative on fail, 0 on success.
> + */
> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);
> +
> +/*
> +   free image info.
> +   params:
> +     info, in, pointer, *info would be set to NULL after function called.
> +   return:
> +     void.
> + */
> +void qb_infoimage_free(struct QBlockInfoImage **info);
> +
> +/* misc */
> +bool qb_supports_format(enum QBlockFormat fmt);
> +bool qb_supports_protocol(enum QBlockProtocol proto);
> +
> +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);

This needs a more detailed description. I also think that you'll want to
add a const char** parameter for human readable error messages (as
printed with qerror_report or error_set in qemu).

Kevin
Wayne Xia Aug. 13, 2012, 10:27 a.m. UTC | #5
于 2012-8-10 18:48, Paolo Bonzini 写道:
> Il 09/08/2012 12:12, Wenchao Xia ha scritto:
>> +/* copy information and take care the member difference in differect version.
>> +   Assuming all new member are added in the tail, struct size is the first
>> +   member, this is old to new version, src have its struct_size set. */
>> +static void qboc_adjust_o2n(struct QBlockOptionCreate *dest,
>> +                            struct QBlockOptionCreate *src)
>> +{
>> +    /* for simple it does memcpy now, need to take care of embbed structure */
>> +    memcpy(dest, src, src->struct_size);
>
> You need an assertion that src->struct_size < sizeof(*dest).
>
> However, the structure size perhaps wasn't my brightest idea ever.  But
> still thanks for preparing this prototype!  Now that we have a more
> complete API to discuss, we can iterate to something better.
>
>
>> +        assert(0 == set_option_parameter_int(param,
>> +                                BLOCK_OPT_SIZE, o_cow->virt_size));
>
> Assertions should not have side effects.
>
    what side effects would this line have?

>
>> diff --git a/libqblock.h b/libqblock.h
>> new file mode 100644
>> index 0000000..d2e9502
>> --- /dev/null
>> +++ b/libqblock.h
>> @@ -0,0 +1,447 @@
>> +/*
>> + * Copyright IBM Corp. 2012
>> + *
>> + * Authors:
>> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
>> + */
>> +
>> +#ifndef LIBQBLOCK_H
>> +#define LIBQBLOCK_H
>> +
>> +#include <stdio.h>
>> +#include <stdint.h>
>> +#include <stdlib.h>
>> +
>> +#define bool _Bool
>> +
>> +#define QB_ERR_MEM_ERR (-1)
>> +#define QB_ERR_INTERNAL_ERR (-2)
>> +#define QB_ERR_INVALID_PARAM (-3)
>> +
>> +/* this library is designed around this core struct. */
>> +struct QBlockState;
>> +
>> +/*
>> +   libarary init
>> +   This function get the library ready to use.
>> + */
>> +void libqblock_init(void);
>> +
>> +/*
>> +   create a new qbs object
>> +   params:
>> +     qbs: out, pointer that will receive created obj.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +int qb_state_new(struct QBlockState **qbs);
>> +
>> +/*
>> +   delete a qbs object
>> +   params:
>> +     qbs: in, pointer that will be freed. *qbs will be set to NULL.
>> +   return:
>> +     void.
>> + */
>> +void qb_state_free(struct QBlockState **qbs);
>> +
>> +
>> +/* flag used in open and create */
>> +#define LIBQBLOCK_O_RDWR        0x0002
>> +/* open the file read only and save writes in a snapshot */
>> +#define LIBQBLOCK_O_SNAPSHOT    0x0008
>
> I'd rather avoid exposing this for now.
>
>> +/* do not use the host page cache */
>> +#define LIBQBLOCK_O_NOCACHE     0x0020
>> +/* use write-back caching */
>> +#define LIBQBLOCK_O_CACHE_WB    0x0040
>> +/* use native AIO instead of the thread pool */
>> +#define LIBQBLOCK_O_NATIVE_AIO  0x0080
>
> NATIVE_AIO should be an option for the file protocol.  But it's mostly
> for performance and since we only support synchronous I/O we can drop it
> for now.
>
>
>> +/* don't open the backing file */
>> +#define LIBQBLOCK_O_NO_BACKING  0x0100
>> +/* disable flushing on this disk */
>> +#define LIBQBLOCK_O_NO_FLUSH    0x0200
>> +/* copy read backing sectors into image */
>> +#define LIBQBLOCK_O_COPY_ON_READ 0x0400
>
> I'd rather avoid exposing this for now too.
>
>> +/* consistency hint for incoming migration */
>> +#define LIBQBLOCK_O_INCOMING    0x0800
>
> This is internal to QEMU.
>
> Please add a mask of valid bits and an assertion that unknown bits are zero.
>

   OK to hide this flag now. Do you think this flag belongs to the
control flag or I should sort them  to anther option? In long term the
library should provide full capablities as qemu block layer have so
qemu can try migrate to it, so now I need to make sure the API designed
  have left room for all features qemu have support.

>> +
>> +#define LIBQBLOCK_O_CACHE_MASK \
>> +   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
>> +
>> +enum QBlockProtocol {
>> +    QB_PROTO_FILE = 0,
>> +    QB_PROTO_MAX
>> +};
>> +
>> +enum QBlockFormat {
>> +    QB_FMT_NONE = 0,
>> +    QB_FMT_COW,
>> +    QB_FMT_QED,
>> +    QB_FMT_QCOW,
>> +    QB_FMT_QCOW2,
>> +    QB_FMT_RAW,
>> +    QB_FMT_RBD,
>> +    QB_FMT_SHEEPDOG,
>> +    QB_FMT_VDI,
>> +    QB_FMT_VMDK,
>> +    QB_FMT_VPC,
>> +    QB_FMT_MAX
>> +};
>> +
>> +/* block target location info, it include all information about how to find
>> +   the image */
>> +struct QBlockLocInfo {
>> +    int struct_size;
>> +    const char *filename;
>> +    enum QBlockProtocol protocol;
>> +};
>> +
>> +/* how to open the image */
>> +struct QBlockOptionOpen {
>> +    int struct_size;
>> +    struct QBlockLocInfo o_loc; /* how to find */
>> +    enum QBlockFormat o_fmt_type; /* how to extract */
>> +    int o_flag; /* how to control */
>> +};
>
> You are right that the embedded structs are very complicated.  I think
> we need to find the right balance between structs for extensibility and
> function arguments for ease of use.
>
> For example, we could have
>
> int qb_open(struct QBlockState *qbs,
>              struct QBlockLocation *loc,
>              struct QBlockFormatOption *op,
>              int o_flag);
>
> where:
>
> - QBlockLocation is basically your QBlockLocInfo, but restructured to
> use unions for the protocol-specific fields.
>
> - QBlockFormatOption is basically your QBlockOptionFormat struct.  Thus
> we can plan for specifying format-specific options at open time rather
> than just at create time (this can be useful, for example, for things
> such as lazy_refcounts).  Until support is there in the block layer, we
> can simply check that all format-specific options are zero.
>
> Since both QBlockLocation and QBlockFormatOption are basically a
> discriminated record (enum + union of structs) we can add a large char
> member to the union (e.g. char padding[512]) to keep the ABI stable.
> Users must zero out all fields, and future additions must ensure that
> the default value is represented by all-zeros.
>
>> +    const char *prealloc_mode; /* off or metadata */
>
> Make this an enum.
>
>> +    bool prealloc_mode;
>
> Here too.
>
>> +};
>> +
>> +struct QBlockOption_vmdk {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    bool compat_version6;
>> +    const char *subfmt;
>> +    /* vmdk flat extent format, values:
>> +   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
>> +    twoGbMaxExtentFlat | streamOptimized} */
>
> Here too.
>
>> +/* information */
>> +/* image related info, static information, from user perspective. */
>> +/* now it is a plain structure, wonder if it could be foldered into embbed one
>> +   to reflect that format related information better. */
>> +struct QBlockInfoImage {
>
> QBlockImageInfo
>
>> +    int struct_size;
>
> Here the struct size makes more sense, because it's a returned struct.
>
>> +    char *filename;
>> +    enum QBlockProtocol protocol;
>
> Can this just be a struct QBlockLocation * (using a pointer avoids
> problems with embedded structs)?
>
>> +    enum QBlockFormat format;
>
> Can this just be a struct QBlockFormatOption * (same as the above)?
>
   Yes, I tried it before but found that structure alignment is hard to
solve, but as you suggest, I think plain structure plus pointer could
make it simpler. Blue Swirl suggest using a API to unify property
setting and solve version difference, not using structure, as:
  qb_set_property(s, "filename", "c:\\\\autoexec.bat");
  What do you think of that way?
>> +    size_t virt_size;
>> +    /* advance info */
>> +    size_t allocated_size;
>> +    bool encrypt;
>> +    char *backing_filename;
>> +};
>> +
>> +/* image info */
>> +/*
>> +   get image info.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     info, out, pointer that would receive the information.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);
>
> qb_get_image_info
>
   OK.

>> +
>> +/*
>> +   free image info.
>> +   params:
>> +     info, in, pointer, *info would be set to NULL after function called.
>> +   return:
>> +     void.
>> + */
>> +void qb_infoimage_free(struct QBlockInfoImage **info);
>
> qb_free_image_info
>
> Paolo
>
>> +
>> +/* misc */
>> +bool qb_supports_format(enum QBlockFormat fmt);
>> +bool qb_supports_protocol(enum QBlockProtocol proto);
>> +
>> +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);
>> +#endif
>>
>
>
Wayne Xia Aug. 13, 2012, 11:20 a.m. UTC | #6
于 2012-8-10 19:02, Kevin Wolf 写道:
> Am 09.08.2012 12:12, schrieb Wenchao Xia:
>>    This patch is the API design.
>>
>> Signed-off-by: Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> ---
>>   libqblock.c |  670 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>   libqblock.h |  447 +++++++++++++++++++++++++++++++++++++++
>>   2 files changed, 1117 insertions(+), 0 deletions(-)
>>   create mode 100644 libqblock.c
>>   create mode 100644 libqblock.h
>
> Ignoring the implementation for now as the design should be visible in
> the header file.
>
>> diff --git a/libqblock.h b/libqblock.h
>> new file mode 100644
>> index 0000000..d2e9502
>> --- /dev/null
>> +++ b/libqblock.h
>> @@ -0,0 +1,447 @@
>> +/*
>> + * Copyright IBM Corp. 2012
>> + *
>> + * Authors:
>> + *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
>> + *
>> + * This library is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * This library is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with this library; if not, write to the Free Software
>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
>> + */
>> +
>> +#ifndef LIBQBLOCK_H
>> +#define LIBQBLOCK_H
>> +
>> +#include <stdio.h>
>> +#include <stdint.h>
>> +#include <stdlib.h>
>> +
>> +#define bool _Bool
>
> Why not use stdbool.h?
>
   forgot to use that, will change it, thanks.


>> +
>> +#define QB_ERR_MEM_ERR (-1)
>> +#define QB_ERR_INTERNAL_ERR (-2)
>> +#define QB_ERR_INVALID_PARAM (-3)
>
> qemu uses errno values internally, I think they would make sense in the
> library interface as well.
>
>> +
>> +/* this library is designed around this core struct. */
>> +struct QBlockState;
>> +
>> +/*
>> +   libarary init
>> +   This function get the library ready to use.
>> + */
>> +void libqblock_init(void);
>> +
>> +/*
>> +   create a new qbs object
>> +   params:
>> +     qbs: out, pointer that will receive created obj.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +int qb_state_new(struct QBlockState **qbs);
>> +
>> +/*
>> +   delete a qbs object
>> +   params:
>> +     qbs: in, pointer that will be freed. *qbs will be set to NULL.
>> +   return:
>> +     void.
>> + */
>> +void qb_state_free(struct QBlockState **qbs);
>
> What happens if the qbs is open? Will it be flushed and closed? If so,
> can it fail and we need to allow an error return?
>
     user should call qb_close() if he have called qb_open(), other wise
unexpected thing happens such as this file is not flushed. I need
document this in the comments.

>> +
>> +/* flag used in open and create */
>> +#define LIBQBLOCK_O_RDWR        0x0002
>> +/* open the file read only and save writes in a snapshot */
>> +#define LIBQBLOCK_O_SNAPSHOT    0x0008
>> +/* do not use the host page cache */
>> +#define LIBQBLOCK_O_NOCACHE     0x0020
>> +/* use write-back caching */
>> +#define LIBQBLOCK_O_CACHE_WB    0x0040
>> +/* use native AIO instead of the thread pool */
>> +#define LIBQBLOCK_O_NATIVE_AIO  0x0080
>> +/* don't open the backing file */
>> +#define LIBQBLOCK_O_NO_BACKING  0x0100
>> +/* disable flushing on this disk */
>> +#define LIBQBLOCK_O_NO_FLUSH    0x0200
>> +/* copy read backing sectors into image */
>> +#define LIBQBLOCK_O_COPY_ON_READ 0x0400
>> +/* consistency hint for incoming migration */
>> +#define LIBQBLOCK_O_INCOMING    0x0800
>> +
>> +#define LIBQBLOCK_O_CACHE_MASK \
>> +   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
>> +
>> +enum QBlockProtocol {
>> +    QB_PROTO_FILE = 0,
>> +    QB_PROTO_MAX
>> +};
>> +
>> +enum QBlockFormat {
>> +    QB_FMT_NONE = 0,
>> +    QB_FMT_COW,
>> +    QB_FMT_QED,
>> +    QB_FMT_QCOW,
>> +    QB_FMT_QCOW2,
>> +    QB_FMT_RAW,
>> +    QB_FMT_RBD,
>> +    QB_FMT_SHEEPDOG,
>> +    QB_FMT_VDI,
>> +    QB_FMT_VMDK,
>> +    QB_FMT_VPC,
>> +    QB_FMT_MAX
>> +};
>
> Not sure if this is a good idea with respect to extensibility. Today you
> only need to create a new block/foo.c and add it to the Makefile in
> order to add a new format and protocol. It would be better if the
> library could make use of it without changing these enums, e.g. by
> referring to formats by string (possibly getting a struct referring to a
> format by something like qblk_get_format("raw"))
>
   qblk_get_format("raw") seems a good way solve the option difference,
maybe user call it as:
   void *op = qblk_get_format_option("raw");
   struct QBlockOption_raw *o_raw = (struct QBlockOption_raw *)op;
   o_raw.virt_size = 16 * 1024;

   I think this is good, with the cost of user have to deal string
instead of simpler enum integer.

>> +
>> +/* block target location info, it include all information about how to find
>> +   the image */
>> +struct QBlockLocInfo {
>> +    int struct_size;
>> +    const char *filename;
>> +    enum QBlockProtocol protocol;
>> +};
>
> This is a relatively simplistic view of the world. It works okay for
> local image files (which is probably the most common use case for the
> library), but you get into trouble as soon as you want to build some
> less trivial structures, like something including blkdebug.
>
> Eventually, this leads straight into -blockdev discussions, so maybe we
> should indeed go with a very simplistic approach for the start, but
> always be aware that a more general solution is to be expected and
> should be possible to fit in naturally.
>
   if blkdebug is used for debug block I/O, maybe we can sort it into
another protocol.

>> +
>> +/* how to open the image */
>> +struct QBlockOptionOpen {
>> +    int struct_size;
>> +    struct QBlockLocInfo o_loc; /* how to find */
>> +    enum QBlockFormat o_fmt_type; /* how to extract */
>> +    int o_flag; /* how to control */
>> +};
>> +
>> +/*
>> +   create a new QBlockOptionOpen structure.
>> +   params:
>> +     op: out, pointer that will receive created structure.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +static inline int qb_oo_new(struct QBlockOptionOpen **op)
>> +{
>> +    *op = calloc(1, sizeof(struct QBlockOptionOpen));
>> +    if (*op == NULL) {
>> +        return QB_ERR_MEM_ERR;
>> +    }
>> +    (*op)->struct_size = sizeof(struct QBlockOptionOpen);
>> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
>> +    return 0;
>> +}
>> +
>> +/*
>> +   free QBlockOptionOpen structure.
>> +   params:
>> +     op: in, *op will be set as NULL after called.
>> +   return:
>> +     void
>> + */
>> +static inline void qb_oo_free(struct QBlockOptionOpen **op)
>> +{
>> +    free(*op);
>> +    *op = NULL;
>> +}
>> +
>> +/*
>> +   open a block object.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     op: in, options for open.
>> +   return:
>> +     0 on success, negative on fail.
>> + */
>> +int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op);
>
> No further explanation on what negative values there are allowed? In
> practice I guess -errno, but will it be officially undefined?
>
> Will there be a function that allows getting an error message if it has
> failed?
>
     Yes, there is a API doint that.

>> +
>> +/*
>> +   close a block object.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +   return:
>> +     void.
>> + */
>> +void qb_close(struct QBlockState *qbs);
>
> Does it imply a flush? Do we need an error return then?
>
    No flush now, will flush and document it next time.

>> +
>> +/* format related options, struct_size must be set. */
>> +struct QBlockOption_cow {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +};
>> +
>> +struct QBlockOption_qed {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    enum QBlockFormat backing_fmt;
>> +    int backing_flag;
>> +    size_t cluster_size; /* unit is bytes */
>> +    size_t table_size; /* unit is clusters */
>> +};
>> +
>> +struct QBlockOption_qcow {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    bool encrypt;
>> +};
>> +
>> +struct QBlockOption_qcow2 {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *compat_level; /* "Compatibility level (0.10 or 1.1)" */
>> +    const char *backing_file;
>> +    enum QBlockFormat backing_fmt;
>> +    int backing_flag;
>> +    bool encrypt;
>> +    size_t cluster_size; /* unit is bytes */
>> +    const char *prealloc_mode; /* off or metadata */
>> +};
>> +
>> +struct QBlockOption_raw {
>> +    int struct_size;
>> +    size_t virt_size;
>> +};
>> +
>> +struct QBlockOption_rbd {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    size_t cluster_size;
>> +};
>> +
>> +struct QBlockOption_sheepdog {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    const char *prealloc_mode; /* off or full */
>> +};
>> +
>> +struct QBlockOption_vdi {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    size_t cluster_size;
>> +    bool prealloc_mode;
>> +};
>> +
>> +struct QBlockOption_vmdk {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *backing_file;
>> +    int backing_flag;
>> +    bool compat_version6;
>> +    const char *subfmt;
>> +    /* vmdk flat extent format, values:
>> +   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
>> +    twoGbMaxExtentFlat | streamOptimized} */
>> +};
>> +
>> +struct QBlockOption_vpc {
>> +    int struct_size;
>> +    size_t virt_size;
>> +    const char *subfmt; /* "{dynamic (default) | fixed} " */
>> +};
>
> Ouch. :-)
>
> But maybe it's the best we can do...
>
   This structure brought format specified options to the surface,
user could understand them easier and simply give values to the
members, so this is a friendly API. But with a union implement became
hard, I think the way your suggested as
qblk_format_option_get("raw")
could make thing easier.

>> +
>> +union QBlockOption_fmt {
>> +    struct QBlockOption_cow       o_cow;
>> +    struct QBlockOption_qed       o_qed;
>> +    struct QBlockOption_qcow      o_qcow;
>> +    struct QBlockOption_qcow2     o_qcow2;
>> +    struct QBlockOption_raw       o_raw;
>> +    struct QBlockOption_rbd       o_rbd;
>> +    struct QBlockOption_sheepdog  o_sheepdog;
>> +    struct QBlockOption_vdi       o_vdi;
>> +    struct QBlockOption_vmdk      o_vmdk;
>> +    struct QBlockOption_vpc       o_vpc;
>> +};
>> +
>> +struct QBlockOptionFormat {
>> +    int struct_size;
>> +    enum QBlockFormat fmt_type;
>> +    union QBlockOption_fmt fmt_op;
>> +};
>> +
>> +/* struct_size in o_loc and o_fmt must set. To make this structure extensible,
>> +   all new member must be added in the tail of each structure. */
>> +struct QBlockOptionCreate {
>> +    int struct_size;
>
> size_t?
>
   struct_size will not be big, should I also use size_t?

>> +    struct QBlockLocInfo o_loc;
>> +    struct QBlockOptionFormat o_fmt;
>> +};
>> +
>> +/*
>> +   create a new QBlockOptionCreate structure.
>> +   params:
>> +     op: out, pointer that will receive created structure.
>> +     fmt: format want to use.
>> +   return:
>> +     0 on succeed, negative on failure.
>> + */
>> +static inline int qb_oc_new(struct QBlockOptionCreate **op,
>> +                            enum QBlockFormat fmt)
>> +{
>> +    *op = calloc(1, sizeof(struct QBlockOptionCreate));
>> +    if (*op == NULL) {
>> +        return QB_ERR_MEM_ERR;
>> +    }
>> +    (*op)->struct_size = sizeof(struct QBlockOptionCreate);
>> +    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
>> +    (*op)->o_fmt.struct_size = sizeof(struct QBlockOptionFormat);
>> +
>> +    switch (fmt) {
>> +    case QB_FMT_COW:
>> +        (*op)->o_fmt.fmt_op.o_cow.struct_size =
>> +                                          sizeof(struct QBlockOption_cow);
>> +        break;
>> +    case QB_FMT_QED:
>> +        (*op)->o_fmt.fmt_op.o_qed.struct_size =
>> +                                          sizeof(struct QBlockOption_qed);
>> +        break;
>> +    case QB_FMT_QCOW:
>> +        (*op)->o_fmt.fmt_op.o_qcow.struct_size =
>> +                                          sizeof(struct QBlockOption_qcow);
>> +        break;
>> +    case QB_FMT_QCOW2:
>> +        (*op)->o_fmt.fmt_op.o_qcow2.struct_size =
>> +                                          sizeof(struct QBlockOption_qcow2);
>> +        break;
>> +    case QB_FMT_RAW:
>> +        (*op)->o_fmt.fmt_op.o_raw.struct_size =
>> +                                          sizeof(struct QBlockOption_raw);
>> +        break;
>> +    case QB_FMT_RBD:
>> +        (*op)->o_fmt.fmt_op.o_rbd.struct_size =
>> +                                          sizeof(struct QBlockOption_rbd);
>> +        break;
>> +    case QB_FMT_SHEEPDOG:
>> +        (*op)->o_fmt.fmt_op.o_sheepdog.struct_size =
>> +                                          sizeof(struct QBlockOption_sheepdog);
>> +        break;
>> +    case QB_FMT_VDI:
>> +        (*op)->o_fmt.fmt_op.o_vdi.struct_size =
>> +                                          sizeof(struct QBlockOption_vdi);
>> +        break;
>> +    case QB_FMT_VMDK:
>> +        (*op)->o_fmt.fmt_op.o_vmdk.struct_size =
>> +                                          sizeof(struct QBlockOption_vmdk);
>> +        break;
>> +    case QB_FMT_VPC:
>> +        (*op)->o_fmt.fmt_op.o_vpc.struct_size =
>> +                                          sizeof(struct QBlockOption_vpc);
>> +        break;
>> +    default:
>> +        break;
>> +    }
>> +    (*op)->o_fmt.fmt_type = fmt;
>> +    return 0;
>> +}
>> +
     This is what make implement hard, with format union, in order to
form a friendly API.


>> +/*
>> +   free QBlockOptionCreate structure.
>> +   params:
>> +     op: in, *op will be set as NULL after called.
>> +   return:
>> +     void
>> + */
>> +static inline void qb_oc_free(struct QBlockOptionCreate **op)
>> +{
>> +    free(*op);
>> +    *op = NULL;
>> +}
>> +/*
>> +   create a block file.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     op: in, create option.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op);
>> +
>> +
>> +/* sync access */
>> +/*
>> +   block sync read.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     buf: out, buffer that receive the content.
>> +     len: in, length to read.
>> +     offset: in, offset in the block data.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
>> +                                                      off_t offset);
>> +
>> +/*
>> +   block sync read.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     buf: in, buffer that would be wrote.
>> +     len: in, length to write.
>> +     offset: in, offset in the block data.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
>> +                                                       off_t offset);
>> +
>> +/*
>> +   block sync flush.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_flush(struct QBlockState *qbs);
>> +
>> +/* information */
>> +/* image related info, static information, from user perspective. */
>> +/* now it is a plain structure, wonder if it could be foldered into embbed one
>> +   to reflect that format related information better. */
>> +struct QBlockInfoImage {
>> +    int struct_size;
>> +    char *filename;
>> +    enum QBlockProtocol protocol;
>> +    enum QBlockFormat format;
>> +    size_t virt_size;
>> +    /* advance info */
>> +    size_t allocated_size;
>> +    bool encrypt;
>> +    char *backing_filename;
>> +};
>
> What about things like cluster size, dirty state etc. that only some
> image formats have?
>
    hard to resolve, enum plus sub format structure seems reasonable. My
plan is using embbed structure:
struct QBlockInfoImage {
    int struct_size;
    struct QBlockInfoLoc *i_loc;
    struct QBlockInfoFormat *i_fmt;
};
   with format string user got, user do a cast to specified format info.

>> +
>> +/* image info */
>> +/*
>> +   get image info.
>> +   params:
>> +     qbs: in, pointer to QBlockState.
>> +     info, out, pointer that would receive the information.
>> +   return:
>> +     negative on fail, 0 on success.
>> + */
>> +int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);
>> +
>> +/*
>> +   free image info.
>> +   params:
>> +     info, in, pointer, *info would be set to NULL after function called.
>> +   return:
>> +     void.
>> + */
>> +void qb_infoimage_free(struct QBlockInfoImage **info);
>> +
>> +/* misc */
>> +bool qb_supports_format(enum QBlockFormat fmt);
>> +bool qb_supports_protocol(enum QBlockProtocol proto);
>> +
>> +const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);
>
> This needs a more detailed description. I also think that you'll want to
> add a const char** parameter for human readable error messages (as
> printed with qerror_report or error_set in qemu).
>
   This API try to return human readable strings. Will add document.

> Kevin
>
Andreas Färber Aug. 13, 2012, 5:38 p.m. UTC | #7
Am 13.08.2012 12:27, schrieb Wenchao Xia:
> 于 2012-8-10 18:48, Paolo Bonzini 写道:
>> Il 09/08/2012 12:12, Wenchao Xia ha scritto:
>>> +        assert(0 == set_option_parameter_int(param,
>>> +                                BLOCK_OPT_SIZE, o_cow->virt_size));
>>
>> Assertions should not have side effects.
>>
>    what side effects would this line have?

It sets some BLOCK_OPT_SIZE parameter. :)

What Paolo probably meant is to extract that part from the assert():
ret = set_option_...(...);
assert(ret == 0);
eliminating the kraxelism at the same time. ;)
Then you can more safely do, e.g., #define assert(x) do {} while (0)

Regards,
Andreas
Wayne Xia Aug. 15, 2012, 8:12 a.m. UTC | #8
Hi,
   according to the comments, the header files were adjusted as
following:
   1 info are sorted into two kinds of structure: location and format,
which are both union + structure type.
   2 removed size_struct. Although it is suggested to use pointer to
make embbed struct easier, but with union it requires memory allocated
at create time, for eg:
struct QBlockOption_fmt_qed {
     size_t virt_size;
     struct QBlockOptionLoc *backing_loc;
};
union QBlockOption_fmt {
     struct QBlockOption_fmt_cow       o_cow;
     struct QBlockOption_fmt_qed       o_qed;
};
   when user create union QBlockOption_fmt, he must set format type
first so we can malloc that memory regions to the pointer. This also
bring risk that at free time the format type is also need to be set
correctly to free the correct pointer. So this version using reserved
space and the structures are created with xx_new functions internally.
   3 Balanced the parameters extension and folder depth, More than one
parameters were used in APIs.
   4 advanced information that only some format have, will get -1 to
indicate it do not exist.

/*
  * QEMU block layer library
  *
  * Copyright IBM, Corp. 2012
  *
  * Authors:
  *  Wenchao Xia   <xiawenc@linux.vnet.ibm.com>
  *
  * This work is licensed under the terms of the GNU LGPL, version 2 or 
later.
  * See the COPYING.LIB file in the top-level directory.
  *
  */

#ifndef LIBQBLOCK_H
#define LIBQBLOCK_H

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>

#define QB_ERR_MEM_ERR (-1)
#define QB_ERR_INTERNAL_ERR (-2)
#define QB_ERR_INVALID_PARAM (-3)

/* this library is designed around this core struct. */
struct QBlockState;

/**
  * libqblock_init: Initialize the library
  */
void libqblock_init(void);

/**
  * qb_state_new: allocate a new QBloctState struct
  *
  * Following qblock action were based on this struct
  *
  * Returns 0 if succeed, negative value on fail.
  *
  * @qbs: used to receive the created struct.
  */
int qb_state_new(struct QBlockState **qbs);

/**
  * qb_state_free: free a QBloctState struct
  *
  * if it is opened, a qb_close must be called before free.
  *
  * @qbs: pointer to the struct's pointer.
  */
void qb_state_free(struct QBlockState **qbs);


/* flag used in open and create */
#define LIBQBLOCK_O_RDWR        0x0002
/* do not use the host page cache */
#define LIBQBLOCK_O_NOCACHE     0x0020
/* use write-back caching */
#define LIBQBLOCK_O_CACHE_WB    0x0040
/* don't open the backing file */
#define LIBQBLOCK_O_NO_BACKING  0x0100
/* disable flushing on this disk */
#define LIBQBLOCK_O_NO_FLUSH    0x0200

#define LIBQBLOCK_O_CACHE_MASK \
    (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)

#define LIBQBLOCK_O_VALID_MASK \
    (LIBQBLOCK_O_RDWR | LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | \
     LIBQBLOCK_O_NO_BACKING | LIBQBLOCK_O_NO_FLUSH)

enum QBlockProtocol {
     QB_PROTO_FILE = 0,
     QB_PROTO_MAX
};

enum QBlockFormat {
     QB_FMT_NONE = 0,
     QB_FMT_COW,
     QB_FMT_QED,
     QB_FMT_QCOW,
     QB_FMT_QCOW2,
     QB_FMT_RAW,
     QB_FMT_RBD,
     QB_FMT_SHEEPDOG,
     QB_FMT_VDI,
     QB_FMT_VMDK,
     QB_FMT_VPC,
     QB_FMT_MAX
};

struct QBlockOption_prot_file {
     char *filename;
};

union QBlockOption_prot {
     struct QBlockOption_prot_file       o_file;
};

/**
  * struct QBlockOptionLoc: contains information about how to find the image
  *
  * @prot_type: protocol type, now only support FILE.
  * @prot_op: protocol related options.
  */
struct QBlockOptionLoc {
     enum QBlockProtocol prot_type;
     union QBlockOption_prot prot_op;
     uint8 reserved[512];
};

/**
  * qb_ol_new: create a new struct QBlockOptionLoc.
  *
  * return 0 on success, negative on fail.
  *
  * @op: pointer to receive the new created one.
  */
int qb_ol_new(struct QBlockOptionLoc **op);

/**
  * qb_ol_free: free a struct QBlockOptionLoc.
  *
  * @op: pointer to the object, *op would be set to NULL.
  */
void qb_ol_free(struct QBlockOptionLoc **op);


/* format related options */
struct QBlockOption_fmt_cow {
     size_t virt_size;
     struct QBlockOptionLoc backing_loc;
};

struct QBlockOption_fmt_qed {
     size_t virt_size;
     struct QBlockOptionLoc backing_loc;
     enum QBlockFormat backing_fmt;
     size_t cluster_size; /* unit is bytes */
     size_t table_size; /* unit is clusters */
};

struct QBlockOption_fmt_qcow {
     size_t virt_size;
     struct QBlockOptionLoc backing_loc;
     bool encrypt;
};

/* "Compatibility level (0.10 or 1.1)" */
enum QBlockOption_fmt_qcow2_cpt {
     QBO_FMT_QCOW2_CPT_None = 0,
     QBO_FMT_QCOW2_CPT_V010,
     QBO_FMT_QCOW2_CPT_V011,
};

/* off or metadata */
enum QBlockOption_fmt_qcow2_prealloc {
     QBO_FMT_QCOW2_PREALLOC_OFF = 0,
     QBO_FMT_QCOW2_PREALLOC_METADATA,
};

struct QBlockOption_fmt_qcow2 {
     size_t virt_size;
     struct QBlockOptionLoc backing_loc;
     enum QBlockFormat backing_fmt;
     bool encrypt;
     size_t cluster_size; /* unit is bytes */
     enum QBlockOption_fmt_qcow2_cpt cpt_lv;
     enum QBlockOption_fmt_qcow2_prealloc pre_mode;
};

struct QBlockOption_fmt_raw {
     size_t virt_size;
};

struct QBlockOption_fmt_rbd {
     size_t virt_size;
     size_t cluster_size;
};

/* off or full */
enum QBlockOption_fmt_sheepdog_prealloc {
     QBO_FMT_SD_PREALLOC_OFF = 0,
     QBO_FMT_SD_PREALLOC_FULL,
};

struct QBlockOption_fmt_sheepdog {
     size_t virt_size;
     struct QBlockOptionLoc backing_loc;
     enum QBlockOption_fmt_sheepdog_prealloc pre_mode;
};

enum QBlockOption_fmt_vdi_prealloc {
     QBO_FMT_VDI_PREALLOC_FALSE = 0,
     QBO_FMT_VDI_PREALLOC_TRUE,
};

struct QBlockOption_fmt_vdi {
     size_t virt_size;
     size_t cluster_size;
     enum QBlockOption_fmt_vdi_prealloc pre_mode;
};

/* whether compact to vmdk verion 6 */
enum QBlockOption_fmt_vmdk_cpt {
     QBO_FMT_VMDK_CPT_NONE = 0,
     QBO_FMT_VMDK_CPT_VMDKV6,
};

/* vmdk flat extent format, values:
"{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
twoGbMaxExtentFlat | streamOptimized} */
enum QBlockOption_fmt_vmdk_subfmt {
     QBO_FMT_VMDK_SUBFMT_MONOLITHIC_SPARSE = 0,
     QBO_FMT_VMDK_SUBFMT_MONOLITHIC_FLAT,
     QBO_FMT_VMDK_SUBFMT_TWOGBMAX_EXTENT_SPARSE,
     QBO_FMT_VMDK_SUBFMT_TWOGBMAX_EXTENT_FLAT,
     QBO_FMT_VMDK_SUBFMT_STREAM_OPTIMIZED,
};

struct QBlockOption_fmt_vmdk {
     size_t virt_size;
     struct QBlockOptionLoc backing_loc;
     enum QBlockOption_fmt_vmdk_cpt cpt_lv;
     enum QBlockOption_fmt_vmdk_subfmt subfmt;
};

/* "{dynamic (default) | fixed} " */
enum QBlockOption_fmt_vpc_subfmt {
     QBO_FMT_VPC_SUBFMT_DYNAMIC = 0,
     QBO_FMT_VPC_SUBFMT_FIXED,
};

struct QBlockOption_fmt_vpc {
     size_t virt_size;
     enum QBlockOption_fmt_vpc_subfmt subfmt;
};

union QBlockOption_fmt {
     struct QBlockOption_fmt_cow       o_cow;
     struct QBlockOption_fmt_qed       o_qed;
     struct QBlockOption_fmt_qcow      o_qcow;
     struct QBlockOption_fmt_qcow2     o_qcow2;
     struct QBlockOption_fmt_raw       o_raw;
     struct QBlockOption_fmt_rbd       o_rbd;
     struct QBlockOption_fmt_sheepdog  o_sheepdog;
     struct QBlockOption_fmt_vdi       o_vdi;
     struct QBlockOption_fmt_vmdk      o_vmdk;
     struct QBlockOption_fmt_vpc       o_vpc;
};

struct QBlockOptionFormat {
     enum QBlockFormat fmt_type;
     union QBlockOption_fmt fmt_op;
     uint8 reserved[512];
};

/**
  * qb_of_new: create a new QBlockOptionFormat structure.
  *
  * return 0 on success, negative on fail.
  *
  * @op: pointer that will receive created struct.
  */
int qb_of_new(struct QBlockOptionFormat **op);

/**
  * qb_of_free: free QBlockOptionFormat structure.
  *
  * @op: pointer to the struct, *op would be set to NULL.
  */
void qb_of_free(struct QBlockOptionFormat **op);


/**
  * qb_open: open a block object.
  *
  * return 0 on success, negative on fail.
  *
  * @qbs: pointer to struct QBlockState.
  * @loc: location options for open, how to find the image.
  * @fmt: format options, how to extract the data, only valid member now is
      fmt->fmt_type.
  * @flag: behavior control flags.
  */
int qb_open(struct QBlockState *qbs, struct QBlockOptionLoc *loc,
                                  struct QBlockOptionFormat *fmt, int flag);

/**
  * qb_close: close a block object.
  *
  * qb_flush is automaticlly done inside.
  *
  * @qbs: pointer to struct QBlockState.
  */
void qb_close(struct QBlockState *qbs);

/**
  * qb_create: create a block image or object.
  *
  * return negative on fail, 0 on success.
  *
  * @qbs: pointer to struct QBlockState.
  * @loc: location options for open, how to find the image.
  * @fmt: format options, how to extract the data.
  * @flag: behavior control flags.
  */
int qb_create(struct QBlockState *qbs, struct QBlockOptionLoc *loc,
                                    struct QBlockOptionFormat *fmt, int 
flag);


/* sync access */
/**
  * qb_read: block sync read.
  *
  * return negative on fail, 0 on success.
  *
  * @qbs: pointer to struct QBlockState.
  * @buf: buffer that receive the content.
  * @len: length to read.
  * @offset: offset in the block data.
  */
int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
                                                       off_t offset);
/**
  * qb_write: block sync write.
  *
  * return negative on fail, 0 on success.
  *
  * @qbs: pointer to struct QBlockState.
  * @buf: buffer that receive the content.
  * @len: length to write.
  * @offset: offset in the block data.
  */
int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
                                                        off_t offset);
/**
  * qb_flush: block sync flush.
  *
  * return negative on fail, 0 on success.
  *
  * @qbs: pointer to struct QBlockState.
  */
int qb_flush(struct QBlockState *qbs);


/* image information */
/**
  * QBlockInfoImage: information about the block image.
  *
  * @i_loc: location info.
  * @i_fmt: format info.
  * @i_virt_size: virtual size.
  * @i_alloc_size: allocated size, -1 if not available.
  * @i_dirty: dirty status, -1 if not available.
  */
struct QBlockInfoImage {
     struct QBlockOptionLoc i_loc;
     struct QBlockOptionFormat i_fmt;
     size_t i_virt_size;
     /* advance info */
     size_t i_allocated_size;
     int i_dirty;
};

/**
  * qb_get_image_info: get image info.
  *
  * return negative on fail, 0 on success.
  *
  * @qbs: pointer to struct QBlockState.
  * @info: pointer that would receive the information.
  */
int qb_get_image_info(struct QBlockState *qbs, struct QBlockInfoImage 
**info);

/**
  * qb_free_image_info: free image info.
  *
  * @info: pointer to the information struct.
  */
void qb_free_image_info(struct QBlockInfoImage **info);

/* misc */
bool qb_supports_format(enum QBlockFormat fmt);
bool qb_supports_protocol(enum QBlockProtocol proto);

/**
  * qb_error_get_detail: get erro details.
  *
  * return a human readable string.
  *
  * @qbs: pointer to struct QBlockState.
  * @err_ret: negative value last qblock API returned.
  * @err_num: pointer that will receive errno, it is valid only when 
err_ret is
  *     QB_ERR_INTERNAL_ERR.
  */
const char *qb_error_get_detail(struct QBlockState *qbs, int err_ret,
                                                          int *err_num);
#endif
diff mbox

Patch

diff --git a/libqblock.c b/libqblock.c
new file mode 100644
index 0000000..52e46dc
--- /dev/null
+++ b/libqblock.c
@@ -0,0 +1,670 @@ 
+/*
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#include "libqblock.h"
+
+#include <unistd.h>
+#include "block.h"
+#include "block_int.h"
+
+#define FUNC_FREE free
+#define FUNC_MALLOC malloc
+#define FUNC_CALLOC calloc
+
+#define CLEAN_FREE(p) { \
+        FUNC_FREE(p); \
+        (p) = NULL; \
+}
+
+/* details should be hidden to user */
+struct QBlockState {
+    BlockDriverState *bdrvs;
+    char *filename;
+    const char *err_msg;
+    int err_no;
+};
+
+static void set_err_nomem(struct QBlockState *qbs)
+{
+    qbs->err_msg = NULL;
+    qbs->err_no = 0;
+}
+
+void libqblock_init(void)
+{
+    bdrv_init();
+    return;
+}
+
+int qb_state_new(struct QBlockState **qbs)
+{
+    *qbs = FUNC_CALLOC(1, sizeof(struct QBlockState));
+    if (*qbs == NULL) {
+        return QB_ERR_MEM_ERR;
+    }
+    (*qbs)->bdrvs = bdrv_new("hda");
+    if ((*qbs)->bdrvs == NULL) {
+        CLEAN_FREE(*qbs);
+        return QB_ERR_INTERNAL_ERR;
+    }
+    return 0;
+}
+
+void qb_state_free(struct QBlockState **qbs)
+{
+    CLEAN_FREE((*qbs)->filename);
+    if ((*qbs)->bdrvs == NULL) {
+        bdrv_delete((*qbs)->bdrvs);
+        (*qbs)->bdrvs = NULL;
+    }
+    CLEAN_FREE(*qbs);
+    return;
+}
+
+static const char *fmt2str(enum QBlockFormat fmt)
+{
+    const char *ret = NULL;
+    switch (fmt) {
+    case QB_FMT_COW:
+        ret = "cow";
+        break;
+    case QB_FMT_QED:
+        ret = "qed";
+        break;
+    case QB_FMT_QCOW:
+        ret = "qcow";
+        break;
+    case QB_FMT_QCOW2:
+        ret = "qcow2";
+        break;
+    case QB_FMT_RAW:
+        ret = "raw";
+        break;
+    case QB_FMT_RBD:
+        ret = "rbd";
+        break;
+    case QB_FMT_SHEEPDOG:
+        ret = "sheepdog";
+        break;
+    case QB_FMT_VDI:
+        ret = "vdi";
+        break;
+    case QB_FMT_VMDK:
+        ret = "vmdk";
+        break;
+    case QB_FMT_VPC:
+        ret = "vpc";
+        break;
+    default:
+        break;
+    }
+    return ret;
+}
+
+static enum QBlockFormat str2fmt(const char *fmt)
+{
+    enum QBlockFormat ret = QB_FMT_NONE;
+    if (0 == strcmp(fmt, "cow")) {
+        ret = QB_FMT_COW;
+    } else if (0 == strcmp(fmt, "qed")) {
+        ret = QB_FMT_QED;
+    } else if (0 == strcmp(fmt, "qcow")) {
+        ret = QB_FMT_QCOW;
+    } else if (0 == strcmp(fmt, "qcow2")) {
+        ret = QB_FMT_QCOW2;
+    } else if (0 == strcmp(fmt, "raw")) {
+        ret = QB_FMT_RAW;
+    } else if (0 == strcmp(fmt, "rbd")) {
+        ret = QB_FMT_RBD;
+    } else if (0 == strcmp(fmt, "sheepdog")) {
+        ret = QB_FMT_SHEEPDOG;
+    } else if (0 == strcmp(fmt, "vdi")) {
+        ret = QB_FMT_VDI;
+    } else if (0 == strcmp(fmt, "vmdk")) {
+        ret = QB_FMT_VMDK;
+    } else if (0 == strcmp(fmt, "vpc")) {
+        ret = QB_FMT_VPC;
+    }
+    return ret;
+}
+
+/* copy information and take care the member difference in differect version.
+   Assuming all new member are added in the tail, struct size is the first
+   member, this is old to new version, src have its struct_size set. */
+static void qboo_adjust_o2n(struct QBlockOptionOpen *dest,
+                            struct QBlockOptionOpen *src)
+{
+    /* for simple it does memcpy now, need to take care of embbed structure */
+    memcpy(dest, src, src->struct_size);
+}
+
+int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op)
+{
+    int ret = 0, bd_ret;
+    BlockDriverState *bs;
+    struct QBlockOptionOpen qboo;
+    BlockDriver *bd;
+    const char *fmt;
+
+    /* take care of user settings */
+    qboo_adjust_o2n(&qboo, op);
+
+    /* check parameters */
+    if (qboo.o_loc.filename == NULL) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "filename was not set.";
+        qbs->err_no = 0;
+        goto out;
+    }
+
+    if (path_has_protocol(qboo.o_loc.filename) > 0) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "filename have protocol.";
+        qbs->err_no = 0;
+        goto out;
+    }
+
+    if (qboo.o_loc.protocol >= QB_PROTO_MAX) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "protocol was not supported.";
+        qbs->err_no = 0;
+        goto out;
+    }
+
+    bd = NULL;
+    fmt = fmt2str(qboo.o_fmt_type);
+    if (fmt != NULL) {
+        bd = bdrv_find_format(fmt);
+        assert(bd != NULL);
+    }
+
+
+    /* do real openning */
+    bs = qbs->bdrvs;
+    bd_ret = bdrv_open(bs, qboo.o_loc.filename, qboo.o_flag, bd);
+    if (bd_ret < 0) {
+        ret = QB_ERR_INTERNAL_ERR;
+        qbs->err_msg = "failed to open the file.";
+        qbs->err_no = -errno;
+        goto out;
+    }
+
+    if (qbs->filename != NULL) {
+        FUNC_FREE(qbs->filename);
+    }
+    qbs->filename = strdup(qboo.o_loc.filename);
+    if (qbs->filename == NULL) {
+        ret = QB_ERR_INVALID_PARAM;
+        set_err_nomem(qbs);
+        goto out;
+    }
+
+ out:
+    return ret;
+}
+
+void qb_close(struct QBlockState *qbs)
+{
+    BlockDriverState *bs;
+
+    bs = qbs->bdrvs;
+    bdrv_close(bs);
+    return;
+}
+
+/* copy information and take care the member difference in differect version.
+   Assuming all new member are added in the tail, struct size is the first
+   member, this is old to new version, src have its struct_size set. */
+static void qboc_adjust_o2n(struct QBlockOptionCreate *dest,
+                            struct QBlockOptionCreate *src)
+{
+    /* for simple it does memcpy now, need to take care of embbed structure */
+    memcpy(dest, src, src->struct_size);
+}
+
+int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op)
+{
+    int ret = 0, bd_ret;
+    BlockDriverState *bs = NULL;
+    BlockDriver *drv = NULL, *backing_drv = NULL;
+    struct QBlockOptionCreate qbco;
+    const char *fmt = NULL, *fmt_backing_str = NULL, *tmp;
+    QEMUOptionParameter *param = NULL, *create_options = NULL;
+    QEMUOptionParameter *backing_fmt, *backing_file, *size;
+    struct QBlockOption_cow *o_cow = NULL;
+    struct QBlockOption_qed *o_qed = NULL;
+    struct QBlockOption_qcow *o_qcow = NULL;
+    struct QBlockOption_qcow2 *o_qcow2 = NULL;
+    struct QBlockOption_raw *o_raw = NULL;
+    struct QBlockOption_rbd *o_rbd = NULL;
+    struct QBlockOption_sheepdog *o_sd = NULL;
+    struct QBlockOption_vdi *o_vdi = NULL;
+    struct QBlockOption_vmdk *o_vmdk = NULL;
+    struct QBlockOption_vpc *o_vpc = NULL;
+    int backing_flag = 0;
+
+    /* take care of user settings */
+    qboc_adjust_o2n(&qbco, op);
+
+    /* check parameters */
+    if (qbco.o_loc.filename == NULL) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "filename was not set.";
+        qbs->err_no = 0;
+        goto out;
+    }
+
+    if (path_has_protocol(qbco.o_loc.filename) > 0) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "filename have protocol.";
+        qbs->err_no = 0;
+        goto out;
+    }
+
+    if (qbco.o_loc.protocol >= QB_PROTO_MAX) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "protocol was not supported.";
+        qbs->err_no = 0;
+        goto out;
+    }
+
+    /* set parameters */
+    fmt = fmt2str(qbco.o_fmt.fmt_type);
+    if (fmt == NULL) {
+        ret = QB_ERR_INVALID_PARAM;
+        qbs->err_msg = "format is not valid.";
+        qbs->err_no = 0;
+        goto out;
+    }
+    drv = bdrv_find_format(fmt);
+    assert(drv != NULL);
+
+    create_options = append_option_parameters(create_options,
+                                              drv->create_options);
+    param = parse_option_parameters("", create_options, param);
+
+    switch (qbco.o_fmt.fmt_type) {
+    case QB_FMT_COW:
+        o_cow = &qbco.o_fmt.fmt_op.o_cow;
+        assert(0 == set_option_parameter_int(param,
+                                BLOCK_OPT_SIZE, o_cow->virt_size));
+        if (o_cow->backing_file) {
+            assert(0 == set_option_parameter(param,
+                                BLOCK_OPT_BACKING_FILE, o_cow->backing_file));
+        }
+        backing_flag = o_cow->backing_flag;
+        break;
+    case QB_FMT_QED:
+        o_qed = &qbco.o_fmt.fmt_op.o_qed;
+        assert(0 == set_option_parameter_int(param,
+                                BLOCK_OPT_SIZE, o_qed->virt_size));
+        if (o_qed->backing_file) {
+            assert(0 == set_option_parameter(param,
+                                BLOCK_OPT_BACKING_FILE, o_qed->backing_file));
+        }
+        fmt_backing_str = fmt2str(o_qed->backing_fmt);
+        if (fmt_backing_str) {
+            assert(0 == set_option_parameter(param,
+                                BLOCK_OPT_BACKING_FMT, fmt_backing_str));
+        }
+        assert(0 == set_option_parameter_int(param,
+                                BLOCK_OPT_CLUSTER_SIZE, o_qed->cluster_size));
+        assert(0 == set_option_parameter_int(param,
+                                BLOCK_OPT_TABLE_SIZE, o_qed->table_size));
+        backing_flag = o_qed->backing_flag;
+        break;
+    case QB_FMT_QCOW:
+        o_qcow = &qbco.o_fmt.fmt_op.o_qcow;
+        assert(0 == set_option_parameter_int(param,
+                                BLOCK_OPT_SIZE, o_qcow->virt_size));
+        if (o_qcow->backing_file) {
+            assert(0 == set_option_parameter(param,
+                                BLOCK_OPT_BACKING_FILE, o_qcow->backing_file));
+        }
+        tmp = o_qcow->encrypt ? "on" : "off";
+        assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp));
+        backing_flag = o_qcow->backing_flag;
+        break;
+    case QB_FMT_QCOW2:
+        o_qcow2 = &qbco.o_fmt.fmt_op.o_qcow2;
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_SIZE, o_qcow2->virt_size));
+        if (o_qcow2->backing_file) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_BACKING_FILE, o_qcow2->backing_file));
+        }
+        fmt_backing_str = fmt2str(o_qcow2->backing_fmt);
+        if (fmt_backing_str) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_BACKING_FMT, fmt_backing_str));
+        }
+        if (o_qcow2->compat_level) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_COMPAT_LEVEL, o_qcow2->compat_level));
+        }
+        tmp = o_qcow2->encrypt ? "on" : "off";
+        assert(0 == set_option_parameter(param, BLOCK_OPT_ENCRYPT, tmp));
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_CLUSTER_SIZE, o_qcow2->cluster_size));
+        if (o_qcow2->prealloc_mode) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_PREALLOC, o_qcow2->prealloc_mode));
+        }
+        backing_flag = o_qcow2->backing_flag;
+        break;
+    case QB_FMT_RAW:
+        o_raw = &qbco.o_fmt.fmt_op.o_raw;
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_SIZE, o_raw->virt_size));
+        break;
+    case QB_FMT_RBD:
+        o_rbd = &qbco.o_fmt.fmt_op.o_rbd;
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_SIZE, o_rbd->virt_size));
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_CLUSTER_SIZE, o_rbd->cluster_size));
+        break;
+    case QB_FMT_SHEEPDOG:
+        o_sd = &qbco.o_fmt.fmt_op.o_sheepdog;
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_SIZE, o_sd->virt_size));
+        if (o_sd->backing_file) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_BACKING_FILE, o_sd->backing_file));
+        }
+        if (o_sd->prealloc_mode) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_PREALLOC, o_sd->prealloc_mode));
+        }
+        backing_flag = o_sd->backing_flag;
+        break;
+    case QB_FMT_VDI:
+        o_vdi = &qbco.o_fmt.fmt_op.o_vdi;
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_SIZE, o_vdi->virt_size));
+        /* following option is not always valid depends on configuration */
+        set_option_parameter_int(param,
+                              BLOCK_OPT_CLUSTER_SIZE, o_vdi->cluster_size);
+        set_option_parameter_int(param, "static", o_vdi->prealloc_mode);
+        break;
+    case QB_FMT_VMDK:
+        o_vmdk = &qbco.o_fmt.fmt_op.o_vmdk;
+        assert(0 == set_option_parameter_int(param,
+                              BLOCK_OPT_SIZE, o_vmdk->virt_size));
+        if (o_vmdk->backing_file) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_BACKING_FILE, o_vmdk->backing_file));
+        }
+        tmp = o_vmdk->compat_version6 ? "on" : "off";
+        assert(0 == set_option_parameter(param, BLOCK_OPT_COMPAT6, tmp));
+        if (o_vmdk->subfmt) {
+            assert(0 == set_option_parameter(param,
+                              BLOCK_OPT_SUBFMT, o_vmdk->subfmt));
+        }
+        backing_flag = o_vmdk->backing_flag;
+        break;
+    case QB_FMT_VPC:
+        o_vpc = &qbco.o_fmt.fmt_op.o_vpc;
+        assert(0 == set_option_parameter_int(param,
+                               BLOCK_OPT_SIZE, o_vpc->virt_size));
+        if (o_vpc->subfmt) {
+            assert(0 == set_option_parameter(param,
+                               BLOCK_OPT_SUBFMT, o_vpc->subfmt));
+        }
+        break;
+    default:
+        break;
+    }
+
+    backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
+    if (backing_file && backing_file->value.s) {
+        if (!strcmp(op->o_loc.filename, backing_file->value.s)) {
+            ret = QB_ERR_INVALID_PARAM;
+            qbs->err_msg = "backing file is the same with new file.";
+            qbs->err_no = 0;
+            goto out;
+        }
+    }
+
+    backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
+    if (backing_fmt && backing_fmt->value.s) {
+        backing_drv = bdrv_find_format(backing_fmt->value.s);
+        assert(backing_drv != NULL);
+    }
+
+    size = get_option_parameter(param, BLOCK_OPT_SIZE);
+    if (size && size->value.n <= 0) {
+        if (backing_file && backing_file->value.s) {
+            uint64_t size;
+            char buf[32];
+            int back_flags;
+
+            /* backing files always opened read-only */
+            back_flags =
+                backing_flag &
+                ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
+
+            bs = bdrv_new("");
+
+            ret = bdrv_open(bs, backing_file->value.s,
+                                back_flags, backing_drv);
+            if (ret < 0) {
+                ret = QB_ERR_INTERNAL_ERR;
+                qbs->err_msg = "failed to open the backing file.";
+                qbs->err_no = -errno;
+                goto out;
+            }
+            bdrv_get_geometry(bs, &size);
+            size *= 512;
+
+            snprintf(buf, sizeof(buf), "%" PRId64, size);
+            set_option_parameter(param, BLOCK_OPT_SIZE, buf);
+        } else {
+            ret = QB_ERR_INVALID_PARAM;
+            qbs->err_msg = "neither size or backing file was not set.";
+            qbs->err_no = 0;
+            goto out;
+        }
+    }
+
+    bd_ret = bdrv_create(drv, op->o_loc.filename, param);
+
+    if (bd_ret < 0) {
+        ret = QB_ERR_INTERNAL_ERR;
+        qbs->err_no = bd_ret;
+        if (bd_ret == -ENOTSUP) {
+            qbs->err_msg = "formatting option not supported.";
+        } else if (bd_ret == -EFBIG) {
+            qbs->err_msg = "The image size is too large.";
+        } else {
+            qbs->err_msg = "Error in creating the image.";
+        }
+        goto out;
+    }
+
+out:
+    free_option_parameters(create_options);
+    free_option_parameters(param);
+
+    if (bs) {
+        bdrv_delete(bs);
+    }
+
+    return ret;
+}
+
+/* ignore case that len is not alligned to 512 now. */
+int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
+                                                      off_t offset)
+{
+    int ret = 0, bd_ret;
+    BlockDriverState *bs;
+
+    bs = qbs->bdrvs;
+
+    assert((len & 0x01ff) == 0);
+    assert((offset & 0x01ff) == 0);
+    bd_ret = bdrv_read(bs, offset >> 9, (uint8_t *)buf, len >> 9);
+    if (bd_ret < 0) {
+        ret = QB_ERR_INTERNAL_ERR;
+        qbs->err_no = bd_ret;
+        if (bd_ret == -EIO) {
+            qbs->err_msg = "Generic I/O error.";
+        } else if (bd_ret == -ENOMEDIUM) {
+            qbs->err_msg = "No meida inserted.";
+        } else if (bd_ret == -EINVAL) {
+            qbs->err_msg = "Sector was not correct.";
+        } else {
+            qbs->err_msg = "Internal error.";
+        }
+    }
+    return ret;
+}
+
+/* ignore case that len is not alligned to 512 now. */
+int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
+                                                       off_t offset)
+{
+    int ret = 0, bd_ret;
+    BlockDriverState *bs;
+
+    bs = qbs->bdrvs;
+
+    assert((len & 0x01ff) == 0);
+    assert((offset & 0x01ff) == 0);
+    bd_ret = bdrv_write(bs, offset >> 9, buf, len >> 9);
+    if (bd_ret < 0) {
+        ret = QB_ERR_INTERNAL_ERR;
+        qbs->err_no = bd_ret;
+        if (bd_ret == -EIO) {
+            qbs->err_msg = "Generic I/O error.";
+        } else if (bd_ret == -ENOMEDIUM) {
+            qbs->err_msg = "No meida inserted.";
+        } else if (bd_ret == -EINVAL) {
+            qbs->err_msg = "Sector was not correct.";
+        } else if (bd_ret == -EACCES) {
+            qbs->err_msg = "Ready only device.";
+        } else {
+            qbs->err_msg = "Internal error.";
+        }
+    }
+    return ret;
+}
+
+int qb_flush(struct QBlockState *qbs)
+{
+    int ret = 0, bd_ret;
+    BlockDriverState *bs;
+
+    bs = qbs->bdrvs;
+    bd_ret = bdrv_flush(bs);
+    if (bd_ret < 0) {
+        ret = QB_ERR_INTERNAL_ERR;
+        qbs->err_no = bd_ret;
+        qbs->err_msg = NULL;
+    }
+    return ret;
+}
+
+int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info)
+{
+    int ret = 0;
+    BlockDriverState *bs;
+    const char *tmp;
+    uint64_t total_sectors;
+    char backing_filename[1024];
+
+    struct QBlockInfoImage *ifimg = FUNC_CALLOC(1,
+                                           sizeof(struct QBlockInfoImage));
+    if (ifimg == NULL) {
+        ret = QB_ERR_MEM_ERR;
+        set_err_nomem(qbs);
+        goto out;
+    }
+
+    bs = qbs->bdrvs;
+    ifimg->filename = strdup(qbs->filename);
+    if (ifimg->filename == NULL) {
+        ret = QB_ERR_MEM_ERR;
+        set_err_nomem(qbs);
+        goto err;
+    }
+    tmp = bdrv_get_format_name(bs);
+    ifimg->format = str2fmt(tmp);
+    /* support only file now */
+    ifimg->protocol = QB_PROTO_FILE;
+
+    bdrv_get_geometry(bs, &total_sectors);
+    ifimg->virt_size = total_sectors * 512;
+    ifimg->allocated_size = bdrv_get_allocated_file_size(bs);
+    ifimg->encrypt = bdrv_is_encrypted(bs);
+    bdrv_get_full_backing_filename(bs, backing_filename,
+                                   sizeof(backing_filename));
+    if (backing_filename[0] != '\0') {
+        ifimg->backing_filename = strdup(backing_filename);
+        if (ifimg->backing_filename == NULL) {
+            ret = QB_ERR_MEM_ERR;
+            set_err_nomem(qbs);
+            goto err;
+        }
+    }
+
+    *info = ifimg;
+ out:
+    return ret;
+ err:
+    qb_infoimage_free(&ifimg);
+    return ret;
+}
+
+void qb_infoimage_free(struct QBlockInfoImage **info)
+{
+    if (*info == NULL) {
+        return;
+    }
+    FUNC_FREE((*info)->filename);
+    FUNC_FREE((*info)->backing_filename);
+    FUNC_FREE(*info);
+    *info = NULL;
+    return;
+}
+
+bool qb_supports_format(enum QBlockFormat fmt)
+{
+    if (fmt < QB_FMT_MAX) {
+        return true;
+    }
+    return false;
+}
+
+bool qb_supports_protocol(enum QBlockProtocol proto)
+{
+    if (proto < QB_PROTO_MAX) {
+        return true;
+    }
+    return false;
+}
+
+const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num)
+{
+    *err_num = qbs->err_no;
+    return qbs->err_msg;
+}
diff --git a/libqblock.h b/libqblock.h
new file mode 100644
index 0000000..d2e9502
--- /dev/null
+++ b/libqblock.h
@@ -0,0 +1,447 @@ 
+/*
+ * Copyright IBM Corp. 2012
+ *
+ * Authors:
+ *  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+#ifndef LIBQBLOCK_H
+#define LIBQBLOCK_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define bool _Bool
+
+#define QB_ERR_MEM_ERR (-1)
+#define QB_ERR_INTERNAL_ERR (-2)
+#define QB_ERR_INVALID_PARAM (-3)
+
+/* this library is designed around this core struct. */
+struct QBlockState;
+
+/*
+   libarary init
+   This function get the library ready to use.
+ */
+void libqblock_init(void);
+
+/*
+   create a new qbs object
+   params:
+     qbs: out, pointer that will receive created obj.
+   return:
+     0 on succeed, negative on failure.
+ */
+int qb_state_new(struct QBlockState **qbs);
+
+/*
+   delete a qbs object
+   params:
+     qbs: in, pointer that will be freed. *qbs will be set to NULL.
+   return:
+     void.
+ */
+void qb_state_free(struct QBlockState **qbs);
+
+
+/* flag used in open and create */
+#define LIBQBLOCK_O_RDWR        0x0002
+/* open the file read only and save writes in a snapshot */
+#define LIBQBLOCK_O_SNAPSHOT    0x0008
+/* do not use the host page cache */
+#define LIBQBLOCK_O_NOCACHE     0x0020
+/* use write-back caching */
+#define LIBQBLOCK_O_CACHE_WB    0x0040
+/* use native AIO instead of the thread pool */
+#define LIBQBLOCK_O_NATIVE_AIO  0x0080
+/* don't open the backing file */
+#define LIBQBLOCK_O_NO_BACKING  0x0100
+/* disable flushing on this disk */
+#define LIBQBLOCK_O_NO_FLUSH    0x0200
+/* copy read backing sectors into image */
+#define LIBQBLOCK_O_COPY_ON_READ 0x0400
+/* consistency hint for incoming migration */
+#define LIBQBLOCK_O_INCOMING    0x0800
+
+#define LIBQBLOCK_O_CACHE_MASK \
+   (LIBQBLOCK_O_NOCACHE | LIBQBLOCK_O_CACHE_WB | LIBQBLOCK_O_NO_FLUSH)
+
+enum QBlockProtocol {
+    QB_PROTO_FILE = 0,
+    QB_PROTO_MAX
+};
+
+enum QBlockFormat {
+    QB_FMT_NONE = 0,
+    QB_FMT_COW,
+    QB_FMT_QED,
+    QB_FMT_QCOW,
+    QB_FMT_QCOW2,
+    QB_FMT_RAW,
+    QB_FMT_RBD,
+    QB_FMT_SHEEPDOG,
+    QB_FMT_VDI,
+    QB_FMT_VMDK,
+    QB_FMT_VPC,
+    QB_FMT_MAX
+};
+
+/* block target location info, it include all information about how to find
+   the image */
+struct QBlockLocInfo {
+    int struct_size;
+    const char *filename;
+    enum QBlockProtocol protocol;
+};
+
+/* how to open the image */
+struct QBlockOptionOpen {
+    int struct_size;
+    struct QBlockLocInfo o_loc; /* how to find */
+    enum QBlockFormat o_fmt_type; /* how to extract */
+    int o_flag; /* how to control */
+};
+
+/*
+   create a new QBlockOptionOpen structure.
+   params:
+     op: out, pointer that will receive created structure.
+   return:
+     0 on succeed, negative on failure.
+ */
+static inline int qb_oo_new(struct QBlockOptionOpen **op)
+{
+    *op = calloc(1, sizeof(struct QBlockOptionOpen));
+    if (*op == NULL) {
+        return QB_ERR_MEM_ERR;
+    }
+    (*op)->struct_size = sizeof(struct QBlockOptionOpen);
+    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
+    return 0;
+}
+
+/*
+   free QBlockOptionOpen structure.
+   params:
+     op: in, *op will be set as NULL after called.
+   return:
+     void
+ */
+static inline void qb_oo_free(struct QBlockOptionOpen **op)
+{
+    free(*op);
+    *op = NULL;
+}
+
+/*
+   open a block object.
+   params:
+     qbs: in, pointer to QBlockState.
+     op: in, options for open.
+   return:
+     0 on success, negative on fail.
+ */
+int qb_open(struct QBlockState *qbs, struct QBlockOptionOpen *op);
+
+/*
+   close a block object.
+   params:
+     qbs: in, pointer to QBlockState.
+   return:
+     void.
+ */
+void qb_close(struct QBlockState *qbs);
+
+/* format related options, struct_size must be set. */
+struct QBlockOption_cow {
+    int struct_size;
+    size_t virt_size;
+    const char *backing_file;
+    int backing_flag;
+};
+
+struct QBlockOption_qed {
+    int struct_size;
+    size_t virt_size;
+    const char *backing_file;
+    enum QBlockFormat backing_fmt;
+    int backing_flag;
+    size_t cluster_size; /* unit is bytes */
+    size_t table_size; /* unit is clusters */
+};
+
+struct QBlockOption_qcow {
+    int struct_size;
+    size_t virt_size;
+    const char *backing_file;
+    int backing_flag;
+    bool encrypt;
+};
+
+struct QBlockOption_qcow2 {
+    int struct_size;
+    size_t virt_size;
+    const char *compat_level; /* "Compatibility level (0.10 or 1.1)" */
+    const char *backing_file;
+    enum QBlockFormat backing_fmt;
+    int backing_flag;
+    bool encrypt;
+    size_t cluster_size; /* unit is bytes */
+    const char *prealloc_mode; /* off or metadata */
+};
+
+struct QBlockOption_raw {
+    int struct_size;
+    size_t virt_size;
+};
+
+struct QBlockOption_rbd {
+    int struct_size;
+    size_t virt_size;
+    size_t cluster_size;
+};
+
+struct QBlockOption_sheepdog {
+    int struct_size;
+    size_t virt_size;
+    const char *backing_file;
+    int backing_flag;
+    const char *prealloc_mode; /* off or full */
+};
+
+struct QBlockOption_vdi {
+    int struct_size;
+    size_t virt_size;
+    size_t cluster_size;
+    bool prealloc_mode;
+};
+
+struct QBlockOption_vmdk {
+    int struct_size;
+    size_t virt_size;
+    const char *backing_file;
+    int backing_flag;
+    bool compat_version6;
+    const char *subfmt;
+    /* vmdk flat extent format, values:
+   "{monolithicSparse (default) | monolithicFlat | twoGbMaxExtentSparse |
+    twoGbMaxExtentFlat | streamOptimized} */
+};
+
+struct QBlockOption_vpc {
+    int struct_size;
+    size_t virt_size;
+    const char *subfmt; /* "{dynamic (default) | fixed} " */
+};
+
+union QBlockOption_fmt {
+    struct QBlockOption_cow       o_cow;
+    struct QBlockOption_qed       o_qed;
+    struct QBlockOption_qcow      o_qcow;
+    struct QBlockOption_qcow2     o_qcow2;
+    struct QBlockOption_raw       o_raw;
+    struct QBlockOption_rbd       o_rbd;
+    struct QBlockOption_sheepdog  o_sheepdog;
+    struct QBlockOption_vdi       o_vdi;
+    struct QBlockOption_vmdk      o_vmdk;
+    struct QBlockOption_vpc       o_vpc;
+};
+
+struct QBlockOptionFormat {
+    int struct_size;
+    enum QBlockFormat fmt_type;
+    union QBlockOption_fmt fmt_op;
+};
+
+/* struct_size in o_loc and o_fmt must set. To make this structure extensible,
+   all new member must be added in the tail of each structure. */
+struct QBlockOptionCreate {
+    int struct_size;
+    struct QBlockLocInfo o_loc;
+    struct QBlockOptionFormat o_fmt;
+};
+
+/*
+   create a new QBlockOptionCreate structure.
+   params:
+     op: out, pointer that will receive created structure.
+     fmt: format want to use.
+   return:
+     0 on succeed, negative on failure.
+ */
+static inline int qb_oc_new(struct QBlockOptionCreate **op,
+                            enum QBlockFormat fmt)
+{
+    *op = calloc(1, sizeof(struct QBlockOptionCreate));
+    if (*op == NULL) {
+        return QB_ERR_MEM_ERR;
+    }
+    (*op)->struct_size = sizeof(struct QBlockOptionCreate);
+    (*op)->o_loc.struct_size = sizeof(struct QBlockLocInfo);
+    (*op)->o_fmt.struct_size = sizeof(struct QBlockOptionFormat);
+
+    switch (fmt) {
+    case QB_FMT_COW:
+        (*op)->o_fmt.fmt_op.o_cow.struct_size =
+                                          sizeof(struct QBlockOption_cow);
+        break;
+    case QB_FMT_QED:
+        (*op)->o_fmt.fmt_op.o_qed.struct_size =
+                                          sizeof(struct QBlockOption_qed);
+        break;
+    case QB_FMT_QCOW:
+        (*op)->o_fmt.fmt_op.o_qcow.struct_size =
+                                          sizeof(struct QBlockOption_qcow);
+        break;
+    case QB_FMT_QCOW2:
+        (*op)->o_fmt.fmt_op.o_qcow2.struct_size =
+                                          sizeof(struct QBlockOption_qcow2);
+        break;
+    case QB_FMT_RAW:
+        (*op)->o_fmt.fmt_op.o_raw.struct_size =
+                                          sizeof(struct QBlockOption_raw);
+        break;
+    case QB_FMT_RBD:
+        (*op)->o_fmt.fmt_op.o_rbd.struct_size =
+                                          sizeof(struct QBlockOption_rbd);
+        break;
+    case QB_FMT_SHEEPDOG:
+        (*op)->o_fmt.fmt_op.o_sheepdog.struct_size =
+                                          sizeof(struct QBlockOption_sheepdog);
+        break;
+    case QB_FMT_VDI:
+        (*op)->o_fmt.fmt_op.o_vdi.struct_size =
+                                          sizeof(struct QBlockOption_vdi);
+        break;
+    case QB_FMT_VMDK:
+        (*op)->o_fmt.fmt_op.o_vmdk.struct_size =
+                                          sizeof(struct QBlockOption_vmdk);
+        break;
+    case QB_FMT_VPC:
+        (*op)->o_fmt.fmt_op.o_vpc.struct_size =
+                                          sizeof(struct QBlockOption_vpc);
+        break;
+    default:
+        break;
+    }
+    (*op)->o_fmt.fmt_type = fmt;
+    return 0;
+}
+
+/*
+   free QBlockOptionCreate structure.
+   params:
+     op: in, *op will be set as NULL after called.
+   return:
+     void
+ */
+static inline void qb_oc_free(struct QBlockOptionCreate **op)
+{
+    free(*op);
+    *op = NULL;
+}
+/*
+   create a block file.
+   params:
+     qbs: in, pointer to QBlockState.
+     op: in, create option.
+   return:
+     negative on fail, 0 on success.
+ */
+int qb_create(struct QBlockState *qbs, struct QBlockOptionCreate *op);
+
+
+/* sync access */
+/*
+   block sync read.
+   params:
+     qbs: in, pointer to QBlockState.
+     buf: out, buffer that receive the content.
+     len: in, length to read.
+     offset: in, offset in the block data.
+   return:
+     negative on fail, 0 on success.
+ */
+int qb_read(struct QBlockState *qbs, const void *buf, size_t len,
+                                                      off_t offset);
+
+/*
+   block sync read.
+   params:
+     qbs: in, pointer to QBlockState.
+     buf: in, buffer that would be wrote.
+     len: in, length to write.
+     offset: in, offset in the block data.
+   return:
+     negative on fail, 0 on success.
+ */
+int qb_write(struct QBlockState *qbs, const void *buf, size_t len,
+                                                       off_t offset);
+
+/*
+   block sync flush.
+   params:
+     qbs: in, pointer to QBlockState.
+   return:
+     negative on fail, 0 on success.
+ */
+int qb_flush(struct QBlockState *qbs);
+
+/* information */
+/* image related info, static information, from user perspective. */
+/* now it is a plain structure, wonder if it could be foldered into embbed one
+   to reflect that format related information better. */
+struct QBlockInfoImage {
+    int struct_size;
+    char *filename;
+    enum QBlockProtocol protocol;
+    enum QBlockFormat format;
+    size_t virt_size;
+    /* advance info */
+    size_t allocated_size;
+    bool encrypt;
+    char *backing_filename;
+};
+
+/* image info */
+/*
+   get image info.
+   params:
+     qbs: in, pointer to QBlockState.
+     info, out, pointer that would receive the information.
+   return:
+     negative on fail, 0 on success.
+ */
+int qb_infoimage_get(struct QBlockState *qbs, struct QBlockInfoImage **info);
+
+/*
+   free image info.
+   params:
+     info, in, pointer, *info would be set to NULL after function called.
+   return:
+     void.
+ */
+void qb_infoimage_free(struct QBlockInfoImage **info);
+
+/* misc */
+bool qb_supports_format(enum QBlockFormat fmt);
+bool qb_supports_protocol(enum QBlockProtocol proto);
+
+const char *qb_error_get_detail(struct QBlockState *qbs, int *err_num);
+#endif