qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [qemu-devel] [PATCH V2 2/3] [RFC] libqblock-API design


From: Wenchao Xia
Subject: Re: [Qemu-devel] [qemu-devel] [PATCH V2 2/3] [RFC] libqblock-API design
Date: Fri, 10 Aug 2012 16:35:40 +0800
User-agent: Mozilla/5.0 (Windows NT 5.1; rv:14.0) Gecko/20120713 Thunderbird/14.0

于 2012-8-10 1:36, Blue Swirl 写道:
On Thu, Aug 9, 2012 at 10:12 AM, Wenchao Xia <address@hidden> wrote:
   This patch is the API design.

Signed-off-by: Wenchao Xia <address@hidden>
---
  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 <address@hidden>
+ *
+ * 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 <address@hidden>
+ *
+ * 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






--
Best Regards

Wenchao Xia




reply via email to

[Prev in Thread] Current Thread [Next in Thread]