[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [qemu-devel] [PATCH V2 2/3] [RFC] libqblock-API design
From: |
Wenchao Xia |
Subject: |
[Qemu-devel] [qemu-devel] [PATCH V2 2/3] [RFC] libqblock-API design |
Date: |
Thu, 9 Aug 2012 18:12:17 +0800 |
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
+ */
+
+#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 <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
+ */
+
+#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
--
1.7.1
- [Qemu-devel] [qemu-devel] [PATCH V2 2/3] [RFC] libqblock-API design,
Wenchao Xia <=